博客專欄

        EEPW首頁 > 博客 > 深入理解char * ,char ** ,char a[ ] ,char *a[]

        深入理解char * ,char ** ,char a[ ] ,char *a[]

        發(fā)布人:電子禪石 時間:2024-01-26 來源:工程師 發(fā)布文章
        1.數(shù)組的本質(zhì)

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

        2.指針

        指針也是一種變量,只不過它的內(nèi)存單元中保存的是一個標識其他位置的地址。由于地址也是整數(shù),在32位平臺下,指針默認為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"};

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

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

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

        另外

        printf("%s",str);

        為什么用首地址就可以輸出字符串。

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

        舉例:

        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;  
        }

        結(jié)果:

        00422020

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

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

        字符數(shù)組:

        char  str[10] = "hello";

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

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

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

        4.char * 與 char a[]
        char  *s;
        
        char  a[] ;

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

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

        因此可以 s = a;

        但是不能 a = s;

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

        當然也可以這樣:

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

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

        比如

        printf("%c",*a);

        將會打印出 'h'

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

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

        定義 char *s 時, 這是個指針變量,只占四個字節(jié),32位,用來保存一個地址。

        sizeof(a) = 10 ; 
        sizeof(s)  = ?

        當然是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;  
          }

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

        因此sizeof(a)當然是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;  
          }

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

        char **s;

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

        舉例:

        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),就會崩潰,
        咱來慢慢推敲一下。

        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)存單元的地址,雖然都是地址,但是類型是不一樣的。

        最后說明,sizoof(指針)的大小根據(jù)電腦操作系統(tǒng)而定,一般32位操作系統(tǒng)所占用的內(nèi)存大小是4,64位操作系統(tǒng)指針的大小是8。

        char  **s;  
         *s = "hello world";

        在我的ubuntu上打印 printf("%s",*s),沒有崩潰。

        更多參考


        *博客內(nèi)容為網(wǎng)友個人發(fā)布,僅代表博主個人觀點,如有侵權(quán)請聯(lián)系工作人員刪除。



        關(guān)鍵詞: c

        相關(guān)推薦

        技術(shù)專區(qū)

        關(guān)閉
        主站蜘蛛池模板: 察隅县| 大埔县| 忻城县| 府谷县| 都江堰市| 芜湖县| 凯里市| 托里县| 安徽省| 宜兴市| 崇仁县| 宿迁市| 舒兰市| 普陀区| 大洼县| 双辽市| 达拉特旗| 常州市| 麦盖提县| 昌宁县| 舞阳县| 莱西市| 阿克| 罗山县| 吉木萨尔县| 壤塘县| 广丰县| 景谷| 上饶市| 武功县| 普兰店市| 汨罗市| 怀仁县| 清流县| 江口县| 津南区| 灵石县| 肥乡县| 汉沽区| 琼中| 平遥县|