sizeof、strlen、字符串、數組,整到一塊,你還清楚嗎?


寫在前面

sizeof、strlen、字符串、數組,提到這些概念,相信學過C語言的人都能耳熟能詳,也能談得頭頭是道,但是,在實際運用中,當這些內容交織在一起時,大家卻不一定能搞地清清楚楚,本文的目的正是幫助大家將相關知識總結清楚。

 

正文

先看一段代碼

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 void testchar(char str[])
 5 {
 6     printf("%d %d\n", sizeof(str), strlen(str));
 7 }
 8 
 9 void testint(int arr[])
10 {
11     printf("%d\n", sizeof(arr));
12 }
13 
14 int main()
15 {
16     char str[] = "abc";
17     printf("%d %d\n", sizeof(str), strlen(str)); //4 3
18 
19     char str1[10] = "abc";
20     printf("%d %d\n", sizeof(str1), strlen(str1)); //10 3
21 
22     char dog[] = "wangwang\0miao";
23     printf("%d %d\n", sizeof(dog), strlen(dog)); //14 8
24     testchar(dog); //4 8
25 
26     char *cat = "wangwang\0miaomiao";
27     printf("%d %d\n", sizeof(cat), strlen(cat)); //4 8
28     
29     int arr[10] = { 0 };
30     printf("%d %d\n", sizeof(arr), sizeof(arr[11])); //40 4
31     testint(arr); //4
32 
33     return 0;
34 }

 

 結果

 

在解釋上面的例子之前,我們先來說一說sizeof和strlen。

語法上的本質不同:

sizeof是運算符,strlen是函數。

適用范圍不一樣:

對sizeof(name)而言,name可以是變量名也可以是類型名,對strlen而言,參數必須是char*類型的,即strlen僅用於字符串。

重中之重——從底層看本質

strlen(ptr)的執行機理是:從參數ptr所指向的內存開始向下計數,直到內存中的內容是全0(即’\0’)為止(不會對’\0’進行計數)。用strlen測量字符串的長度,其實就是基於這個原理。

sizeof(name)的執行機理是:如果name是一個類型名,得到的是該類型的大小(所謂類型的大小,指的是:如果存在一個該類型的變量,這個變量在內存中所占用的字節數),如果name是一個變量名,那么,sizeof(name)並不會真正訪問該變量,而是先獲知該變量的類型,然后再返回該類型的大小(即便是struct這樣的復雜類型,編譯器在編譯時也會根據它的各個域記錄其大小,所以,由類型得到類型大小,不是一件難事)。換句話說,本質上,sizeof的運算對象是類型。如果name是一個變量名,那么,sizeof如何“看待”name的類型,將是一個關鍵問題。(后面我們會對這一點有深刻的體會)

上面提到的這一點,是理解好sizeof和strlen的不二法門,是放之四海皆准的准則。下面,我們就以這樣的准則來分析上面的例子。

a.

char str[] = "abc";
printf("%d %d\n", sizeof(str), strlen(str)); //4 3

這里,是用數組的形式聲明字符串,編譯器會自動在字符串后面加上'\0',所以,數組的元素個數是4而不是3。對於sizeof(str)而言,sizeof將str視為char [4]l類型的變量,所以,sizeof(str)的結果就是整個數組所占有的空間大小。對於strlen(str)來說,它從str指向的內存開始計數,直到遇到全0的內存('\0'),所以最后得到結果3。

b.

char str1[10] = "abc";
printf("%d %d\n", sizeof(str1), strlen(str1)); //10 3

 

編譯器為char str1[10]分配10個數組元素大小的空間,這與初始化它的字符串沒有關系,所以sizeof(str1)得到10。

c.

char dog[] = "wangwang\0miao";
printf("%d %d\n", sizeof(dog), strlen(dog)); //14 8
testchar(dog); //4 8

前兩句和a中的情況相同,sizeof(dog)輸出整個數組所占的內存大小(包括編譯器加上去的'\0'),strlen(dog)遇到'\0'就停止,所以輸出8。

再看后面的函數調用,數組名dog作為函數實參傳入,我們再來回顧一下testchar函數

void testchar(char str[])
{
    printf("%d %d\n", sizeof(str), strlen(str));
}

我們發現,這里sizeof(str)並沒有像sizeof(dog)那樣得到14,而是得到了4。這是因為,str是函數形參,盡管它是以數組名的形式出現的,傳給它的實參也確實是數組名,但sizeof僅僅把它當成一個char*類型的指針看待,所以,sizeof(str)的結果就是char *類型所占的空間4。至於strlen(str),我們前面說過,它執行的機理就是從str指向的內存開始向下計數,直到遇到'\0',所以依然得到8。

d.

char *cat = "wangwang\0miaomiao";
printf("%d %d\n", sizeof(cat), strlen(cat)); //4 8 

由於cat明確聲明為char*,所以sizeof將它視為指針,得到4。

e.

int arr[10] = { 0 };
printf("%d %d\n", sizeof(arr), sizeof(arr[11])); //40 4
testint(arr); //4

前面說過,當數組名作為函數形參出現時,sizeof僅僅將其視為一個指針,否則,sizeof認為它代表整個數組,所以,sizeof(arr)得到整個數組所占的字節數40,而testint(arr)的結果是int*類型的指針的長度4。

sizeof(int[11])中,很明顯數組越界了,但並不會出現運行時錯誤。原因是:依據我們給出的判斷准則,sizeof並沒有真正訪問arr[11],根據arr的聲明,sizeof知道arr[11]是int型的,所以返回int類型的大小。

至於testint(arr),道理和c中的testchar(dog)相同。

 

最后,基於上面的討論,給出編碼准則:

1.永遠不要用sizeof來求字符串長度!它不是干這個活的,所以你也永遠不會得到正確答案。

2.不要自作聰明地用sizeof(arr)/sizeof(arr[0])這樣的代碼求數組的長度!sizeof也不是干這個活的。如果arr是函數形參,得到的結果將是錯誤的(除非你在32位系統下恰好聲明int arr[1]或者char arr[4]等,但這純屬巧合)。既然是數組,長度自然是已知的,求數組長度這一本身,就是多此一舉的愚蠢行為。

 

寫在后面

本文的目的,就是使讀者對C語言的基礎知識——sizeof和strlen有一個本質的認識,同時對與之相關的易錯、易混問題有一個正確、清晰的判斷。由於在下才疏學淺,錯誤疏漏之處在所難免,希望廣大讀者積極批評指正,您的批評指正是在下前進的不竭動力。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM