1. 不可以提取不知指向何方的指標(指標變數必需先指向某個可以合法操作的
空間,才能進行操作):
錯誤例子: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);
2. 不要試圖用 char* 去更改一個"字串常數":
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);
3. 不能在函式中回傳一個指向區域性自動變數的指標(區域變數在離開該區域
時被消滅,因此呼叫端得到的指標所指的字串內容失效了,但如果指標內容
是用動態的方式配置,這塊空間是在heap而非stack上,heap空間不會自
動回收,因此這塊空間在離開函式後依然有效,但要記得free掉記憶體):
錯誤例子: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;
}
4. 在一個運算式中,不能對一個基本型態的變數修改其值超過一次以上(C/C++
並沒有強制規定參數會由哪個方向開始處理(不像Java是由左到右),因此可
能會造成與預期不符的情況):
錯誤例子: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++);
}
5. 在 Macro 定義中, 務必為它的參數個別加上括號():
錯誤例子:#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 之前,取得函式位址。
6. 不可以在 stack 設置過大的變數(編譯器會自行決定 stack 的上限,可能是
數KB 或數十 KB,當變數所需的空間過大時,很容易造成 stack overflow,
程式亦隨之當掉,若真正需要如此大的空間,那麼建議配置在 heap 上,或
是採用static / globla variabl):
錯誤例子:int array[10000000];
正確例子:int *array = (int*) malloc(10000000*sizeof(int));
7. 不要猜想二維陣列可以用 pointer to pointer 來傳遞:
void pass1DArray( int array[] );
int a[10];
pass1DArray( a ); /* 可以合法編譯,而且執行結果正確!! */
事實上,編譯器會這麼看待
void pass1DArray( int *array );
int a[10];
pass1DArray( &a[0] );
二維陣列不能直接改成 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 );
8. 函式內 new 出來的空間記得要讓主程式的指標接住:
錯誤例子:void newArray(int* local, int size) {
local = (int*) malloc( size * sizeof(int) );
}
int main() {
int* ptr;
newArray(ptr, 10);
}
原因如下:
1. int* ptr; ptr -> |__未知的空間__|
______________
2. 呼叫函式 newArray ptr -> |__未知的空間__| <- local
______________
3. malloc 取得合法空間 ptr -> |__未知的空間__|
______________
|___合法空間___| <- local
______________
4. 離開函式 ptr -> |__未知的空間__|
local接到了ptr指向的那個位置,接著函式內local要到了新的位置但是
ptr指向的位置還是沒變的,因此離開函式後就好像事什麼都沒發生,嚴格
說起來還發生了 memory leak。
以下是一種解決辦法:int* createNewArray(int size) {
return (int*) malloc( size * sizeof(int) );
}
int main() {
int* ptr;
ptr = createNewArray(10);
}
如果是 C++可用 Reference:void newArray(int*& local, int size) {
local = new int[size];
}