務必理解指標與記憶體模型,不要死記硬背。
c語言中的字串一般是char *
型別的,這是怎樣存在記憶體中的呢?
如是上圖,假設字母a處於記憶體的第400號格仔,那麼後面幾個字母也是緊跟著的。c
char *s = "nihao";
| s:400 |
|---|---|---|---|
|'n'|'i'|'h'|'a'|'o'| 0 |
|---|---|---|---|---|---|
|400|401|402|403|404|405|
變數s本身並沒有儲存字串,而存的是字串的首位址400。也即,s指向這個字串。
為什麼沒有專門乙個字串的型別而是要靠乙個指標指向它呢?因為字串的長度是不固定的,所以乙個字串還包含著長度資訊,基本型別是無法處理資料結構的。
我們都知道字串是以0結尾的,而且這個更像是一種約定,c編譯器本身並沒有對此做任何保證。比如這樣
這樣做是危險的,因為s只有3個格仔,字串結尾的0並沒有放進去。如果在它後面的記憶體格仔並不是0,那這個字串就跟我們預期的不一樣了。c
char s[3] = "asd";
puts(s); /* prints "asd" or something longer */
"abc"[0] = 'z'; /* wrong */
char *s = "abc";
s[0] = 'z'; /* wrong */
char s[5] = "abc";
s[0] = 'z' /* right */
當指標s指向的是字串常量(即直接寫在程式裡面的字串時),要注意它是不可寫的
為啥用陣列就沒問題呢,因為陣列的初始化和指標有點區別
char s[5] = "abc";
/* 相當於 */
char s[5];
strcpy(s, "abc");
如果擔心自己會不小心寫錯,可以加上const
關鍵字,這樣編譯的時候就會報錯
這是乙個好習慣,接下來的示例程式中都會這麼寫。
const char *s = "abc";
s[0] = 'z'; /* causes a compiling error instead of runtime error */
/* wrong */
char *s;
char c = s[0];
上面的程式編譯是能過的(可能有warning),但執行是一定會出錯的,因為編譯器並不知道s指向哪些格仔。
/* right */
const char *s = "nihao";
char c = s[0];
這樣,其實是隱式的分配了6個格仔(包括字串結尾的0),並讓s指向它們
/* right */
char s[6];
char c = s[0];
/* right */
char s[6] = "nihao";
char c = s[0];
陣列其實跟指標沒什麼區別,主要的區別是它在宣告的時候就分配好了格仔(方括號裡的6就是告訴編譯器給我6個格仔),而且陣列不能改變它的指向(也不能再要更多的格仔)。
const char *s = "abcd";
const char *t = "abcd";
/* wrong */
if (s == t)
/* right */
if (!strcmp(s, t))
因為s和t都沒有存字串的內容,它們存的是字串的位址,如果用==
比較,比較的是兩個字串的位址是否相同。我們希望比較的是內容是否相同。
請使用c語言庫函式中的strcmp
比較字串是否相等
/* tries to copy a string */
char s[5] = "abcd";
char *t = s;
t[3] = 'z';
puts(s); /* puts "abcz" */
上面這種做法讓t和s指向同一字串,修改t指向的內容,會發現s指向的內容也被修改了。這種做法沒有錯,經常會用到,但不一定是你想要的。
/* wrong */
char *s = "abcd";
char *t; /* not initialized */
strcpy(t, s);
/* right */
char *s = "abcd";
char t[10] = ; /* or char *t = (char *) malloc(5*sizeof(char)); */
strcpy(t, s);
使用strcpy
複製字串的內容而不是指標,但也要注意初始化t這個指標
int,float之類的很簡單直接return就好
但現在我想寫乙個函式,它能夠得到乙個字串
/* no problem, but meaningless */
const char *f()
/* wrong */
char *f()
/* result correct but not good */
char *f()
第一種情況就不說了,返回乙個字串常量並沒有問題因為它不可修改,但是不可修改也就沒什麼意義了。
第二種情況是完全錯誤的,返回乙個區域性的陣列。這個陣列的記憶體會在函式呼叫完後被收回,因此返回的指標指向的時候沒有意義的地方。現代編譯器一般都會對這個有warning。
第三種情況是返回malloc的指標。這種情況你可以得到正確的答案,但是不推薦,呼叫這個函式的人很有可能
一旦沒有注意,多次呼叫這個函式,結果就是記憶體溢位,這樣的錯誤還非常不好排查,所以不推薦
正確的做法是把分配記憶體這種事情放在函式外面做,正如strcpy
一樣
char *strcpy(char *dest, const char *src)
return dest;
}
注意這裡返回了char *
但其實返回的正是原本傳進來的dest
,這裡只是為了方便而已。
呼叫方法在上面也提到過。
在c語言中 C語言中的指標和字串
之前我們說過關於字元的一些內容,字元的原理,歸根到底就是將我們常用的一些字元根據編號整理成一張統一的表,每個字元對應著這個表裡面的乙個數值,這一過程就是字元的編碼。然而大多數時候我們根本就不會使用單個字元,比如計算機上面要輸出一句句子 hello world 這個句子裡面就是由多個字元所組成的,分別...
C語言中字元陣列和字串指標分析
寫下面的測試程式 include int main int argc,char argv 執行後螢幕上得到如下結果 其實看到結果估計很多東西就好明白了,先看看前三個輸出也就是關於變數day的,在 char day 15 abcdefghijklmn 這個語句執行的時候,系統就分配了一段長15的記憶體...
C語言中字元陣列和字串指標分析
這幾天搞unix上的c程式,裡面用到了很多字元陣列和字串指標,我記得在學完c語言後相當一段時間裡,對指標這個東西還是模模糊糊,後來工作也沒怎麼用到過c,雖然網上這類的文章也有很多,還是決定自己在這做個小總結,也算加深下自己的印象,寫了下面的測試程式 01 include 02 03intmain i...