c语言 指针到字符串,C语言中的指针和字符串
前言
務必理解指針與內存模型,不要死記硬背。
內存里的字符串
C語言中的字符串一般是char *類型的,這是怎樣存在內存中的呢?
cchar *s = "NIHAO";
| s:400 |
|---|---|---|---|
|'N'|'I'|'H'|'A'|'O'| 0 |
|---|---|---|---|---|---|
|400|401|402|403|404|405|
如是上圖,假設字母A處于內存的第400號格子,那么后面幾個字母也是緊跟著的。
變量s本身并沒有儲存字符串,而存的是字符串的首地址400。也即,s指向這個字符串。
為什么沒有專門一個字符串的類型而是要靠一個指針指向它呢?因為字符串的長度是不固定的,所以一個字符串還包含著長度信息,基本類型是無法處理數據結構的。
我們都知道字符串是以0結尾的,而且這個更像是一種約定,C編譯器本身并沒有對此做任何保證。比如這樣
cchar s[3] = "asd";
puts(s); /* prints "asd" or something longer */
這樣做是危險的,因為s只有3個格子,字符串結尾的0并沒有放進去。如果在它后面的內存格子并不是0,那打印這個字符串時就跟我們預期的不一樣了。
字符串常量不可寫
c"abc"[0] = 'z'; /* wrong */
char *s = "abc";
s[0] = 'z'; /* wrong */
char s[5] = "abc";
s[0] = 'z' /* right */
當指針s指向的是字符串常量(即直接寫在程序里面的字符串時),要注意它是不可寫的
為啥用數組就沒問題呢,因為數組的初始化和指針有點區別
cchar s[5] = "abc";
/* 相當于 */
char s[5];
strcpy(s, "abc");
如果擔心自己會不小心寫錯,可以加上const關鍵字,這樣編譯的時候就會報錯
這是一個好習慣,接下來的示例程序中都會這么寫。
cconst char *s = "abc";
s[0] = 'z'; /* causes a compiling error instead of runtime error */
指針是要初始化才能使用的
c/* wrong */
char *s;
s[0];
上面的程序編譯是能過的(可能有warning),但運行是一定會出錯的,因為編譯器并不知道s指向哪些格子。
c/* right */
const char *s = "NIHAO";
s[0];
這樣,其實是隱式的分配了6個格子(包括字符串結尾的0),并讓s指向它們
c/* right */
char s[6];
s[0];
c/* right */
char s[6] = "NIHAO";
s[0];
數組其實跟指針沒什么區別,主要的區別是它在聲明的時候就分配好了格子(方括號里的6就是告訴編譯器給我6個格子),而且數組不能改變它的指向(也不能再要更多的格子)。
為什么不能用等號來比較字符串?
比較字符串
cconst char *s = "abcd";
const char *t = "abcd";
/* wrong */
if (s == t) {
...
}
/* right */
if (!strcmp(s, t)) {
...
}
因為s和t都沒有存字符串的內容,它們存的是字符串的地址,如果用==比較,比較的是兩個字符串的地址是否相同。我們希望比較的是內容是否相同。
請使用C語言庫函數中的strcmp比較字符串是否相等
復制字符串
c/* tries to copy a string */
char s[5] = "abcd";
char *t = s;
t[3] = 'z';
puts(s); /* puts "abcz" */
上面這種做法讓t和s指向同一字符串,修改t指向的內容,會發現s指向的內容也被修改了。這種做法沒有錯,經常會用到,但不一定是你想要的。
c/* wrong */
char *s = "abcd";
char *t; /* not initialized */
strcpy(t, s);
c/* right */
char *s = "abcd";
char t[10] = {0}; /* or char *t = (char *) malloc(5*sizeof(char)); */
strcpy(t, s);
使用strcpy復制字符串的內容而不是指針,但也要注意初始化t這個指針
怎樣讓函數得到一個字符串結果
int,float之類的很簡單直接return就好
但現在我想寫一個函數,它能夠得到一個字符串
三種錯誤的或者不太好的做法
c/* no problem, but meaningless */
const char *f()
{
const char *s = "abcd";
return s;
}
/* wrong */
char *f()
{
char s[100];
/* do something with s */
return s;
}
/* result correct but not good */
char *f()
{
int n = 10;
char *s = (char *) malloc(n*sizeof(char));
/* do something with s */
return s;
}
第一種情況就不說了,返回一個字符串常量并沒有問題因為它不可修改,但是不可修改也就沒什么意義了。
第二種情況是完全錯誤的,返回一個局部的數組。這個數組的內存會在函數調用完后被收回,因此返回的指針指向的時候沒有意義的地方。現代編譯器一般都會對這個有warning。
第三種情況是返回malloc的指針。這種情況你可以得到正確的答案,但是不推薦,調用這個函數的人很有可能
不知道函數里面分配過內存
不知道應該什么時候free這部分內存
忘了free這部分內存
一旦沒有注意,多次調用這個函數,結果就是內存溢出,這樣的錯誤還非常不好排查,所以不推薦
正確的做法
正確的做法是把分配內存這種事情放在函數外面做,正如strcpy一樣
cchar *strcpy(char *dest, const char *src)
{
int i;
for (i = 0; i < strlen(src); i++) {
dest[i] = src[i];
}
return dest;
}
dest是我們想要返回的字符串,它是從外面傳進來的原因是我們不想在函數內部為它分配內存,而是在外面分配好了,里面只對這個字符串進行修改。
注意這里返回了char *但其實返回的正是原本傳進來的dest,這里只是為了方便而已。
總結
以上是生活随笔為你收集整理的c语言 指针到字符串,C语言中的指针和字符串的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux 命令 抛后台,Linux 后
- 下一篇: c语言程序设计徐立辉答案,C语言程序设计