問題1:
字符數組名可以作為左值嗎?當然不行
比如
char str[20] = {'h','e','l','l','o',' ','w','o','r','l','d'};
str++;
不可以這么干,因為字符數組名是一個常量指針,也就是是一個const char*
#include <stdio.h> int main() { char str[20] = {'h','e','l','l','o',' ','w','o','r','l','d'}; printf("sizeof(str): %d\n",sizeof(str)); printf("str: %s\n",str); str = str + 1; //error return 0; }
運行結果如下:
當數組名為左值時,它的類型是字符數組;當數組名為右值時,它的數據類型是字符指針。
#include <stdio.h> #include <string.h> int main(void) { char buf[10]; char *p= “afdal”; buf = p; //將一個char* 賦給一個char[10],類型不一樣,必然不成功 printf(“the buf is:%s\n”,buf); return 0; }
問題2:
字符數組如何進行初始化?
#include <stdio.h> int main() { char ptr[20] = "hello world"; //success char str[20] = {'h','e','l','l','o',' ','w','o','r','l','d'}; //success char ctr[20]; ctr = "hello world"; // error: incompatible types when assigning to type ‘char[20]’ from type ‘char *’ return 0; }
在給字符數組初始化的時候,會自動在字符數組的結尾加上'\0'
#include <stdio.h> int main() { char str[20] = {'h','e','l','l','o',' ','w','o','r','l','d'}; printf("sizeof(str): %d\n",sizeof(str)); printf("str: %s\n",str); int i = 0; while(*(str + i) != '\0') //判斷是否加上'\0' { printf("%c\n",*( str + i++)); } return 0; }
運行結果如下:
問題3:
字符數組越界訪問能編譯通過嗎?
字符數組越界訪問編譯可以通過,沒有報錯,這樣會出現很多的問題
#include <stdio.h> int main() { char str[12] = {'h','e','l','l','o',' ','w','o','r','l','d'}; printf("%c\n",str[100]); return 0; }
打印為空
問題4:
字符數組和字符指針又有什么區別呢?
首先在內存的中位置不同,字符數組保存的字符串存放在內存的棧中,而字符指針指向的字符串保存在內存的靜態存儲區中。
其次字符數組保存的字符串屬於字符串變量,可以被修改,而字符指針指向的字符串是屬於字符串常量,不能被修改。
#include <stdio.h> int main() { char str[12] = {'h','e','l','l','o',' ','w','o','r','l','d'}; char* ptr = "hello world"; str[0] = 'f'; ptr[0] = 'f'; //將字符指針指向的字符串修改,將出現段錯誤,因為該內存地址只讀,因為該字符串屬於常量 return 0; }
運行結果:
段錯誤是指訪問的內存超出了系統給這個程序所設定的內存空間,例如訪問了不存在的內存地址、訪問了系統保護的內存地址、訪問了只讀的內存地址等等情況。
#include <stdio.h> int main() { char* ptr = "hello world"; ptr[11] = '!'; //往常量字符串末尾添加字符,相當於連接兩個字符串,出錯 ptr[12] = '\0'; return 0; }
這樣也會出現段錯誤。
問題5:
字符指針是不是和字符數組名都是指針常量呢?
不是,字符指針,可以改變它的指向,也就是可以為左值。可以將一個字符指針指向一個字符串常量后又指向另外一個字符串常量。字符指針在為初始化之前,他是一個未定義的值,將指向任何可能的地方,所以在使用字符指針時一定要注意初始化。
#include <stdio.h> int main() { char* ptr; printf("ptr: %c\n",*ptr); return 0; }
運行結果:
編譯可以通過,但是ptr指向的內存地址是任意的
當然也可以將一個字符指針指向一個字符數組。
#include <stdio.h> int main() { char str[12] = {'h','e','l','l','o',' ','w','o','r','l','d'}; char* ptr = str; printf("ptr: %s\n",ptr); return 0; }
運行結果:
問題6:
如果一個字符指針指向一個堆中動態內存。那么我們如何初始化該內存中的值?
#include <stdio.h> #include <stdlib.h> int main() { char* str = (char*)malloc(sizeof(char)*20); printf("str:%p\n",str); //str在堆中內存首地址 str = "hello world"; printf("str:%p\n",str); //str在靜態存儲區內存首地址 char* ptr = "I love you"; printf("ptr:%p\n",ptr); //str在靜態存儲區內存首地址 return 0; }
運行結果如下:
很明顯前后的地址不一樣,前一個指向堆中的內存的地址,而后一個指向了靜態存儲區中的內存的地址。我本以為我通過上述方式進行初始化str的時候,我可以將堆中內存進行初始化,我發現我錯了,字符指針將被重新指向。但是我想如果我們將str聲明為const呢?那將會出現什么樣的結果呢?
#include <stdio.h> #include <stdlib.h> int main() { const char* str = (char*)malloc(sizeof(char)*20); //我以為const將修飾str,使得str不能再作為左值而出現,我錯了,const修飾的是由str指向的字符串, printf("str:%p\n",str); //該字符串不能再被修改 str = "hello world"; printf("str:%p\n",str); return 0; }
運行結果:
如果我們將上述的代碼做如下修改,程序還能編譯通過嗎?
#include <stdio.h> #include <stdlib.h> int main() { const char* str = (char*)malloc(sizeof(char)*20); printf("str:%p\n",str); str[0] = 'h'; //這種賦值才是對str原來所指向的堆中的內存的賦值,編譯不過,因為str指向的堆中的內存是只讀的。 str = "hello world"; printf("str:%p\n",str); return 0; }
運行結果如下:
上述就引發了我思考為什么const修飾的是str指向的字符串,而不是str本身呢?
原來,char* const str才是真正的修飾str本身,使得str為只讀的,不能再被修改,也就是重新指向其他內存地址。而const char* str 和 char const* str 的意義是一致的,都是修飾的是*str。
#include <stdio.h> #include <stdlib.h> int main() { char* const str = (char*)malloc(sizeof(char)*20); printf("str:%p\n",str); str[0] = 'h'; str = "hello world"; //變量str被重新指向,error,編譯不過 printf("str:%p\n",str); return 0; }
運行結果如下:
那么我們又該如何初始化一個字符指針指向的內存呢?
其實我們可以很清楚的分析,如果str是一個字符指針,也就是一個變量,變量是可以被重新賦值的,而每一個字符串本身是有一個地址的,str = “hello world”,必然就是改變了str的值,使得str保存着"hello world"的內存首地址,而一旦你將str = "I love you",必然str將保存這"I love you"的內存首地址,這都是毋庸置疑的。
#include <stdio.h> int main() { printf("hello world:%p\n","hello world"); //將打印出"hello world"的內存地址
printf("I love you:%p\n","I love you"); //將打印出"I love you"的內存地址 return 0; }
運行如下:
下面我得回到這樣一個問題?
str 和 *str的區別,很明顯,str是一個指針,而*str是str指向的內存的存放的值,既然我們想改變str指向內存中的值,既*str的值,那么為何不將*str作為左值,來初始化str所指向內存的值呢?而*str 不就是 str[0]嗎?所以很明顯了。上述問題:那么我們又該如何初始化一個字符指針指向的內存呢?當然這個僅限於字符指針指向的是由mlloc分配的堆中的內存以及字符數組指向的棧中的內存。而字符指針如果指向字符常量,不可修改字符指針指向的內存的值,因為字符常量是只讀的。
#include <stdio.h> #include <stdlib.h> int main() { char* const str = (char*)malloc(sizeof(char)*20); printf("str:%p\n",str); str[0] = 'A'; //數組下標型賦值 *(str + 1) = 'l'; //指針++型賦值 str[2] = 'e', *(str + 3) = 'x'; printf("str:%s\n",str); printf("str:%p\n",str); return 0; }
運行如下:
#include <stdio.h> #include <stdlib.h> int main() { char str[20]; printf("str:%p\n",str); str[0] = 'A'; *(str + 1) = 'l'; str[2] = 'e', *(str + 3) = 'x'; //str[4] = '\0'; //當我們沒有手動給字符串結尾附上'\0' printf("str:%s\n",str); printf("str:%p\n",str); return 0; }
運行如下:
上述的運行結果說明上述程序並沒有自動給字符串結尾附上'\0',對於字符數組除非這樣賦值,str[20] = "hello world",不然你就得手動給字符串附上字符串結尾'\0'。
#include <stdio.h> #include <stdlib.h> int main() { char str[20]; printf("str:%p\n",str); str[0] = 'A'; *(str + 1) = 'l'; str[2] = 'e', *(str + 3) = 'x'; str[4] = '\0'; printf("str:%s\n",str); printf("str:%p\n",str); return 0; } ~
運行結果:
附上一段我關於memcpy()和memmove()的代碼的實現。
#include <stdio.h> #include <stdlib.h> void mem_copy(char* const dest, const char* const src) { for(int i = 0; *(src + i) != '\0'; i++) *(dest + i) = *(src + i); } void mem_move(char* const dest, const char* const src) { int i = 0; while(*(src + i) != '\0') i++; for(; i != -1; i--) *(dest + i) = *(src + i); } int main() { char* src = "hello world"; char* const dest1 = (char*)malloc(sizeof(char) * 20); char dest2[20]; mem_copy(dest1,src); mem_move(dest2,src); printf("dest1:%s\n",dest1); printf("dest2:%s\n",dest2); return 0; }
當我們需要將內存的中的某一地址段的內容拷貝到內存中另一地址段中,以上是將字符串常量區的內容拷貝到堆中和棧中,我們可以看到我分別使用了malloc和字符數組,當然我們得考慮可能在拷貝的過程中會有地址段重疊的問題,重疊該怎么拷貝,解決方案很簡單,就是從尾向前拷貝,即可。
運行結果如下:
以上是我對c語言中字符串操作的一些理解,有什么問題,請大家幫忙指出~~thanks