--- 面試須知與應答技巧
--- 資料結構 / 變數儲存與記憶體
--- 程式語言 / 演算法
--- 作業系統 / 多執行緒(多程序)的觀念與實作控制
--- 轉自ptt: [重要] 發文前務必閱讀:C/C++常見問題十三誡
--- 擬真試題1/擬真試題2/擬真試題3/擬真試題4
--- 直接列出考古題1(精華完整53題含解答)
--- 直接列出考古題2(精華完整35題含解答)
--- 深度討論考古題1(DEMO完整10題含解答) 選擇使用C,C++,C#或JAVA
--- 深度討論考古題2(DEMO完整11題含解答) 選擇使用C,C++,C#或JAVA
--- 撲克牌(大老二)洗牌與牌型判斷(JAVA)
--- Python基本教學
--- Leetcode實戰討論
轉自ptt: [重要] 發文前務必閱讀:C/C++常見問題十三誡
1. 不可以提取不知指向何方的指標(指標變數必需先指向某個可以合法操作的空間,才能進行操作):
2. 不能存取超過陣列既定範圍的空間
3. 不可以提取不知指向何方的指標
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
錯誤例子:char *pc1; /* 未給予初值,不知指向何方 */ char *pc2 = 0; /* pc2 起始化為 null pointer */ *pc1 = 'a'; /* 將 'a' 寫到不知何方,錯誤 */ *pc2 = 'b'; /* 將 'b' 寫到「位址0」,錯誤 */ 正確例子:char c; char *pc1 = &c; *pc1 = 'a'; /* c 的內容變為 'a' */ 錯誤例子:char *name; /* name 尚未指向有效的空間 */ printf("Your name, please: "); gets(name); /* 您確定要寫入的那塊空間合法嗎??? */ printf("Hello, %s\n", name); 正確例子(如果編譯期就能決定字串的最大空間用char[]): char name[21]; /* 讀入字串最長 20 個字元,保留一格空間放 '\0' */ printf("Your name, please: "); gets(name); printf("Hello, %s\n", name); (若是在執行時期才能決定字串的最大空間,則需利用動態分配空間) size_t length; char *name; printf("請輸入字串的最大長度(含null字元): "); scanf("%u", &length); name = (char *)malloc(length); printf("Your name, please: "); scanf("%s", name); printf("Hello, %s\n", name); free(name); |
4. 不要試圖用 char* 去更改一個”字串常數”:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
char* pc = "john"; /* pc 現在指著一個字串常數 */ *pc = 'J'; /* 但是 pc 沒有權利去更改這個常數! */ 說明:字串常數的內容是"唯讀"的,有使用權但沒有更改的權利,若希望使 用可以更改的字串,需將其放在合法空間。 錯誤例子(strcat()不會另行配置空間,只將資料附加到 s1 所指唯讀字串的後面,會寫入到程式無權碰觸的記憶體空間): char *s1 = "Hello, "; char *s2 = "world!"; strcat(s1, s2); 正確例子: char s1[20] = "Hello, "; char *s2 = "world!"; strcat(s1, s2); |
5. 不能在函式中回傳一個指向區域性自動變數的指標
(區域變數在離開該區域時被消滅,因此呼叫端得到的指標所指的字串內容失效了,但如果指標內容是用動態的方式配置,這塊空間是在heap而非stack上,heap空間不會自動回收,因此這塊空間在離開函式後依然有效,但要記得free掉記憶體):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
錯誤例子:char *getstr(char *name){ char buf[30] = "hello"; strcat(buf, name); return buf; }。 正確例子:void getstr(char buf[], int buflen, char *name){ char s[] = "hello"; strcpy(buf, s); strcat(buf, name); } 正確例子:int* foo(){ int* pInteger = (int*) malloc( 10*sizeof(int) ); return pInteger; } |
6. 不可以只做 malloc(), 而不做相應的 free()
1 2 |
但若不是用 malloc() 所得到的記憶體,則不可以 free()。已經 free()了所指記憶體的指標,在它指向另一塊有效的動態分配得來的空間之前,不可以再被 free(),也不可以提取(dereference)這個指標。 [C++] 你不可以只做 new, 而不做相應的 delete. |
7. 在數值運算、賦值或比較中不可以隨意混用不同型別的數值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
錯誤例子(1): unsigned int sum = 2000000000 + 2000000000; /* 20 億 */ double f = 10 / 3; 正確例子(1): /* 全部都用 unsigned int, 注意數字後面的 u, 大寫 U 也成 */ unsigned int sum = 2000000000u + 2000000000u; /* 或是用顯式的轉型 */ unsigned int sum = (unsigned int)2000000000 + 2000000000; double f = 10.0 / 3.0; 說明:在目前最普遍的32位元PC作業平台上,整數常數2000000000的型別為 signed int(簡寫為 int),相加後,其結果仍為 int, 但是 signed int 放不下 4000000000, 造成算術溢位(arithmetic overflow),很可能無法將正確的值指派給 unsigned int sum,縱使 unsigned int 放得下4000000000的數值。 注意:寫成unsigned int sum = (unsigned int)(2000000000 + 2000000000);也是不對的。 例子(2):(感謝 sekya 網友提供) unsigned char a = 0x80; char b = 0x80; /* implementation-defined result */ if( a == 0x80 ) { /* 恒真 */ printf( “a ok\n" ); if( b == 0x80 ) { /* 不一定恒真 */ printf( “b ok\n" ); } 說明:在將 char 型別定義為範圍從 -128 至 +127 的系統上,int 0x80(其值等於 +128)要轉成 char 會放不下,會產生編譯器自行定義的值。這樣的程式就不具可移植性了。 |
8. 在一個運算式中,不能對一個基本型態的變數修改其值超過一次以上
(C/C++並沒有強制規定參數會由哪個方向開始處理(不像Java是由左到右),因此可能會造成與預期不符的情況):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
錯誤例子:int i = 7; int j = ++i + i++; 正確例子:int i = 7; int j = ++i; j += i++; 錯誤例子:x = x++; 錯誤例子:int arr[5]; int i = 0; arr[i] = i++; 正確例子:int arr[5]; int i = 0; arr[i] = i; i++; 錯誤例子:int Integer=10; printf("%d %d %d", Integer++, Integer++, Integer++); 錯誤例子:void foo(int a, int b) { ... } int main() { int i=0; foo(i++, i++); } |
9. 在 Macro 定義中, 務必為它的參數個別加上括號():
1 2 3 4 5 6 7 8 |
錯誤例子:#define SQUARE(x) (x * x) 正確例子:#define SQUARE(x) ((x) * (x)) 如果是用 C++,可利用inline function來取代上述的 macro,以免除 macro 定義的種種危險性。 如:inline int square(int x) { return x * x; } macro 定義出的「偽函式」至少缺乏下列幾項函式本有的能力: (1) 無法進行參數型別的檢查。 (2) 無法遞迴呼叫。 (3) 無法用 &加在 macro name 之前,取得函式位址。 |
10. 不可以在 stack 設置過大的變數
(編譯器會自行決定 stack 的上限,可能是數KB 或數十 KB,當變數所需的空間過大時,很容易造成 stack overflow,程式亦隨之當掉,若真正需要如此大的空間,那麼建議配置在 heap 上,或是採用static / globla variabl):
1 2 |
錯誤例子:int array[10000000]; 正確例子:int *array = (int*) malloc(10000000*sizeof(int)); |
11. 使用浮點數精確度造成的誤差問題
根據 IEEE 754 的規範,又電腦中是用有限的二進位儲存數字,因此常有可能因為精確度而造成誤差,例如加減乘除,等號大小判斷,分配律等數學上常用到的操作,很有可能因此而出錯(不成立)
12. 不要猜想二維陣列可以用 pointer to pointer 來傳遞:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
//首先必須有個觀念,C 語言中陣列是無法直接拿來傳遞的! //不過這時候會有人跳出來反駁: void pass1DArray( int array[] ); int a[10]; pass1DArray( a ); /* 可以合法編譯,而且執行結果正確!! */ //事實上,編譯器會這麼看待 void pass1DArray( int *array ); int a[10]; pass1DArray( &a[0] ); //我們可以順便看出來,array 變數本身可以 decay 成記憶體起頭的位置 //因此我們可以 int *p = a; 這種方式,拿指標去接陣列。 //二維陣列不能直接改成 int ** //錯誤例子: void pass2DArray( int **array ); int a[5][10]; pass2DArray( a ); /* 這時候編譯器會報錯 */ //在一維陣列中,宣告了一個 a[10],那我可以把 a 當成指標來操作 *a 到 *(a+9),但是多維陣列無法如此使用。 void pass2DArray(int (*array) [10]); // array 是個指標,指向 int [10] int a[5][10]; pass2DArray( a ); |
13. 函式內 new 出來的空間記得要讓主程式的指標接住:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
//對指標不熟悉的使用者會以為以下的程式碼是符合預期的 //錯誤例子: void newArray(int* local, int size) { local = (int*) malloc( size * sizeof(int) ); } int main() { int* ptr; newArray(ptr, 10); } //接著就會找了很久的 bug,最後仍然搞不懂為什麼 ptr 沒有指向剛剛拿到的合法空間 //讓我們再回顧一次,並且用圖表示 //原因如下: // 1. int* ptr; ptr -> |__未知的空間__| // 2. 呼叫函式 newArray ptr -> |__未知的空間__| <- local // 3. malloc 取得合法空間 ptr -> |__未知的空間__| // |___合法空間___| <- local // 4. 離開函式 ptr -> |__未知的空間__| //用圖看應該一切就都明白了,我也不需冗言解釋 //也許有人會想問,指標不是傳址嗎? //精確來講,指標也是傳值,只不過該值是一個位址 (ex: 0xfefefefe) //local接到了ptr指向的那個位置,接著函式內local要到了新的位置但是ptr指向的位置還是沒變的,因此離開函式後就好像事什麼都沒發生,嚴格說起來還發生了 memory leak。 //以下是一種解決辦法: int* createNewArray(int size) { return (int*) malloc( size * sizeof(int) ); } int main() { int* ptr; ptr = createNewArray(10); } //改成這樣亦可 ( 為何用 int** 就可以?想想他會傳什麼過去給local ) void createNewArray(int** local, int size) { *local = (int*) malloc( size * sizeof(int) ); } int main() { int *ptr; createNewArray(&ptr, 10); } //如果是 C++可用 Reference: void newArray(int*& local, int size) { local = new int[size]; } |
繼續閱讀…
本文章 目錄:
--- 面試須知與應答技巧
--- 資料結構 / 變數儲存與記憶體
--- 程式語言 / 演算法
--- 作業系統 / 多執行緒(多程序)的觀念與實作控制
--- 轉自ptt: [重要] 發文前務必閱讀:C/C++常見問題十三誡
--- 擬真試題1/擬真試題2/擬真試題3/擬真試題4
--- 直接列出考古題1(精華完整53題含解答)
--- 直接列出考古題2(精華完整35題含解答)
--- 深度討論考古題1(DEMO完整10題含解答) 選擇使用C,C++,C#或JAVA
--- 深度討論考古題2(DEMO完整11題含解答) 選擇使用C,C++,C#或JAVA
--- 撲克牌(大老二)洗牌與牌型判斷(JAVA)
--- Python基本教學
--- Leetcode實戰討論