字符串并不是C語言中默認存在的類型,但是由于字符串的實用性,在譚浩強C語言中有對字符串的一些描述,但是不是特別的詳細。有時候寫代碼時都不會注意到一些小細節,本文就結合實驗分析一下字符串與字符數組之間的差別。
字符串與字符數組的區別在過去我一直是處于一知半解的狀態,字符串是通常被認為是常量,是保存在一段固定的內存中的,這段內存是以'\0'為結束符,這段內存通常只能通過一個指針來找到。字符數組其實和其他數組沒什么區別,只是保存的數據類型是字符類型(char),它沒有強制要求最后的元素是否是'\0'。字符數組的數組名是指向第0個字符的指針,而不是指向這個字符串的。這與我前期博客中對數組的分析結論是相同的,數組名并不像指針存在一個地址來保存指針名,數組名可以看做是匯編程序中的一個標號,并沒有專門的地址來保存數組名。
但是字符串和字符數組又有很多相似的地方,特別是當我們結合指針操作字符數組時候,就會導致錯誤的產生,我覺得只有搞清楚一些概念就能較好的避免這種錯誤的產生。這個概念就是指針就是指針,數組就是數組,兩個搞混只是因為某些巧合使我們誤以為指針和數組是等值的。兩者實質上是不相同的。
字符數組和字符串的定義如下:
1. //字符串
2. char *str = "string is a constant";
3. //字符數組
4. char strarray[] = {'s','t','r','i','n','g',' ','i','s',' ','a',' ','c','o','n','s','t','a','n','t','\0'};
5. //或者
6. char strarray[] = "string is a constant";
7. char strarray[] = {"string is a constant"};
其中從上面的定義可以知道字符串實質上一塊內存,其中保存的值是常量,常量其實就是不能采用程序對內存中的值進行修改,當然只是程序不能,我認為就像嵌入式C語言中的const類型一樣,雖然我們通常認為這個變量是常量,但實質上是只讀性變量,只是不能通過程序修改,還可以通過其他的方式修改。這時候str指向的內存空間中保存的數據是不能被程序修改的,也只能通過str指針對這段內存進行訪問。
如果字符數組定義成單個的字符,我們也許還能較好的分別,但如果是如第二種定義、第三種形式定義,我們可能就會產生很大的不理解,這與字符串的類型基本上沒有什么差別,只是多了一個數組符號[ ],但是這個數組符號就意味著數據類型的改變,我們可以知道數組中的值是可以改變的,不是常量,說明strarray是一個數組變量,而str卻是一個字符串常量指針。Strarray是一個標號,存儲器中并沒有專門分配內存空間存儲strarray的值。但是指針和數組名很多時候的一些相似性使得我們在處理的時候很難去判斷,比如下面這段代碼:
char * str = "Constant String";
char strarray[] = "Constant String";
int str_len,size_str,array_len,size_array;
str_len = strlen(str);
array_len = strlen(strarray);
size_str = sizeof(str);
size_array = sizeof(strarray);
size_str = sizeof(*str);
如果知道其中的一些數組與指針的一些區別可以較好的得到這些值,首先strlen(str)是指求解字符串的長度,但是不包含'\0',因此str_len就是字符串的長度,也就是str_len = 15,array_len也是求解字符串長度的值,我們前面已經指出數組和字符串存在差別,但為什么還可以這樣做呢。我們暫且認為包含'\0'的字符數組可以看做是字符串或者偽字符串,兩者非常的相似,都是存在一段內存,保存一段數據,格式也非常的相似,所以strlen認為兩者是相同的,但實質上還是存在差別的,但是采用strlen求解字符串長度時,array_len和str_len 是相同的。
size_str 和size_array這兩個變量的值是我寫這篇文章的初衷,因為我之前根本沒有起分析其中的一些差別,理所當然的認為兩者是相同的,但細細比較發現兩者確實存在問題。size_str = sizeof(str)的值是多少?sizeof是指對變量或者類型求解存儲空間,是C語言中不被重視的關鍵字,被大多數人認為是一個函數,實質上是一個關鍵字。這樣我們就能比較快速的確定后面三個變量的值啦,size_str = sizeof(str)是指對str的存儲器長度進行計算,str是一個字符型指針,我們知道,指針類型的變量實質上就是一個地址,在32機中,地址通常需要4個字節的內存空間來存儲,因此,size_str = 4;知道了sizeof的意義,size_array就比較簡單了,strarray是一個數組變量,是一段內存空間的標示,sizeof(strarray)是指上就是求解數組的內存大小,這時候的內存大小包含'\0',因此size_array的值應該是15+1=16。而接下來的size_str = sizeof(*str)就更加簡單了,因為我們知道對于字符串指針而言,指向的只是字符串的第0個元素,而不是整個字符串,這時候又會和數組的數組名有一定的交叉,數組的數組名我們通常被認為是一個常量指針(左值右值的差別),也就是指向的起始地址是不變的,但是通常被認為是指向數組首個元素的常指針,不能進行++,--操作。接著說明sizeof(*str),*str是指指向字符串的第0個元素的指針的值,也就是說*str是指上就是字符串的第0個元素,也就是一個字符類型,字符類型的大小也就是1字節了,因此這時候的size_str = 1。
上面的分析說明了在字符串和數組的相關操作中使用strlen的安全性總是比sizeof好,特別是不清楚sizeof的一些意義的情況下。
通過上面的分析我們應該字符串和字符數組還是存在很多差別的,但是只是理論上的分析,接下來采用程序進行測試。這個程序主要檢測字符串是否為常量,而字符數組是可以修改的。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define BUFS 20
int main()
{
char s[] = {"This is just a test!!"};
char d[] = {"Hello String strncpy!!"};
char *str = "String constant!";
char *str1 = "Testing!!";
/*不能采用定義指針的情況下就進行字符串的操作,
*需要分配一定長度的空間,
*因為沒有內存空間不能操作
*/
char *p = malloc(BUFS * sizeof(char));
/*修改字符數組*/
strncpy(p,s,strlen(s));
printf("p = %s\n",p);
printf("d = %s\n",d);
strncpy(d,s,strlen(s));
printf("d = %s\n",d);
strncpy(d,s,strlen(d));
printf("d = %s\n",d);
/*修改字符串*/
printf("Test the string constant\n");
printf("Constant String str = %s\n",str);
strncpy(str,d,strlen(str));
printf("p = %s\n",p);
printf("d = %s\n",d);
strncpy(d,s,strlen(s));
printf("d = %s\n",d);
strncpy(d,s,strlen(d));
printf("d = %s\n",d);
/*修改字符串*/
printf("Test the string constant\n");
printf("Constant String str = %s\n",str);
strncpy(str,d,strlen(str));
printf("Constant String str = %s\n",str);
return 0;
}
編譯以后出現的效果如下:
[gong@Gong-Computer c_languange]$ gcc -g strncpytest.c -o strncpytest
[gong@Gong-Computer c_languange]$ ./strncpytest
p = This is just a
d = Hello String
d = This is just a
d = This is just a
Test the string constant
Constant String str = String
Segmentation fault (core dumped)
結合上面的代碼可以知道關于字符數組的操作都順利的完成了,但是字符串的修改卻沒有正常的完成,出現了
異常(Segmentation fault (core dumped)),實質上就是因為字符串是不能修改的,導致了問題的拋出。據說在ANSI C中規定對字符串的操作會導致不確定的結果,這也說明了字符串是不能進行修改的。
通過上面的分析,數組和指針還是存在非常大的差別的,在字符串的操作過程中這種差別更加的明顯,因此在實際寫代碼的過程中要時時注意這些小的差別,只有銘記一些概念才能真正的寫好代碼。
總結:
字符串是一個常量,不應該被修改,修改會出現錯誤,字符數組可以進行修改,因為數組中的值并不是不變的。在關于字符串的操作中,最好是采用首先將字符串賦值給字符數組,然后通過指針的方式訪問進行修改等相關操作,而不能直接對字符串指針進行修改操作。
數組是一段內存的標示,指針只是保存了一個地址值。