C語言中由于指針的靈活性著拭,導致指針能代替數組使用荷愕,或者混合使用衡怀,這些導致了許多指針和數組的迷惑,因此安疗,刻意再次深入探究了指針和數組這玩意兒抛杨,其他類型的數組比較簡單,容易混淆的是字符數組和字符指針這兩個荐类。怖现。。下面就開始剖析一下這兩位的恩怨情仇玉罐。屈嗤。。
〉跏洹1 數組的本質
數組是多個元素的集合饶号,在內存中分布在地址相連的單元中,所以可以通過其下標訪問不同單元的元素季蚂。茫船。
2 指針扭屁。
指針也是一種變量算谈,只不過它的內存單元中保存的是一個標識其他位置的地址。料滥。由于地址也是整數然眼,在32位平臺下,指針默認為32位葵腹。高每。
3 指針的指向践宴?
指向的直接意思就是指針變量所保存的其他的地址單元中所存放的數據類型觉义。
int? * p ;//p 變量保存的地址所在內存單元中的數據類型為整型
? ? ? ? ? float *q;// ........................................浮點型
? ? ? ? ? 不論指向的數據類型為那種,指針變量其本身永遠為整型浴井,因為它保存的地址晒骇。
? ? 4? 字符數組。。洪囤。
? ? ? ? 字面意思是數組徒坡,數組中的元素是字符。瘤缩。確實喇完,這就是它的本質意義。
? ? ? ? char? str[10];
? ? ? ? 定義了一個有十個元素的數組剥啤,元素類型為字符锦溪。
? ? ? ? C語言中定義一個變量時可以初始化。
? ? ? ? char? str[10] = {"hello world"};
? ? ? ? 當編譯器遇到這句時府怯,會把str數組中從第一個元素把hello world\0 逐個填入刻诊。。
? ? ? ? 由于C語言中沒有真正的字符串類型牺丙,可以通過字符數組表示字符串则涯,因為它的元素地址是連續(xù)的,這就足夠了冲簿。
? ? ? ? C語言中規(guī)定數組代表數組所在內存位置的首地址粟判,也是 str[0]的地址,即str = &str[0];
? ? ? ? 而printf("%s",str); 為什么用首地址就可以輸出字符串峦剔。档礁。
? ? ? ? ? 因為還有一個關鍵,在C語言中字符串常量的本質表示其實是一個地址吝沫,這是許多初學者比較難理解的問題呻澜。。野舶。
? ? ? ? ? 舉例:
? ? ? ? ? char? *s ;
? ? ? ? ? s = "China";
? ? ? ? ? 為什么可以把一個字符串賦給一個指針變量。宰衙。
? ? ? ? ? 這不是類型不一致嗎平道??供炼?
? ? ? ? ? 這就是上面提到的關鍵 一屋。。
? ? ? ? ? C語言中編譯器會給字符串常量分配地址袋哼,如果 "China", 存儲在內存中的 0x3000 0x3001 0x3002 0x3003 0x3004 0x3005 .
? ? ? ? ? s = "China" 冀墨,意識是什么,對了涛贯,地址诽嘉。
? ? ? ? ? 其實真正的意義是 s ="China" = 0x3000;
? ? ? ? ? 看清楚了吧 ,你把China 看作是字符串,但是編譯器把它看作是地址 0x3000虫腋,即字符串常量的本質表現是代表它的第一個字符的地址骄酗。。悦冀。趋翻。。盒蟆。踏烙。。历等。讨惩。
? ? ? ? ? s = 0x3000
? ? ? ? ? 這樣寫似乎更符合直觀的意思。募闲。步脓。
? ? ? ? ? 搞清楚這個問題。浩螺。
? ? ? ? ? 那么 %s 靴患,它的原理其實也是通過字符串首地址輸出字符串,printf("%s ", s);? 傳給它的其實是s所保存的字符串的地址要出。鸳君。。
? ? ? ? ? 比如
? ? ? ? #include <stdio.h>
? ? ? int main()
? ? ? {
? ? ? ? char *s;
? ? ? ? s = "hello";
? ? ? ? printf("%p\n",s);
? ? ? ? return 0;
? ? ? }
其實做為一個開發(fā)者患蹂,有一個學習的氛圍跟一個交流圈子特別重要這里我推薦一個C語言C++交流群583650410或颊,不管你是小白還是轉行人士歡迎入駐,大家一起交流成長传于。免費的公開課供你學習囱挑!
可以看到 s = 0x00422020 ,這也是"China"的首地址
? ? ? 所以沼溜,printf("%s",0x00422020);也是等效的平挑。。
? ? ? 字符數組:
? ? ? char? str[10] = "hello"系草;
? ? ? 前面已經說了通熄,str = &str[0] , 也等于 "hello"的首地址找都。唇辨。
? ? ? 所以printf("%s",str); 本質也是 printf("%s", 地址");
? ? ? ? C語言中操作字符串是通過它在內存中的存儲單元的首地址進行的,這是字符串的終極本質能耻。赏枚。亡驰。
? ? 5? char *? 與 char? a[ ];
? ? ? char? *s;
? ? ? char? a[ ] ;
? ? ? 前面說到 a代表字符串的首地址,而s 這個指針也保存字符串的地址(其實首地址)嗡贺,即第一個字符的地址隐解,這個地址單元中的數據是一個字符,
? 這也與 s 所指向的 char 一致诫睬。
? ? ? 因此可以 s = a;
? ? ? 但是不能 a = s;
? ? ? C語言中數組名可以復制給指針表示地址煞茫, 但是卻不能賦給給數組名,它是一個常量類型摄凡,所以不能修改续徽。。
? ? ? 當然也可以這樣:
? ? ? ? char? a [ ] = "hello";
? ? ? ? char *s =a;
? ? ? ? for(int i= 0; i < strlen(a) ; i++)
? ? ? ? ? ? printf("%c", s[i]);
? ? ? ? 或? printf("%c",*s++);
? ? ? ? 字符指針可以用 間接操作符 *取其內容亲澡,也可以用數組的下標形式 [ ]钦扭,數組名也可以用 *操作,因為它本身表示一個地址 床绪。客情。
? ? ? 比如 printf("%c",*a);? 將會打印出 'h'
? ? ? char * 與 char a[ ] 的本質區(qū)別:
? ? ? 當定義 char a[10 ]? 時,編譯器會給數組分配十個單元癞己,每個單元的數據類型為字符膀斋。。
? ? ? 而定義 char *s 時痹雅,? 這是個指針變量仰担,只占四個字節(jié),32位绩社,用來保存一個地址摔蓝。。
? ? ? sizeof(a) = 10 愉耙;
? ? ? sizeof(s)? = ?
? ? ? 當然是4了贮尉,編譯器分配4個字節(jié)32位的空間,這個空間中將要保存地址朴沿。猜谚。。
? ? ? ? printf("%p",s);
? ? ? ? 這個表示 s 的單元中所保存的地址悯仙。龄毡。
? ? ? ? printf("%p",&s);
? ? ? ? 這個表示變量本身所在內存單元地址吠卷。锡垄。。祭隔。货岭,不要搞混了路操。。
? ? ? ? 用一句話來概括千贯,就是 char *s 只是一個保存字符串首地址的指針變量屯仗, char a[ ] 是許多連續(xù)的內存單元,單元中的元素為char 搔谴,之所以用 char *能達到
char a? [ ] 的效果魁袜,還是字符串的本質,地址敦第,即給你一個字符串地址峰弹,便可以隨心所欲的操所他。芜果。但是鞠呈,char* 和 char a[ ] 的本質屬性是不一樣的。右钾。
? ? 6? ? ? char **? 與char? * a[ ] ;
? ? ? ? ? ? 先看 char? *a [ ] ;
? ? ? ? ? ? 由于[ ] 的優(yōu)先級高于* 所以a先和 [ ]結合蚁吝,他還是一個數組,數組中的元素才是char * 舀射,前面講到char * 是一個變量窘茁,保存的地址。后控。
? ? ? ? ? ? 所以 char *a[ ] = {"China","French","America","German"}庙曙;
? ? ? ? ? ? 同過這句可以看到, 數組中的元素是字符串浩淘,那么sizeof(a) 是多少呢捌朴,有人會想到是五個單詞的占內存中的全部字節(jié)數 6+7+8+7 = 28;
? ? ? ? ? ? 但是其實sizeof(a) = 16张抄;
? ? ? ? ? ? 為什么砂蔽,前面已經說到, 字符串常量的本質是地址署惯,a 數組中的元素為char * 指針左驾,指針變量占四個字節(jié),那么四個元素就是16個字節(jié)了
? ? ? ? ? ? 看一下實例:
? ? ? ? ? ? #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;
}
其實做為一個開發(fā)者极谊,有一個學習的氛圍跟一個交流圈子特別重要這里我推薦一個C語言C++交流群583650410诡右,不管你是小白還是轉行人士歡迎入駐,大家一起交流成長轻猖。免費的公開課供你學習帆吻!
可以看到數組中的四個元素保存了四個內存地址,這四個地址中就代表了四個字符串的首地址咙边,而不是字符串本身猜煮。次员。。
? ? ? 因此sizeof(a)當然是16了王带。淑蔚。
? ? ? 注意這四個地址是不連續(xù)的,它是編譯器為"China","French","America","German" 分配的內存空間的地址愕撰, 所以刹衫,四個地址沒有關聯。
? ? ? ? #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,這四個是元素單元所在的地址搞挣,每個地址相差四個字節(jié)绪妹,這是由于每個元素是一個指針變量占四個字節(jié)。柿究。邮旷。
? ? ? char **s;
? ? ? char **為二級指針, s保存一級指針 char *的地址蝇摸,關于二級指針就在這里不詳細討論了 婶肩,簡單的說一下二級指針的易錯點。?
? ? ? 舉例:
? ? ? char *a [ ] = {"China","French","America","German"};
? ? ? char **s =? a;
? ? ? 為什么能把 a賦給s,因為數組名a代表數組元素內存單元的首地址貌夕,即 a = &a[0] = 0012FF38;
? ? ? 而 0x12FF38即 a[0]中保存的又是 00422FB8 ,這個地址律歼, 00422FB8為字符串"China"的首地址。
? ? ? 即 *s = 00422FB8 = "China";
? ? ? ? 這樣便可以通過s 操作 a 中的數據
? ? ? printf("%s",*s);
? ? ? printf("%s",a[0]);
? ? ? printf("%s",*a);
? ? ? 都是一樣的啡专。险毁。。
? ? ? 但還是要注意们童,不能a = s畔况,前面已經說到,a 是一個常量慧库。跷跪。
? ? ? 再看一個易錯的點:
? ? ? char **s = "hello world";
? ? ? 這樣是錯誤的,
? ? ? 因為? s 的類型是 char **? 而 "hello world "的類型是 char *
? ? ? 雖然都是地址齐板, 但是指向的類型不一樣吵瞻,因此,不能這樣用甘磨。橡羞,從其本質來分析,"hello world",代表一個地址济舆,比如0x003001,這個地址中的內容是 'h'
? ,為 char 型卿泽,而 s 也保存一個地址 ,這個地址中的內容(*s) 是char * 吗冤,是一個指針類型又厉, 所以兩者類型是不一樣的∽滴粒 覆致。。
如果是這樣呢肺蔚?
char? **s;
? ? ? *s = "hello world";
? ? ? 貌似是合理的煌妈,編譯也沒有問題,但是 printf("%s",*s),就會崩潰
? ? ? why??
? ? ? 咱來慢慢推敲一下宣羊。璧诵。
? ? ? printf("%s",*s); 時,首先得有s 保存的地址仇冯,再在這個地址中找到 char *? 的地址之宿,即*s;
? ? ? 舉例:
? ? ? s = 0x1000;
? ? ? 在0x1000所在的內存單元中保存了"hello world"的地址 0x003001 , *s = 0x003001;
? ? ? 這樣printf("%s",*s);
? ? ? 這樣會先找到 0x1000,然后找到0x003001;
? ? ? 如果直接 char? **s;
? ? ? *s = "hello world";
? ? ? s 變量中保存的是一個無效隨機不可用的地址苛坚, 誰也不知道它指向哪里比被。。泼舱。等缀。,*s 操作會崩潰娇昙。尺迂。
? ? ? 所以用 char **s 時,要給它分配一個內存地址冒掌。
? ? ? char? **s ;
? ? ? s = (char **) malloc(sizeof(char**));
? ? ? *s =? "hello world";
? ? ? 這樣 s 給分配了了一個可用的地址噪裕,比如 s = 0x412f;
? ? ? 然后在 0x412f所在的內存中的位置,保存 "hello world"的值股毫。州疾。
? ? 再如:
? ? #include? <stdio.h>
? void? buf( char **s)
? ? {
? ? ? ? ? *s = "message";
? ? }
? ? int main()
? ? {
? ? ? ? char *s ;
? ? ? ? buf(&s);
? ? ? ? printf("%s\n",s);
? ? }
? ? 二級指針的簡單用法。皇拣。严蓖。。氧急,說白了颗胡,二級指針保存的是一級指針的地址,它的類型是指針變量吩坝,而一級指針保存的是指向數據所在的內存單元的地址毒姨,雖然都是地址,但是類型是不一樣的钉寝。弧呐。闸迷。