在分析指針的代碼時,腦子裏必定要有一張內存的分配圖,用於分析各個變量的存儲狀況。數組
一、用變量a給出下面的定義
a) 一個整型數(An integer)
b)一個指向整型數的指針( A pointer to an integer)
c)一個指向指針的的指針,它指向的指針是指向一個整型數( A pointer to a pointer to an intege)r
d)一個有10個整型數的數組( An array of 10 integers)
e) 一個有10個指針的數組,該指針是指向一個整型數的。(An array of 10 pointers to integers)
f) 一個指向有10個整型數數組的指針( A pointer to an array of 10 integers)
g) 一個指向函數的指針,該函數有一個整型參數並返回一個整型數(A pointer to a function that takes an integer as an argument and returns an integer)
h) 一個有10個指針的數組,該指針指向一個函數,該函數有一個整型參數並返回一個整型數 ( An array of ten pointers to functions that take an integer argument and return an integer )
答案是:
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer函數
備註:spa
「&」和「*」兩個運算符的優先級別相同,但按自右而左方向結合設計
在定義時,*號表示後面的變量的類型是指針變量,例如「int *a;」代表a是指針變量,「int * a[10];」代表a[10]裏面成員爲指針(變量),「int (*a)[10]」代表a爲指針變量(該指針類型爲int ()[10])3d
C語言函數返回類型爲指針時的一些問題指針
1.code
#include<stdio.h> char *returnStr() { char *p = 「tigerjibo」; return p; } int main() { char*str; str =returnStr(); //str[0]=’T’; 則會引發錯誤,不能修改只讀數據段中的內容 printf(「%s\n」,str); return0; }
來分析下該程序。blog
(1)char *p = 「tigerjibo」。系統在棧上分配四個字節的空間存放p的數值。「tigerjibo」是字符常量,存放在只讀數據段內。指向完後,系統把」tigerjibo」的地址賦值給p。內存
(2)函數用return 把p的數值返回。該數值指向只讀數據段(該數據段內的數據是靜態的不會改變)。退出子函數後,系統把p的數值銷燬。可是p的數值已經經過return 返回。且只讀數據段中的內容不會被修改和回收(其輸於靜態區域)字符串
(3)在主程序中把該地址又給了str。所以str指向了「tigerjbo」。
(4)該程序雖然能運行,擔又一個缺點,就是在程序中不能修改字符經常量中的數值。若是修改會引發段錯誤。
2.
#include<stdio.h> char *returnStr() { char p[]=」tigerjibo」; return p; } int main() { char *str; str =returStr(); printf(「%s\n」,str); }
編譯該程序後,系統會提示以下警告:
function returns address of local variable
(函數返回一個可變地址)
分析該錯誤:
1>」tigerjibo」是一個字符常量,存放在只讀數據段中,是不能被修改的。
2>char p[],是一個局部變量,當函數被調用時,在棧上開闢一個空間來存放數組P的內容。
3>char p[]=」tigerjibo」,該語句是把」tigerjibo」的值賦值給數值P,存放在數組p地址處。而不是把」tigerjibo」的地址賦值給數組p。所以,「tigerjibo」此時在系統中有一處備份,一個在只讀數據段中(不能修改,內容也不會被回收),一個在棧上存儲(能夠修改起內容,但函數退出後,其棧上存儲的內容也會被回收)。
4>所以,當return p,返回了數組的首地址,可是當函數退出後,其棧上的內容也將被丟棄,局部變量的內存也被清空了,所以該數組首地址處的內容是一個可變的值。
3.
#include<stdio.h> char *returnStr() { static char p[]=」tigerjibo」; return p; } int main() { char *str; str =returnStr(); str[0]=’T’; printf(「%s\n」,str); }
此程序運行正確。
分析以下:
1>」tigerjibo」是一個字符常量,存放在只讀數據段中,是不能被修改的。
2>static char p[],是一個靜態局部變量,在讀寫數據段中開闢一個空間給p用來存放其數值。
3>static char p[]=」tigerjibo」,該語句是把」tigerjibo」的值賦值給數值P,存放在數組p地址處。而不是把」tigerjibo」的地址賦值給數組p。所以,「tigerjibo」此時在系統中有一處備份,一個在只讀數據段中(不能修改,內容也不會被回收),一個在讀寫數據段中存儲(能夠修改其內容,當函數退出後,因其在讀寫數據段中存儲,起內容不會被丟棄)。
4>所以,當return p,返回了數組的首地址,可是當函數退出後,雖然棧上的內容都清除了,可是p地址是讀寫數據段中的地址,其上的內容不會被回收。
這裏先補充如下知識:
char day[15] = "abcdefghijklmn"; 這個語句執行的時候,系統就分配了一段長15的內存,並把這段內存起名爲day,裏面的值爲"abcdefghijklmn",以下圖所示:
對於char* strTmp = "opqrstuvwxyz";,這句語句執行後,字符串指針strTmp的內存的圖示以下:
4.
#include<stdio.h> #include<string.h> #include<strdlib.h> void getmemory(char *p) { p = (char *)malloc(100); } int main() { char *str=NULL; getmemory(str); strcpy(str,」helloworld」); printf(「%s\n」,str); }
編譯後錯誤:段錯誤
分析:在主程序中,str地址爲空。在函數傳遞中將str的地址傳給了子函數中的指針p(是拷貝了一份),而後在字函數中給p在堆上申請了一個100字節的空間,並把首地址賦值給p。可是函數傳遞中,p值改變不會影響到主函數中str的值。所以,str的地址仍爲空。在strcpy中引用空指針會出現段錯誤。
假如str不是初始化爲NULL,而是一個char字符串,在調用p = (char *)malloc(100);以前p的值以下:
在調用p = (char *)malloc(100);以後p的值以下:
可見,str依舊指向原來的地址
5.
void GetMemory2(char **p, int num) { *p = (char *)malloc(sizeof(char) * num); } void Test2(void){ char *str = NULL; GetMemory2(&str, 100); // 注意參數是 &str,而不是str strcpy(str, "hello"); cout<< str << endl; free(str); }
----------------------------------&str是指針的地址,將指針的地址傳給形參p,則p也指向str,
因此*p = (char *)malloc(sizeof(char) * num);也就是給p所指向的str分配了內存,因此正確。(在堆中分配的空間具備全局性)
假如str不是初始化爲NULL,而是一個char字符串,在調用p = (char *)malloc(100);以前p的值以下:
在調用p = (char *)malloc(100);以後p的值以下:
6.
char *GetMemory3(int num) { char *p = (char *)malloc(sizeof(char) * num); return p; } void Test3(void) { char *str = NULL; str = GetMemory3(100); strcpy(str, "hello"); cout<< str << endl; free(str); }
----------------------------正確
在「return p」以前:
在調用GetMemory以後:
7.
char *GetString(void) { char p[] = "hello world"; return p; // 編譯器將提出警告 } void Test4(void) { char *str = NULL; str = GetString(); // str 的內容是垃圾 cout<< str << endl; }
不要用return語句返回指向「棧內存」的指針,由於該內存在函數結束時自動消亡;
在「return p」以前:
在「return p」以後,棧已經被跟隨子函數的中止而銷燬,因此:
8.
char *GetString2(void) { char *p = "hello world"; return p; } void Test5(void) { char *str = NULL; str = GetString2(); cout<< str << endl; }
函 數Test5運行雖然不會出錯,可是函數GetString2的設計概念倒是錯誤的。由於GetString2內的「hello world」是常量字符串,位於靜態存儲區,它在程序生命期內恆定不變。不管何時調用GetString2,它返回的始終是同一個「只讀」的內存塊。
在「return p」以前:
在「return p」以後:
9.
void test(void) { char *p = (char *) malloc(100); strcpy(p, 「hello」); free(p); // p 所指的內存被釋放,可是p所指的地址仍然不變 … if(p != NULL) // 沒有起到防錯做用 { strcpy(p, 「world」); // 出錯 } }
在「free(p);」以前:
在「free(p);」以後:
即雖然「hello」所在的堆的100個空間已經被消除了,可是p的值仍是0x10006000,是個野指針,不能使用。因此建議在free以後,添加如下代碼:
p=NULL;
10.
#include <stdio.h> int main(void) { char *p = "helloworld"; *p = 'T'; printf("p=%s\n", p); return 0; }
編譯經過,但運行時「Segmentation fault (core dumped)」
Segmentation fault (core dumped)通常是對內存操做不當形成的,常見的有:
(1)數組超出範圍。
(2)修改了只讀內存。
這裏由於p指向"helloworld",是字符常量,存放在只讀數據段內,不能被修改。若將p指針改成數組,即正確。