char* p 和 char p[] 的區別

        在最近的項目中頻繁的使用char* p 和 char p[ 10 ],總是默認的以爲他們相同,可是在用的過程中總是出現一些錯誤。在網上搜了下還是有許多的講解,就整理下添加自己的一下想法。

char* p = "HelloWorld";

"HelloWorld"的類型是 const char*,編譯器在編譯的過程中會在靜態數據區爲"HelloWorld"分配空間存儲這個字符串,然後將字符串的首地址賦值給指針 char* p。當使用指針p試圖改變"HelloWorld"時,如:p[0] = 'a',編譯器並不會出錯,但是在運行時會出錯。改成char p[] = "HelloWorld"就不會出錯了,char p[] = "HelloWorld"現在內存中開闢一連續存儲單元,然後將字符串存到存儲單元。


        函數中可以返回,已經申請內存空間的局部變量的指針:

char* str()
{
       char* p;
       p = (char*)malloc(10);
       return p;
}

 實例A:

#include <stdio.h>
char *returnStr()
{
    char *p="Hello world!";
    return p;
}
int main()
{
    char *str = NULL;//一定要初始化,好習慣
    str = returnStr();
    printf("%s \n", str);
   
    return 0;
}

這個沒有任何問題,因爲"HelloWorld"是字符竄常量,存放在靜態數據區。把該字符串存放在靜態數據區的首地址賦值給了指針。所以在returnStr()執行完成後,該字符串所在的內存不會被回收,通過指針能夠正常的訪問。

實例B:

#include <stdio.h>
char *returnStr()
{
    char p[] = "hello world!";
    return p;
}
int main()
{
    char *str = NULL;//一定要初始化,好習慣
    str = returnStr();
    printf("%s \n", str);
   
    return 0;
}

”HelloWorld"是字符串常量,但是把字符串常量賦值給局部變量(char p[] 型數組),該局部變量存放在棧中。這樣就有兩塊一樣的內存,也就是說 ”char p[] = "HelloWorld"; “ 這條語句讓”HelloWorld“這個字符串在內存中有兩份拷貝,一份在動態分配內存的棧中,另一份在靜態存儲區,這是與前者的區別。

       當returnStr()執行結束後,棧要被清空,局部變量的內存也被釋放。所以這時函數返回的是一個已被釋放內存地址,打印出來也就是亂碼。

如果真要返回一個局部變量的地址,那麼該局部變量一定要設爲static類型。

實例B-1:

#include <stdio.h>
char *returnStr()
{
    static char p[] = "Hello world!";
    return p;
}
int main()
{
    char *str = NULL;
    str = returnStr();
    printf("%s \n", str);
   
    return 0;
}


這個問題可以通過下面的一個例子來更好的說明:

#include <stdio.h>
//返回的是局部變量的地址,該地址位於動態數據區,棧裏

char *s1()
{
    char* p1 = "qqq";//爲了測試‘char p[]="Hello world!"’中的字符串在靜態存儲區是否也有一份拷貝
    char p[]="Hello world!";
    char* p2 = "w";//爲了測試‘char p[]="Hello world!"’中的字符串在靜態存儲區是否也有一份拷貝
    printf("in s1 p=%p\n", p);
    printf("in s1 p1=%p\n", p1);
    printf("in s1: string's address: %p\n", &("Hello world!"));
    printf("in s1 p2=%p\n", p2);
    return p;
}

//返回的是字符串常量的地址,該地址位於靜態數據區

char *s2()
{
    char *q="Hello world!";
    printf("in s2 q=%p\n", q);
    printf("in s2: string's address: %p\n", &("Hello world!"));
    return q;
}

//返回的是靜態局部變量的地址,該地址位於靜態數據區

char *s3()
{
    static char r[]="Hello world!";
    printf("in s3 r=%p\n", r);
    printf("in s3: string's address: %p\n", &("Hello world!"));
    return r;
}

int main()
{
    char *t1, *t2, *t3;
     t1=s1();
     t2=s2();
     t3=s3();
   
    printf("in main:");
    printf("p=%p, q=%p, r=%p\n", t1, t2, t3);

    printf("%s\n", t1);
    printf("%s\n", t2);
    printf("%s\n", t3);
   
    return 0;
}


運行輸出結果爲:

in s1 p=0013FF0C
in s1 p1=00431084
in s1: string's address: 00431074
in s1 p2=00431070
in s2 q=00431074
in s2: string's address: 00431074
in s3 r=00434DC0
in s3: string's address: 00431074
in main:p=0013FF0C, q=00431074, r=00434DC0
$
Hello world!
Hello world!

這個結果剛好解釋了上面,同時得出一個結論。

字符串常量之所以成爲常量,因爲他可以看做是一個沒有命名的字符串而且是常量,存放在靜態數據區。

這裏的靜態數據區,是相對堆和棧等動態數據區而言。

靜態數據區存放的是全局變量和靜態變量,所以字符串常量又可以稱之爲一個無名的靜態變量。

因爲”HelloWorld“這個字符串在函數s1()和s2()中都引用了,但在內存中只有一個拷貝,這與靜態變量性質相似。









發佈了56 篇原創文章 · 獲贊 12 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章