作者:乾坤一笑 來源:http://blog./smileonce/archive/2005/06/26/8330.html 摘要:介紹了C語言中的char *和字符串,比較深入 前幾天和清風(fēng)雨交談strncpy()函數(shù)的時候,他認為“如果一個函數(shù)有一個char * str的參數(shù),那么str一定是一個字符串”,而我對此不以為然。難得到了周末,抽得出功夫,談?wù)勎覍har *含義的認識,與大家共同討論一下。 C語言是開發(fā)操作系統(tǒng)的首選語言,在很多方面都能從C代碼中看到機器的固有性質(zhì)。比如,PASCAL語言的數(shù)組索引是從1開始計數(shù),而C語言就是從0開始計數(shù)。從0開始計數(shù)不太符合一般人的正常思維模式,但是為什么C語言要采用這種設(shè)計方式呢?因為C語言最初主要是為操作系統(tǒng)開發(fā)人員和編譯器設(shè)計人員設(shè)計的——對于經(jīng)常需要考察內(nèi)存地址的開發(fā)人員,偏移量的概念在他們腦子里面根深蒂固。要把100個int型的整數(shù)放在從地址0x0000開始的一段內(nèi)存中,如果系統(tǒng)是按Byte編址的,那么第100個元素(其數(shù)組索引為99)的要放入的地址必然是:0x0000 + sizeof(int) * 99, 而不是乘以100,所以索引以0開始是很有好處的。(C語言的底層特性請參考P.V.D.L的《Expert C Programming——Deep C Secrets》,中文版《C專家編程》) 同樣而言,C語言對所謂字符串的處理也和其他語言不同。(參見拙著《我用錯了strcat() 》文后的評論) 它同樣體現(xiàn)了便于系統(tǒng)設(shè)計的特點。例如unix系統(tǒng)總是把設(shè)備都映射為文件,對I/O流、光驅(qū)、硬盤、modem的訪問最終都轉(zhuǎn)換為了對文件的處理。而文件,也可以看作是一個長長的字符數(shù)組(文件以EOF結(jié)尾)。C語言沒有專門定義字符串?dāng)?shù)據(jù)類型(如其他語言中的string),它用以‘\0‘結(jié)尾的字符數(shù)組來表示一個邏輯意義上的字符串。C語言用一些對于字符數(shù)組的處理函數(shù)(特別是對以‘\0‘結(jié)尾的字符數(shù)組的處理函數(shù))來處理所謂的以‘\0‘結(jié)尾的字符串,并把他們放在string.*、stdio.*等一些標準庫文件中。string這個字眼也就對人造成了某些誤解,好像是C語言中定義了字符串這種類型一樣。其實C語言只定義了char、int、float、void、poiner這幾種基本類型,這正是C語言簡潔之處。至于所謂的字符串,只是對字符數(shù)組的一種特殊應(yīng)用而已。 由于沒有了“字符串類型”,“傳char *參數(shù)就是傳一個字符串進來”之說自然也不攻自破。那么char *真正的含義是什么?我們不妨用大家最熟悉的int來對比一下。 #include <stdio.h> int main(int argc, char *argv[]) { int b[3] = { 17, 18, 19 }; int a = 5; int d = 2103157716; int * i; // pay attention to this variable(注意:這個指針 i) i = b; printf("%d \n", *i); // i points to a array, *i gets the first number of the array, 代碼中的int * i就是我們關(guān)注的焦點。它是一個指向int指針。也就是說:i指向一個內(nèi)存地址,從這個地址開始存儲了一個數(shù)據(jù)。int * i中的int標明應(yīng)該使用int類型(長度為sizeof(int)個字節(jié))來從這個地址取數(shù)據(jù),也就是說要一次取sizeof(int)個byte的數(shù)據(jù)來拼成最后的結(jié)果。最后一個例子也證明了這一點:如果我們強制用unsigned char的大小的數(shù)據(jù)類型來對這個地址操作,就只能取出數(shù)據(jù)的一部分。反過來說,如果用較大數(shù)據(jù)類型來取實際存儲較小數(shù)據(jù)類型的數(shù)據(jù),就有可能越界操作內(nèi)存,取回一些雜亂的內(nèi)容或?qū)е孪到y(tǒng)崩潰。int b[]這個數(shù)組,標明有一組數(shù),放在以&b開始地址的內(nèi)存空間內(nèi),每個元素占用了sizeof(int)個byte的內(nèi)存單元;如果有類似于i=&b;i++;的操作,i的值就每次遞增sizeof(int)而不是1,這樣確保了i每次都能恰好取回一個正確的int。 同理,char * c也是如此。如果我們定義了一個char *的變量c,那么c也只不過是一個指向內(nèi)存中某個地址的指針而已。之所以標明它是char *的類型,就是說要以sizeof(char)為單位去內(nèi)存中取數(shù)。所以,我們應(yīng)該稱呼char * c為指向char類型的指針——而不是說c就是字符串。為什么傳一個char *指針給printf(),strlen()之類的函數(shù),它就能把它當(dāng)作一個字符串來處理呢?沒錯,我們不是定義了‘\0‘來表示一個"字符串"的終結(jié)么?我們只需從起始地址不斷累加,遍歷字符數(shù)組的每一個元素,直到找到一個‘\0‘為止,就算是處理一個字符串了——從起始地址到‘\0‘為止的字符數(shù)組元素構(gòu)成一個“字符串”,這就是C語言設(shè)計字符串的原理。 所以,當(dāng)一個函數(shù)要求傳入一個char *的參數(shù),并不一定這個參數(shù)就一定是字符串(以‘\0‘結(jié)尾的字符數(shù)組),char *只是一個字符指針而已,它僅僅提供了一個內(nèi)存地址和每次遍歷元素的偏移量而已。究竟函數(shù)對傳入的參數(shù)有什么要求,還要視函數(shù)的具體實現(xiàn)而定。(我想ANSI C應(yīng)該對參數(shù)有所要求和規(guī)定,可惜偶沒有ANSI C文件,無法參考。 )C語言一般約定是用char * str來表示以‘\0‘結(jié)尾的字符數(shù)組,但是由于某些實現(xiàn)上的效率的考慮,往往沒有嚴格遵守這個約定。C語言的設(shè)計理念中沒有強調(diào)使用者一定要使用遵守這個約定,不遵守這個約定也不違背C語言的基本語法規(guī)則。這或許可以看作是C語言和創(chuàng)造和使用它的黑客群體崇尚自由的一種特色、一種精神文化吧。 Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=670277 |
|
來自: wangzai117 > 《我的圖書館》