周立功教授數(shù)年之心血之作《程序設(shè)計與數(shù)據(jù)結(jié)構(gòu)》
第一章為程序設(shè)計基礎(chǔ),本文為1.8.2 字符串常量。
1.8.2字符串常量
字符的真正價值在于你可以將它們串在一起形成一個字符序列,即字符串常量,簡稱字符串。字符串常量就是使用一對雙引號“""”包圍起來的,以空字符NUL(null character,NUL表示為'\0',ASCII碼值為0x00)結(jié)尾的連續(xù)的字符串,其長度為字符串的長度加1。既然使用空字符結(jié)束字符串,那么printf()和strcpy()都將這一點作為默認的前置條件。
注意,NULL和NUL是不同的,NULL表示特殊的指針,通常定義為((void *)0),而NUL是一個char,定義為\0,兩者不能混用。雖然字符常量是由單引號引起來的字符序列,通常由一個字符組成,但也可以包含多個字符,比如,轉(zhuǎn)義字符,在C中它們的類型是int:
printf("%d\n", sizeof(char));
printf("%d\n", sizeof('a'));
執(zhí)行上述代碼可以看到char的長度為1,而字符常量的長度為4。
只要在程序中使用字符串,就必須確定如何聲明保存字符串的變量。如果將它聲明為數(shù)組,則編譯時就已經(jīng)為各個字符保留了內(nèi)存空間;如果將它聲明為指針,則編譯時完全沒有為字符分配任何內(nèi)存,僅在運行時分配空間。比如:
char cStr[4] = "OK!";
char *pcStr = "OK!";
兩者的區(qū)別是,數(shù)組名cStr是常量,而指針名pcStr是變量。注意,如果在初始化指針之前就使用指針,有可能會導致運行出錯,如果有以下定義:
char *pcStr;
printf("%s", *pcStr);
由于這里沒有對pcStr初始化,因此其指向的內(nèi)存是未知的,將會打印出奇怪的字符,于是pcStr自然也就成為了野指針。
>>>1.字符串的引用
由于"OK!"是一個字符串常量,因此是不可修改的。如果試圖執(zhí)行以下操作:
pcStr[2] = 'Z';
雖然編譯期可以通過,但在運行時會出錯。如果以下面這樣的形式賦值:
char cStr[4];
cStr = "OK!";
則是非法的,因為數(shù)組變量名cStr是一個不可修改的常量指針。
如果字符數(shù)組中沒有保存'\0'',它僅僅是字符常量'O'、'K'、'!',不是字符串。即:
char cStr[] = { 'O', 'K', '!'};
而“char cStr[] = "OK!";”只不過是“char cStr[] = {'O', 'K', '!', '\0'};”的另一種寫法,因為字符串是一種特殊的字符數(shù)組變量,所以其存儲方式與數(shù)組變量一致。其中的cStr為數(shù)組變量名,表示此數(shù)組第0個元素的地址(即&cStr[0]),cStr+1表示數(shù)組第1個元素的地址(即&cStr[1]),cStr+2表示數(shù)組變量第2個元素的地址(即&cStr[2]),cStr+3表示數(shù)組變量第3個元素的地址(即&cStr[3]),其存儲形式詳見圖1.13。
圖1.13 “OK!”的存儲形式
C語言中的字符串是以字符數(shù)組變量的形式處理的,具有數(shù)組的屬性,所以不能賦值給整個字符數(shù)組變量,只能將字符逐個賦給字符數(shù)組變量。比如:
char cStr[4];
cStr[0] = 'O'; cStr[1] = 'K'; cStr[2] = '!'; cStr[3] = '\0';
其存儲的不是字符本身,而是以ASCII碼存儲的字符常量(即存值)。
由于字符串常量以'\0'(ASCII碼值為0x00)結(jié)尾,因此可以用cStr[i]作為for循環(huán)語句的“條件部分(布爾表達式)”,檢查cStr[i]是否為'\0'(cStr[i]是以*(cStr+i)形式表示的)。用于處理字符串中每一個字符的慣用法如下:
for(i = 0; cStr[i] != '\0'; i++) …
其等價于
for(i = 0; cStr[i]; i++) …
同理“while(cStr[i] != '\0')”與“while(cStr[i])”是等價的。
當然,也可以使用scanf()函數(shù)的%s格式聲明符輸入字符串,詳見程序清單 1.37。
程序清單1.37字符串的輸入與輸出范例程序
1 #include
2 int main(int argc, char *argv[])
3 {
4 char cStr[10];
5
6 scanf("%s", cStr);
7 printf("%s", cStr);
8 return 0
9 }
由于cStr代表字符數(shù)組的起始地址,因此不需要在cStr前添加&運算符。但采用%s格式符輸入字符串存在一種潛在危險,如果輸入的字符串太長,超出了字符數(shù)組的存儲極限,則程序執(zhí)行錯誤,因此可以使用“字段寬度”來限制輸入字符串的長度更安全。
由于字符串常量的類型是char的數(shù)組,則在表達式中被解讀為指針。即不管字符串有多長,pcStr始終存儲字符串第一個字符的地址,因此使用指向字符串的指針變量即可整體引用一個字符串。比如:
char *pcStr = "OK!";
其中的pcStr是字符指針變量,其等效于
static const char t376[] = "OK!";
char *pcStr = t376;
其中的t376是編譯器分配的一個內(nèi)部變量名,不同編譯器、不同程序、甚至同一個源代碼每次編譯,其名字均可能不同。顯然,程序員不知道這個數(shù)組的名字,即匿名數(shù)組變量。顯而易見,初始化字符數(shù)組存儲字符串和初始化指針指向字符串的區(qū)別在于,數(shù)組名是常量,而指針名是變量,因此字符串的絕大多數(shù)操作都是通過指針完成的。
由此可見,"OK!"就是“char的數(shù)組”,通過sizeof("OK!")也可以證明字符串的本質(zhì)還是數(shù)組,即可用"OK!"作為數(shù)組變量名,詳見程序清單 1.38。
程序清單1.38用字符串作為數(shù)組變量名范例程序
1 #include
2 int main(int argc, char argv *[])
3 {
4 printf("OK!占用的空間%d", sizeof("OK!")); // 輸出"OK!"占用的空間,即4個字節(jié)
5 printf("OK!的地址%x\n", "OK!");// 輸出"OK!"的地址
6 printf("%c\n", *("OK!" + 1)); //輸出"OK!"的第1個元素,即'K'
7 printf("%c\n", "OK!"[0]); //輸出"OK!"的第0個元素,即'O'
8 printf("%d\n", "OK!"[3]); //輸出"OK!"第3個元素的值,即'\0'
9 return 0;
10 }
由于C語言允許對指針添加下標,因此程序清單 1.38(6~8)分別輸出對應(yīng)的元素。顯然,可以利用這種方式將0~15轉(zhuǎn)換為等價的16進制的字符,詳見程序清單 1.39。
程序清單1.39 digit_to_charhex()轉(zhuǎn)換函數(shù)范例程序
1 char digit_to_hexchar(int digit)
2 {
3 return "0123456789ABCDEF"[digit];
4 }
當pcStr指向字符串"OK!"的首地址時,*pcStr表示該地址空間上的值為'O',即pcStr[0]= 'O',pcStr[1]= 'K',pcStr[2]= '!',pcStr[3]= '\0',或*pcStr='O',*(pcStr+1)='K',*(pcStr+2)='!',*(pcStr+3)= '\0'。
-
字符串
+關(guān)注
關(guān)注
1文章
584瀏覽量
20552
原文標題:周立功:字符真正價值在于形成字符序列——字符串的引用
文章出處:【微信號:ZLG_zhiyuan,微信公眾號:ZLG致遠電子】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論