深入理解char * ,char ** ,char a[ ] ,char *a[]
數組是多個(gè)元素的集合,在內存中分布在地址相連的單元中,所以可以通過(guò)其下標訪(fǎng)問(wèn)不同單元的元素。
2.指針指針也是一種變量,只不過(guò)它的內存單元中保存的是一個(gè)標識其他位置的地址。由于地址也是整數,在32位平臺下,指針默認為32位。
3.指針的指向指向的直接意思就是指針變量所保存的其他的地址單元中所存放的數據類(lèi)型。
int *p; //p變量保存的地址所在內存單元中的數據類(lèi)型為整型float *q; // .............浮點(diǎn)型
不論指向的數據類(lèi)型為哪種,指針變量其本身永遠為整型,因為它保存的地址。
4.字符數組字面意思是數組,數組中的元素是字符。確實(shí),這就是它的本質(zhì)意義。
char str[10]; //定義了一個(gè)有十個(gè)元素的數組,元素類(lèi)型為字符。
C語(yǔ)言中定義一個(gè)變量時(shí)可以初始化。
char str[10] = {"hello"};
當編譯器遇到這句時(shí),會(huì )把str數組中從第一個(gè)元素把hello\0 逐個(gè)填入。
由于C語(yǔ)言中沒(méi)有真正的字符串類(lèi)型,可以通過(guò)字符數組表示字符串,因為它的元素地址是連續的,這就足夠了。
C語(yǔ)言中規定數組代表數組所在內存位置的首地址,也是 str[0]的地址,即str = &str[0];
另外
printf("%s",str);
為什么用首地址就可以輸出字符串。
因為還有一個(gè)關(guān)鍵,在C語(yǔ)言中字符串常量的本質(zhì)表示其實(shí)是一個(gè)地址,這是許多初學(xué)者比較難理解的問(wèn)題。
舉例:
char *s ; s = "China";
為什么可以把一個(gè)字符串賦給一個(gè)指針變量。
這不是類(lèi)型不一致嗎
這就是上面提到的關(guān)鍵 。
C語(yǔ)言中編譯器會(huì )給字符串常量分配地址,如果 "China", 存儲在內存中的 0x3000 0x3001 0x3002 0x3003 0x3004 0x3005 .
s = "China" ,意識是什么,對了,地址。
其實(shí)真正的意義是 s ="China" = 0x3000;
看清楚了吧 ,你把China 看作是字符串,但是編譯器把它看作是地址 0x3000,即字符串常量的本質(zhì)表現是代表它的第一個(gè)字符的地址。
s = 0x3000
這樣寫(xiě)似乎更符合直觀(guān)的意思。
搞清楚這個(gè)問(wèn)題。
那么 %s ,它的原理其實(shí)也是通過(guò)字符串首地址輸出字符串,printf("%s ", s); 傳給它的其實(shí)是s所保存的字符串的地址。
比如
#include <stdio.h> int main() { char *s; s = "hello"; printf("%p\n",s); return 0; }
結果:
00422020
可以看到 s = 0x00422020 ,這也是"hello"的首地址
所以,printf("%s",0x00422020);也是等效的。
字符數組:
char str[10] = "hello";
前面已經(jīng)說(shuō)了,str = &str[0] ,也等于 "hello"的首地址。
所以printf("%s",str); 本質(zhì)也是 printf("%s", 地址);
C語(yǔ)言中操作字符串是通過(guò)它在內存中的存儲單元的首地址進(jìn)行的,這是字符串的終極本質(zhì)。
4.char * 與 char a[]char *s; char a[] ;
前面說(shuō)到 a代表字符串的首地址,而s 這個(gè)指針也保存字符串的地址(其實(shí)首地址),即第一個(gè)字符的地址,這個(gè)地址單元中的數據是一個(gè)字符,
這也與 s 所指向的 char 一致。
因此可以 s = a;
但是不能 a = s;
C語(yǔ)言中數組名可以復制給指針表示地址, 但是卻不能賦給給數組名,它是一個(gè)常量類(lèi)型,所以不能修改。
當然也可以這樣:
char a [ ] = "hello"; char *s =a; for(int i= 0; i < strlen(a) ; i++){ printf("%c", s[i]); //或 printf("%c",*s++); }
字符指針可以用 間接操作符 *取其內容,也可以用數組的下標形式 [ ],數組名也可以用 *操作,因為它本身表示一個(gè)地址 。
比如
printf("%c",*a);
將會(huì )打印出 'h'
5.char * 與 char a[] 的本質(zhì)區別:當定義 char a[10] 時(shí),編譯器會(huì )給數組分配十個(gè)單元,每個(gè)單元的數據類(lèi)型為字符。
定義 char *s 時(shí), 這是個(gè)指針變量,只占四個(gè)字節,32位,用來(lái)保存一個(gè)地址。
sizeof(a) = 10 ; sizeof(s) = ?
當然是4了,編譯器分配4個(gè)字節32位的空間,這個(gè)空間中將要保存地址。
printf("%p",s);
這個(gè)表示 s 的單元中所保存的地址。
printf("%p",&s);
這個(gè)表示變量本身所在內存單元地址,不要搞混了。
用一句話(huà)來(lái)概括,就是 char *s 只是一個(gè)保存字符串首地址的指針變量, char a[] 是許多連續的內存單元,單元中的元素為char 。
之所以用 char *能達到char a[]的效果,還是字符串的本質(zhì),地址。即給你一個(gè)字符串地址,便可以隨心所欲的操作他,但是,char *和char a[]的本質(zhì)屬性是不一樣的。
6.char ** 與char *a[]先看
char *a[] ;
由于[] 的優(yōu)先級高于 * 所以a先和 []結合,他還是一個(gè)數組,數組中的元素才是char * ,前面講到char * 是一個(gè)變量,保存的地址。
char *a[] = {"China","French","America","German"};
通過(guò)這句可以看到, 數組中的元素是字符串,那么sizeof(a) 是多少呢,有人會(huì )想到是五個(gè)單詞的占內存中的全部字節數 6+7+8+7 = 28;
但是其實(shí)sizeof(a) = 16;
為什么,前面已經(jīng)說(shuō)到, 字符串常量的本質(zhì)是地址,a 數組中的元素為char * 指針,指針變量占四個(gè)字節,那么四個(gè)元素就是16個(gè)字節了
看一下實(shí)例:
#include <stdio.h> int main() { char *a [] = {"China","French","America","German"}; printf("%p %p %p %p\n",a[0],a[1],a[2],a[3]); return 0; }
可以看到數組中的四個(gè)元素保存了四個(gè)內存地址,這四個(gè)地址中就代表了四個(gè)字符串的首地址,而不是字符串本身。
因此sizeof(a)當然是16了。。
注意這四個(gè)地址是不連續的,它是編譯器為"China","French","America","German" 分配的內存空間的地址, 所以,四個(gè)地址沒(méi)有關(guān)聯(lián)。
#include <stdio.h> int main() { char *a [ ] = {"China","French","America","German"}; printf("%p %p %p %p\n",a[0],a[1],a[2],a[3]); //數組元素中保存的地址 printf("%p %p %p %p\n",&a[0],&a[1],&a[2],&a[3]);//數組元素單元本身的地址 return 0; }
可以看到 0012FF38 0012FF3C 0012FF40 0012FF44,這四個(gè)是元素單元所在的地址,每個(gè)地址相差四個(gè)字節,這是由于每個(gè)元素是一個(gè)指針變量占四個(gè)字節。
char **s;
char **為二級指針, s保存一級指針 char *的地址,關(guān)于二級指針就在這里不詳細討論了 ,簡(jiǎn)單的說(shuō)一下二級指針的易錯點(diǎn)。
舉例:
char *a[] = {"China","French","America","German"}; char **s = a;
為什么能把 a賦給s,因為數組名a代表數組元素內存單元的首地址,即 a = &a[0] = 0012FF38;
而 0x12FF38即 a[0]中保存的又是 00422FB8 ,這個(gè)地址, 00422FB8為字符串"China"的首地址。
*s = 00422FB8 = "China";
這樣便可以通過(guò)s 操作 a 中的數據
printf("%s",*s); printf("%s",a[0]); printf("%s",*a);
都是一樣的。
但還是要注意,不能a = s,前面已經(jīng)說(shuō)到,a 是一個(gè)常量。
再看一個(gè)易錯的點(diǎn):
char **s = "hello world";
這樣是錯誤的,
因為 s 的類(lèi)型是 char ** 而 "hello world "的類(lèi)型是 char *
雖然都是地址, 但是指向的類(lèi)型不一樣,因此,不能這樣用。
從其本質(zhì)來(lái)分析,"hello world",代表一個(gè)地址,比如0x003001,這個(gè)地址中的內容是 'h',為 char 型,而 s 也保存一個(gè)地址 ,這個(gè)地址中的內容(*s) 是char * ,是一個(gè)指針類(lèi)型,所以?xún)烧哳?lèi)型是不一樣的。
如果是這樣呢:
char **s; *s = "hello world";
貌似是合理的,編譯也沒(méi)有問(wèn)題,但是 printf("%s",*s),就會(huì )崩潰,
咱來(lái)慢慢推敲一下。
printf("%s",*s); 時(shí),首先得有s 保存的地址,再在這個(gè)地址中找到 char * 的地址,即 *s;
** 舉例:**
s = 0x1000;
在0x1000所在的內存單元中保存了"hello world"的地址 0x003001 , *s = 0x003001;
這樣printf("%s",*s);
這樣會(huì )先找到 0x1000,然后找到0x003001;
如果直接
char **s; *s = "hello world";
s 變量中保存的是一個(gè)無(wú)效隨機不可用的地址, 誰(shuí)也不知道它指向哪里,*s 操作會(huì )崩潰。
所以用 char **s 時(shí),要給它分配一個(gè)內存地址。
char **s ; s = (char **) malloc(sizeof(char**)); *s = "hello world";
這樣 s 給分配了了一個(gè)可用的地址,比如 s = 0x412f;
然后在 0x412f所在的內存中的位置,保存 "hello world"的值。
再如:
#include <stdio.h> void buf( char **s) { *s = "message"; } int main() { char *s ; buf(&s); printf("%s\n",s); }
二級指針的簡(jiǎn)單用法。,說(shuō)白了,二級指針保存的是一級指針的地址,它的類(lèi)型是指針變量,而一級指針保存的是指向數據所在的內存單元的地址,雖然都是地址,但是類(lèi)型是不一樣的。
最后說(shuō)明,sizoof(指針)的大小根據電腦操作系統而定,一般32位操作系統所占用的內存大小是4,64位操作系統指針的大小是8。
char **s; *s = "hello world";
在我的ubuntu上打印 printf("%s",*s),沒(méi)有崩潰。
更多參考
*博客內容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀(guān)點(diǎn),如有侵權請聯(lián)系工作人員刪除。