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