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 | void foo(char* in){ |
我們應該會期望得到輸出
1 | in funct, aram = laugh |
但結果卻是SEG fault
因為ans作為foo函數的區域變數,在退出函數時就已經消滅了,當aram變數還指向他的時候,其實會出現未定義行為
所以如果想要讓aram保存ans的結果,最好的做法就是用malloc的,如下code
1 | void foo(char* in){ |
Note: 如果aram在丟入foo以後才malloc,仍會因為命名問題消滅造成SEG fault!!
如果想要在函數內進行malloc,則需要傳入aram的地址,如下code
1 | void foo(char** in){ |