char*的奧秘

這篇文主要想要強調的就是,char*作為一個指標,紀錄的是記憶體位址,若指向的記憶體地址沒有被分配,是不可以直接用strcpy的

char[]其實就是一個數組,雖然看似跟char*一樣,但兩者的邏輯不同

char[]

char a[] = "KKK" 其實等同於char a[] = {'K','K','K','\0'}
如果用vs code滑上去看,會發現它其實就是char a[4]

這類型的變數在宣告時就必須給定字元,且後續長度不可修改。

即,不能用char a[] = "KKK"; a = "KKKKK";

這時候它的操作等同於char[n],要改變其中任意「字元」都是可行的(此變數已經作為array被分配在stack中)
但當然如果把最後的’\0’換成其他字元,printf("%s")會印出奇怪的東西

至於char* argv[],同理其實只是等同於char* argv[n] = {'string1','string2',...}而已

要注意的是不可以直接把整個array改成其他字串,在C語言規範中arrays並非modifiable l-values

如果要修改這類的char陣列,須使用strcpy (用malloc/new出來的char array同理)

這類char陣列的size是根據字元數變動的

char*

顧名思義,他其實是字元指標,指向某個字串常數的指標。

C/C++的常數字串儲存方法是為這些字串開一個空間存放他們,當宣告

1
char* A = "hey";

時,其實就是為hey這個字串分配一個空的記憶體位置,並且將A指向這個地址。

也就是說,如果哪天”hey”的值變動了,A的內容也會跟著變動! 同時char*可以指向任意大小的char[],但只要char[]內容更動,char*也會受到影響

因為這個特性,char*不同於char[],他是可以在後天改值的,只要改變他指的地址即可。

  • char*就是浮萍,不像char[]、malloc()出來的char*一樣攜家帶眷

不過,也因為他是單單一個指向記憶體的指標,我們不能透過strcpy等函數塞整個char array給他,他的空間尚未被初始化,如果直接用strcpy很可能導致SEG Falut

而且因為類別正確(strcpy的確是接受char*作為參數),所以編譯器並不會警告,新手在看文件時也會看到輸入參數是char*就傻傻地把char*丟進去了

最後,char*如果沒有被初始化(指向某個常數字串)而直接印出,也會SEG fault

  • 這類char指標的size是根據作業系統的位元數變動的

char *[]

根據C/C++宣告閱讀法,以下面的變數為範例

1
char* hard[3];

我們可以得到他是「hard is size-3 array of pointers to char 」,也就是3個char*。因此我們我們可以套用char*的作法,hard這個變數是不能被strcpy的(不會有任何警告但會SAG fault)

額外補充

另一個常見的SEG fault的原因就是生命週期

考慮以下code

1
2
3
4
5
6
7
8
9
10
void foo(char* in){
char ans[] = "laugh";
in = ans;
printf("\nin funct, aram = %s\n",in);
}

int main(int argc, char* argv[]){
char* aram;
foo(aram);
printf("\nout funct, aram = %s\n",aram);

我們應該會期望得到輸出

1
2
in funct, aram = laugh
out funct, aram = laugh

但結果卻是SEG fault

因為ans作為foo函數的區域變數,在退出函數時就已經消滅了,當aram變數還指向他的時候,其實會出現未定義行為

所以如果想要讓aram保存ans的結果,最好的做法就是用malloc的,如下code

1
2
3
4
5
6
7
8
9
10
11
12
void foo(char* in){
char ans[] = "laugh";
   strcpy(in,ans); //注意要改成strcpy!
  // in = ans;
printf("\nin funct, aram = %s\n",in);
}

int main(int argc, char* argv[]){
char* aram;
   aram = (char*) malloc(sizeof(char)*16); //讓aram變成malloc變數,可以「攜家帶眷」
   foo(aram);
printf("\nout funct, aram = %s\n",aram);

Note: 如果aram在丟入foo以後才malloc,仍會因為命名問題消滅造成SEG fault!!

如果想要在函數內進行malloc,則需要傳入aram的地址,如下code

1
2
3
4
5
6
7
8
9
10
11
void foo(char** in){
char ans[] = "laugh";
*in = (char*) malloc(sizeof(char)*16);
strcpy(*in,ans);
printf("\nin funct, aram = %s\n",*in);
}

int main(int argc, char* argv[]){
char* aram;
   foo(&aram); //改這裡
   printf("\nout funct, aram = %s\n",aram);