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