深入 理解char * ,char ** ,char a[ ] ,char *a[] 的區(qū)別

文章轉(zhuǎn)自https://www.imooc.com/article/30475

C語言中由于指針的靈活性,導(dǎo)致指針能代替數(shù)組使用,或者混合使用,這些導(dǎo)致了許多指針和數(shù)組的迷惑,因此宾尚,刻意再次深入探究了指針和數(shù)組這玩意兒,其他類型的數(shù)組比較簡單谢澈,容易混淆的是字符數(shù)組和字符指針這兩個煌贴。。锥忿。下面就開始剖析一下這兩位的恩怨情仇牛郑。。敬鬓。

1.數(shù)組的本質(zhì)

數(shù)組是多個元素的集合淹朋,在內(nèi)存中分布在地址相連的單元中,所以可以通過其下標(biāo)訪問不同單元的元素钉答。础芍。

2.指針。

指針也是一種變量数尿,只不過它的內(nèi)存單元中保存的是一個標(biāo)識其他位置的地址仑性。。由于地址也是整數(shù)右蹦,在32位平臺下诊杆,指針默認(rèn)為32位歼捐。。

3.指針的指向

指向的直接意思就是指針變量所保存的其他的地址單元中所存放的數(shù)據(jù)類型晨汹。

int  * p ;  //p 變量保存的地址所在內(nèi)存單元中的數(shù)據(jù)類型為整型

float *q;  // ........................................浮點型

不論指向的數(shù)據(jù)類型為那種豹储,指針變量其本身永遠為整型,因為它保存的地址淘这。

4.字符數(shù)組

字面意思是數(shù)組剥扣,數(shù)組中的元素是字符。铝穷。確實钠怯,這就是它的本質(zhì)意義。

char  str[10]; 

定義了一個有十個元素的數(shù)組氧骤,元素類型為字符。

C語言中定義一個變量時可以初始化吃引。

char  str[10] = {"hello world"};

當(dāng)編譯器遇到這句時筹陵,會把str數(shù)組中從第一個元素把hello world\0 逐個填入。

由于C語言中沒有真正的字符串類型镊尺,可以通過字符數(shù)組表示字符串朦佩,因為它的元素地址是連續(xù)的,這就足夠了庐氮。

C語言中規(guī)定數(shù)組代表數(shù)組所在內(nèi)存位置的首地址语稠,也是 str[0]的地址,即str = &str[0];

而printf("%s",str); 為什么用首地址就可以輸出字符串弄砍。仙畦。

因為還有一個關(guān)鍵,在C語言中字符串常量的本質(zhì)表示其實是一個地址音婶,這是許多初學(xué)者比較難理解的問題慨畸。

舉例:

char  *s ;
s = "China";

為什么可以把一個字符串賦給一個指針變量。衣式。

這不是類型不一致嗎寸士??碴卧?

這就是上面提到的關(guān)鍵 弱卡。。

C語言中編譯器會給字符串常量分配地址住册,如果 "China", 存儲在內(nèi)存中的 0x3000 0x3001 0x3002 0x3003 0x3004 0x3005 .

s = "China" 婶博,意識是什么,對了荧飞,地址凡蜻。

其實真正的意義是 s ="China" = 0x3000;

看清楚了吧 搭综,你把China 看作是字符串,但是編譯器把它看作是地址 0x3000划栓,即字符串常量的本質(zhì)表現(xiàn)是代表它的第一個字符的地址兑巾。。忠荞。蒋歌。。委煤。堂油。。碧绞。府框。

s = 0x3000


樣寫似乎更符合直觀的意思。讥邻。迫靖。

搞清楚這個問題。兴使。

那么 %s 系宜,它的原理其實也是通過字符串首地址輸出字符串,printf("%s ", s); 傳給它的其實是s所保存的字符串的地址发魄。盹牧。。

比如

#include <stdio.h>

int main()
{
        char *s;
        s = "hello";
        printf("%p\n",s);
        return 0;
}
image

可以看到 s = 0x00422020 励幼,這也是"China"的首地址

所以汰寓,printf("%s",0x00422020);也是等效的。苹粟。

字符數(shù)組:

char  str[10] = "hello"踩寇;

前面已經(jīng)說了,str = &str[0] 六水, 也等于 "hello"的首地址俺孙。。

所以printf("%s",str); 本質(zhì)也是 printf("%s", 地址");

C語言中操作字符串是通過它在內(nèi)存中的存儲單元的首地址進行的掷贾,這是字符串的終極本質(zhì)睛榄。

5.char * 與 char a[ ];

 char  *s;
 char  a[ ] ;

前面說到 a代表字符串的首地址,而s 這個指針也保存字符串的地址(其實首地址)想帅,即第一個字符的地址场靴,這個地址單元中的數(shù)據(jù)是一個字符,

這也與 s 所指向的 char 一致。

因此可以 s = a;

但是不能 a = s;

C語言中數(shù)組名可以復(fù)制給指針表示地址旨剥, 但是卻不能賦給給數(shù)組名咧欣,它是一個常量類型,所以不能修改轨帜。魄咕。

當(dāng)然也可以這樣:

char  a [ ] = "hello";
char *s =a;
for(int i= 0; i < strlen(a) ; i++) {
  printf("%c", s[i]);
}
   或  printf("%c",*s++);

字符指針可以用 間接操作符 *取其內(nèi)容,也可以用數(shù)組的下標(biāo)形式 [ ]蚌父,數(shù)組名也可以用 *操作哮兰,因為它本身表示一個地址 。苟弛。

比如 printf("%c",*a); 將會打印出 'h'

char * 與 char a[ ] 的本質(zhì)區(qū)別:

當(dāng)定義 char a[10 ] 時喝滞,編譯器會給數(shù)組分配十個單元,每個單元的數(shù)據(jù)類型為字符膏秫。右遭。

而定義 char *s 時, 這是個指針變量缤削,只占四個字節(jié)窘哈,32位,用來保存一個地址僻他。宵距。

sizeof(a) = 10 腊尚;

sizeof(s) = ?

當(dāng)然是4了吨拗,編譯器分配4個字節(jié)32位的空間,這個空間中將要保存地址婿斥。劝篷。。

printf("%p",s);

這個表示 s 的單元中所保存的地址民宿。娇妓。

printf("%p",&s);

這個表示變量本身所在內(nèi)存單元地址。活鹰。哈恰。。志群,不要搞混了着绷。。

用一句話來概括锌云,就是 char *s 只是一個保存字符串首地址的指針變量荠医, char a[ ] 是許多連續(xù)的內(nèi)存單元,單元中的元素為char ,之所以用 char *能達到

char a [ ] 的效果彬向,還是字符串的本質(zhì)兼贡,地址,即給你一個字符串地址娃胆,便可以隨心所欲的操所他遍希。。但是缕棵,char* 和 char a[ ] 的本質(zhì)屬性是不一樣的孵班。。

6.char ** 與char * a[ ] ;

先看 char *a [ ] ;

由于[ ] 的優(yōu)先級高于* 所以a先和 [ ]結(jié)合招驴,他還是一個數(shù)組篙程,數(shù)組中的元素才是char * ,前面講到char * 是一個變量别厘,保存的地址虱饿。。

所以 char *a[ ] = {"China","French","America","German"}触趴;

同過這句可以看到氮发, 數(shù)組中的元素是字符串,那么sizeof(a) 是多少呢冗懦,有人會想到是五個單詞的占內(nèi)存中的全部字節(jié)數(shù) 6+7+8+7 = 28爽冕;

但是其實sizeof(a) = 16;

為什么披蕉,前面已經(jīng)說到颈畸, 字符串常量的本質(zhì)是地址,a 數(shù)組中的元素為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;
}
image

可以看到數(shù)組中的四個元素保存了四個內(nèi)存地址,這四個地址中就代表了四個字符串的首地址爬凑,而不是字符串本身徙缴。。嘁信。

因此sizeof(a)當(dāng)然是16了于样。。

注意這四個地址是不連續(xù)的潘靖,它是編譯器為"China","French","America","German" 分配的內(nè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]); //數(shù)組元素中保存的地址
  printf("%p %p %p %p\n",&a[0],&a[1],&a[2],&a[3]);//數(shù)組元素單元本身的地址

  return 0;
}
image

可以看到 0012FF38 0012FF3C 0012FF40 0012FF44,這四個是元素單元所在的地址携御,每個地址相差四個字節(jié),這是由于每個元素是一個指針變量占四個字節(jié)。

char **s

char **為二級指針啄刹, s保存一級指針 char *的地址涮坐,關(guān)于二級指針就在這里不詳細(xì)討論了 ,簡單的說一下二級指針的易錯點誓军。

舉例:

char *a [ ] = {"China","French","America","German"};

char **s =   a;

為什么能把 a賦給s,因為數(shù)組名a代表數(shù)組元素內(nèi)存單元的首地址袱讹,即 a = &a[0] = 0012FF38;

而 0x12FF38即 a[0]中保存的又是 00422FB8 ,這個地址, 00422FB8為字符串"China"的首地址昵时。

即 *s = 00422FB8 = "China";

這樣便可以通過s 操作 a 中的數(shù)據(jù)

printf("%s",*s);

printf("%s",a[0]);

printf("%s",*a);

都是一樣的捷雕。。壹甥。

但還是要注意救巷,不能a = s,前面已經(jīng)說到句柠,a 是一個常量浦译。。

再看一個易錯的點:

char **s = "hello world";

這樣是錯誤的溯职,

因為 s 的類型是 char ** 而 "hello world "的類型是 char *

雖然都是地址精盅, 但是指向的類型不一樣,因此谜酒,不能這樣用叹俏。,從其本質(zhì)來分析僻族,"hello world",代表一個地址粘驰,比如0x003001,這個地址中的內(nèi)容是 'h'
,為 char 型,而 s 也保存一個地址 鹰贵,這個地址中的內(nèi)容(*s) 是char * 晴氨,是一個指針類型康嘉, 所以兩者類型是不一樣的碉输。 亭珍。敷钾。

如果是這樣呢?

char  **s;

*s = "hello world";

貌似是合理的肄梨,編譯也沒有問題筐骇,但是 printf("%s",*s),就會崩潰

why??

咱來慢慢推敲一下烈涮。。

printf("%s",s); 時,首先得有s 保存的地址模她,再在這個地址中找到 char * 的地址缭保,即s;

舉例:

s = 0x1000;

在0x1000所在的內(nèi)存單元中保存了"hello world"的地址 0x003001 , *s = 0x003001;

這樣printf("%s",*s);

這樣會先找到 0x1000,然后找到0x003001;

如果直接 char **s;

*s = "hello world";

s 變量中保存的是一個無效隨機不可用的地址, 誰也不知道它指向哪里蓖宦。。油猫。稠茂。,*s 操作會崩潰情妖。睬关。

所以用 char **s 時,要給它分配一個內(nèi)存地址毡证。

char **s ;

s = (char ) malloc(sizeof(char));

*s = "hello world";

這樣 s 給分配了了一個可用的地址电爹,比如 s = 0x412f;

然后在 0x412f所在的內(nèi)存中的位置,保存 "hello world"的值料睛。藐不。

再如:

#include  <stdio.h>
void  buf( char **s)
{
  *s = "message";
}
int main()

{
  char *s ;
  buf(&s);
  printf("%s\n",s);

}

二級指針的簡單用法。秦效。雏蛮。。阱州,說白了挑秉,二級指針保存的是一級指針的地址,它的類型是指針變量苔货,而一級指針保存的是指向數(shù)據(jù)所在的內(nèi)存單元的地址犀概,雖然都是地址,但是類型是不一樣的夜惭。姻灶。。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诈茧,一起剝皮案震驚了整個濱河市产喉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌敢会,老刑警劉巖曾沈,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鸥昏,居然都是意外死亡塞俱,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門吏垮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來障涯,“玉大人罐旗,你說我怎么就攤上這事∥ǖ” “怎么了尤莺?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長生棍。 經(jīng)常有香客問我颤霎,道長,這世上最難降的妖魔是什么涂滴? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任友酱,我火速辦了婚禮,結(jié)果婚禮上柔纵,老公的妹妹穿的比我還像新娘缔杉。我一直安慰自己,他們只是感情好搁料,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布或详。 她就那樣靜靜地躺著,像睡著了一般郭计。 火紅的嫁衣襯著肌膚如雪霸琴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天昭伸,我揣著相機與錄音梧乘,去河邊找鬼。 笑死庐杨,一個胖子當(dāng)著我的面吹牛选调,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播灵份,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼仁堪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了填渠?” 一聲冷哼從身側(cè)響起弦聂,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎揭蜒,沒想到半個月后横浑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體剔桨,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡屉更,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了洒缀。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瑰谜。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡欺冀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出萨脑,到底是詐尸還是另有隱情隐轩,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布渤早,位于F島的核電站职车,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏鹊杖。R本人自食惡果不足惜悴灵,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望骂蓖。 院中可真熱鬧积瞒,春花似錦、人聲如沸登下。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽被芳。三九已至缰贝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間畔濒,已是汗流浹背揩瞪。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留篓冲,地道東北人李破。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像壹将,于是被迫代替她去往敵國和親嗤攻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容