理解c-風(fēng)格字符串

總結(jié)

C語言沒有原生的字符串類型!
C-風(fēng)格字符串就是最后一位為'\0'的字符數(shù)組!
C語言通過字符指針來管理字符串!
指向字符串的指針和字符串本身是分開的兩個(gè)東西
存儲(chǔ)多個(gè)字符的兩種方式:申請(qǐng)連續(xù)字符空間和字符數(shù)組
這個(gè)鏈接,對(duì)c-風(fēng)格字符串的理解也是很到位的,輔助理解很重要!
http://www.itdecent.cn/p/4db7a5eedc42?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

課本理解,C++ primer plus 中文版第五版,P225
首先從C語言開始說

1.什么是人理解的字符串?

program就是字符串,沒有單引號(hào),沒有雙引號(hào),沒有\(zhòng)0,任何連續(xù)的字符都認(rèn)為是字符串(字符串在一起就是字符串),包括寫的這句話,中英文夾雜,帶標(biāo)點(diǎn)符號(hào),這在人看來也可以認(rèn)為是字符串。

計(jì)算機(jī)沒法理解人的思維,到底啥是字符串,就得給字符串立個(gè)規(guī)定,規(guī)定啥是字符串,讓計(jì)算機(jī)一看,噢,這個(gè)東西就是個(gè)字符串!規(guī)定!規(guī)定!規(guī)定!

怎么立規(guī)定就要理解計(jì)算機(jī)中的數(shù)據(jù)類型。

類型是計(jì)算機(jī)存儲(chǔ)數(shù)據(jù)的方式,讓計(jì)算機(jī)知道,這個(gè)變量是int類型,那個(gè)變量是double類型。

首先明確一點(diǎn),字符串不是一種類型,字符串是一種概念,概念的實(shí)現(xiàn)才是類型。
比如整型是概念,不是類型,int是類型,unsigned int是類型!

可以通過人為創(chuàng)建某個(gè)數(shù)據(jù)類型,讓這個(gè)數(shù)據(jù)類型來表示字符串,計(jì)算機(jī)就理解了什么是字符串。

問題又來了,怎么創(chuàng)建這種類型來存儲(chǔ)字符串,方便后續(xù)對(duì)字符串的操作?

C語言沒有創(chuàng)建一個(gè)新的類型符來表示字符串,而是通過char類型的數(shù)組來存儲(chǔ)字符串,暫且叫他char數(shù)組類型,這里強(qiáng)調(diào)下,char數(shù)組類型不是字符串的一種類型,只是字符串依靠char數(shù)組來存儲(chǔ)。

前面提到了,字符串是概念,概念的實(shí)現(xiàn)是類型,而類型表示的是存儲(chǔ)方法,那么表示字符串的char數(shù)組這種類型是如何存儲(chǔ)字符串呢?

答案是把字符串拆分成一個(gè)個(gè)字符,依次放到數(shù)組里面,注意這里強(qiáng)調(diào)下,字符串是依靠字符數(shù)組,char[]來存儲(chǔ)的,不代表字符數(shù)組就是字符串!
char str[8] = { 'p', 'r', 'o', 'g', 'r', 'a', 'm'};

這里也解釋下,為啥會(huì)有單引號(hào)和雙引號(hào),單引號(hào)圈住了p,表示p是個(gè)字符常量,p是個(gè)值。

到這里,我們就明白了,c語言是如何在計(jì)算機(jī)中存儲(chǔ)一個(gè)人類認(rèn)知中的字符串。通過字符數(shù)組來依次存放每個(gè)字符。這里強(qiáng)調(diào)了人類認(rèn)知的字符串,因?yàn)樗c計(jì)算機(jī)認(rèn)識(shí)的字符串是不同的。

2.接下來介紹什么是編譯器眼中的字符串

編譯器規(guī)定了,字符串和字符數(shù)組的區(qū)別:最后一位是否是空字符。最后一個(gè)字符為'\0'的字符序列就是字符串,也就是所謂的c-風(fēng)格字符串。

這里是字符序列,不是字符數(shù)組。比如剛才提到的char str[8] = { 'p', 'r', 'o', 'g', 'r', 'a', 'm'};不是c-風(fēng)格字符串,而char str[8] = { 'p', 'r', 'o', 'g', 'r', 'a', 'm', '\0' };是c-風(fēng)格字符串,序列最后以'\0'結(jié)尾。

如何把人類眼中的字符串賦值給代碼中的變量,讓編譯器知道變量是個(gè)字符串,而不是字符數(shù)組

這里強(qiáng)調(diào)是如何完成賦值!

賦值操作需要4個(gè)東西,0.變量的類型,1.左邊的變量名,2.=號(hào),3.右邊的值,重點(diǎn)是右邊的值

3.人得告訴計(jì)算機(jī),我輸?shù)倪@個(gè)值是什么類型,比如1,它既可以是整型,也可以是字符類型。問題來了,怎么告訴,這個(gè)值是什么類型。

這是個(gè)有趣的問題

比如,可以這么寫,int ch='a';這肯定是對(duì)的,'a'是個(gè)字符此時(shí)ch的值打印出來是97,這是因?yàn)锳SCII碼上的字符有對(duì)應(yīng)的int值,這里屬于映射。想說明的問題是,賦值這個(gè)操作得到的結(jié)果是否是我們想要的取決于2個(gè)因素,1.變量的類型,2.值的類型。這里就是值為char類型,變量為int類型,進(jìn)行了類型轉(zhuǎn)換,通過ASCII映射表,找到了char類型的值對(duì)應(yīng)的int類型的值。再強(qiáng)調(diào)下變量的類型與值的類型影響賦值過程。

回到剛才的問題,人得告訴計(jì)算機(jī),我輸?shù)倪@個(gè)值是什么類型。

變量設(shè)為int類型,然后進(jìn)行賦值,那么編譯器很自然知道,2是個(gè)數(shù)字,不是個(gè)字符
int a; // a是整型
a=2; // 2是個(gè)整型數(shù)字

很自然,設(shè)定一個(gè)屬于字符串概念的某種類型變量的語句也應(yīng)該類型,如下
[實(shí)現(xiàn)字符串的類型名稱] a;
a=字符串;

如何把字符串program賦值給a,又回到了剛才的問題,人得告訴計(jì)算機(jī),我輸?shù)倪@個(gè)值是什么類型

字符的值是通過單引號(hào)告訴編譯器,這個(gè)值是字符類型,那么字符串的值怎么告訴編譯器,這個(gè)值屬于c-風(fēng)格字符串。

這里注意,我強(qiáng)調(diào)的是C-風(fēng)格字符串,不是c-風(fēng)格字符串類型或字符串類型!,C語言沒有字符串類型,只有C-風(fēng)格的字符串。

規(guī)定:用單引號(hào)表示單個(gè)字符,雙引號(hào)表示人類認(rèn)知中的字符串,單雙引號(hào)兩者不可混用,這就是字符串的值。

使用單引號(hào)賦值就是前面的數(shù)組賦值的方法,只不過最后一個(gè)字符要求是'\0'
char str[8] = { 'p', 'r', 'o', 'g', 'r', 'a', 'm', '\0' };或者char str[] = { 'p', 'r', 'o', 'g', 'r', 'a', 'm', '\0' };

使用雙引號(hào)賦值有兩種語法
初始化方法1:char* str= "program";或者const char* str= "program"
初始化方法2: char str[] = "program";或者char str[8] = "program";本質(zhì)是一樣的

再次強(qiáng)調(diào)下,1和2的存儲(chǔ)結(jié)構(gòu)是一樣的,都是依靠字符數(shù)組進(jìn)行存儲(chǔ)的,其實(shí)到這里就回答了問題,c語言是如何表示字符串的:

答案:通過指針,指向字符數(shù)組。

4.編譯器認(rèn)為字符串是什么類型

C語言中的字符串語句
char * p="hello world";
右值"hello world"的類型不叫字符串常量類型,c語言沒有字符串常量類型,有很多種叫法,字符串直接量,字符串字面值等。

字符串字面值是一串常量字符,用雙引號(hào)括起來的零個(gè)或多個(gè)字符表示字符串字面值

使用typeid("hello").name()來看其類型,返回的是A6_c,表示char 型 Array(數(shù)組),數(shù)組有6個(gè)元素,實(shí)際是個(gè)char * 的指針類型

5.如何理解字符串初始化在內(nèi)存層次上的執(zhí)行順序是什么?

1.char* str = "program",在rom中申請(qǐng)(類似malloc)一個(gè)連續(xù)的內(nèi)存空間,大小為7+1個(gè)字節(jié),7字節(jié)存儲(chǔ)字符數(shù)組program,編譯器自動(dòng)在字符數(shù)組后面添加了一個(gè)'\0'字符,存儲(chǔ)在1字節(jié)上,于是便把"program"轉(zhuǎn)換成c-風(fēng)格字符串;接著,成功申請(qǐng)內(nèi)存存放數(shù)據(jù)后,接著就會(huì)返回該字符數(shù)組申請(qǐng)的連續(xù)內(nèi)存空間的首地址,這個(gè)地址是個(gè)char*類型,把地址賦值給同樣是char *類型的變量str,完成這條語句。

  1. char str[] = "program",在rom中申請(qǐng)(類似malloc)一個(gè)連續(xù)的內(nèi)存空間,大小為7+1個(gè)字節(jié),7字節(jié)存儲(chǔ)字符數(shù)組program,編譯器自動(dòng)在字符數(shù)組后面添加了一個(gè)'\0'字符,存儲(chǔ)在1字節(jié)上。接著左側(cè)的str變量會(huì)在棧內(nèi)存中申請(qǐng)8個(gè)字節(jié)的連續(xù)空間,str的內(nèi)存申請(qǐng)完成后,編譯器把rom上連續(xù)內(nèi)存上存儲(chǔ)的program一一復(fù)制(等號(hào)的作用)到str指向的棧的連續(xù)內(nèi)存空間中

第一種屬于字符串常量,無法被修改,與字符串直接量相關(guān)聯(lián)的內(nèi)存空間位于只讀部分。但是c語言是沒有字符串常量這種類型,它是常量字符數(shù)組類型,即const char *。通過字符指針來管理字符串。

第二種當(dāng)將字符串直接量賦值給字符數(shù)組的初始值的時(shí)候。由于字符數(shù)組存放與棧中,不允許引用其他地方的內(nèi)存,因此編譯器會(huì)將字符串直接量復(fù)制到棧的數(shù)組內(nèi)存中。因此,可以進(jìn)行相應(yīng)的修改。通過字符數(shù)組來管理字符串。

你會(huì)發(fā)現(xiàn)字符串中沒有字符'\0',怎么說是c-風(fēng)格字符串呢。

可以發(fā)現(xiàn),char str[8] = "program";這里用了8個(gè)字符空間,而program是7個(gè)字符,那么用7個(gè)可以嗎?

答案是不可以,因?yàn)榫幾g器會(huì)默認(rèn)在最后一個(gè)位置填充字符'\0',占用了一個(gè)字符位置。也就是編譯器通過自動(dòng)在字符數(shù)組末尾添加'\0',把雙引號(hào)的內(nèi)容轉(zhuǎn)換成了c-風(fēng)格的字符串,然后賦值給了變量!

sizeof("C++"); //結(jié)果是4!
char c1[] = {'C', '+', '+'}; //不是c-風(fēng)格,沒有‘\0’。求sizeof(c1),結(jié)果為3
char c2[] = {'C', '+', '+', '\0'}; //是c-風(fēng)格。求sizeof(c2),結(jié)果為4
char c3[] = "C++"; //自動(dòng)添加空字符。求sizeof(c3),結(jié)果為4

c語言中字符串操作函數(shù),比如scp,scm等操作的都是c-風(fēng)格字符串,這些字符串函數(shù)定義中就默認(rèn)了字符串的最后一個(gè)字符是'\0'。

6.你可能會(huì)問,為啥要這么設(shè)定'\0'是一個(gè)字符串的結(jié)尾?

這里就涉及到一個(gè)編碼問題:

在C語言中,字符串是以字符數(shù)組的形式進(jìn)行存儲(chǔ)的,且在數(shù)組中以'\0'作為終結(jié)符。由于'\0'在ASCII中表示空字符(NULL),即在語意上不可能有有效字符與之重復(fù),故用其來表示字符串的結(jié)尾至少在ASCII編碼下是合理的。

ASCII編碼及其擴(kuò)充規(guī)范中,每個(gè)字符長度都不超過1Byte,因此,在C風(fēng)格字符串中用'\0'表示結(jié)尾是合法的。

但在UTF16編碼中,每個(gè)字符使用2Byte進(jìn)行編碼,故會(huì)出現(xiàn)其中一個(gè)字符為0x00的情況,此時(shí)如果仍使用C風(fēng)格字符串,則在使用相關(guān)函數(shù)進(jìn)行處理時(shí),會(huì)在第一個(gè)0x00出現(xiàn)的位置就被認(rèn)為是字符串已經(jīng)結(jié)束,但其實(shí)字符串并不在此處終結(jié)。

UTF8是一種很好的解決方案,UTF8中字符的編碼非定長,可能是1Byte或者是2Byte,但是這種編碼方案中用每個(gè)字符的前綴來表示當(dāng)前字符的長度,因此既有足夠的空間來存儲(chǔ)較多的字符,又不會(huì)出現(xiàn)0x00導(dǎo)致字符串在被以C風(fēng)格字符串處理時(shí)異常結(jié)束。

7.設(shè)計(jì)字符串這種數(shù)據(jù)結(jié)構(gòu),一般有兩種想法

1.類似c-風(fēng)格字符串,以一個(gè)標(biāo)識(shí)符表示字符串的結(jié)尾

2.在字符串的開頭記錄了字符的個(gè)數(shù),其實(shí)在C++中設(shè)計(jì)的字符串string類,就不是在末尾以'\0'表示字符串的結(jié)尾,而是依靠在字符串前面放置了字符的個(gè)數(shù)!

8.c++中

在C++語言中,除了繼承了C語言中的這種字符串表達(dá)形式外,還新添了string類用來表達(dá)字符串。

為了區(qū)分C++中這兩種不同的字符串,使用“C風(fēng)格字符串”來特指來源于C語言的字符串存儲(chǔ)方式。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容