聊點基礎的–sizeof,strlen,數組,字符串在一起能整哪些坑?

  • 2019 年 11 月 24 日
  • 筆記

來源:公眾號【編程珠璣】

作者:守望先生

ID:shouwangxiansheng

對於初學者來說,sizeof,strlen,數組,字符串整在一起是痛苦的,它總能在某些莫名其妙的時候整一個措手不及。本文看看它們在一起能挖什麼坑。

例子

在說明這些問題之前,先看一段代碼,看看你是否都理解了。(註:以下代碼結果為編譯為64位程序後運行結果)

//來源:公眾號【編程珠璣】  #include <stdio.h>  #include <string.h>  void testArr(const char str[])  {      printf("%lu %lun", sizeof(str), strlen(str));  }  int main(void)  {      /*test 0*/      char str[] = "hello";      printf("test0 %lu %lunn", sizeof(str), strlen(str)); //6 5        /*test 1*/      char str1[8] = "hello";      printf("test1 %lu %lunn", sizeof(str1), strlen(str1)); //8 5        /**test 2*/      char str2[] = {'h','e','l','l','o'};      printf("test2 %lu %lunn", sizeof(str2), strlen(str2)); //5 10        /**test 3*/      char *str3 = "hello";      printf("test3 %lu %lunn", sizeof(str3), strlen(str3)); //8 5        /*test 4*/      char str4[] = "hello";      testArr(str4);//8 5        /*test 5*/      char str5[] = "hello";      printf("test5 %lu %lun", sizeof(str5), strlen(str5)); //7 4        /*test 6*/      char str6[10] = {0};      printf("test6 %lu %lunn", sizeof(str6), strlen(str6)); //10 0        /*test 7*/      char str7[5] = "hello";      printf("test7 %lu %lunn", sizeof(str7), strlen(str7)); //5 10        /*test 8*/      char str8[5] = {0};      strncpy(str8,"hello",5);      printf("%sn",str8);//hellohello      return 0;  }

特別注意觀察test2和test7。 在解釋這些測試之前,先複習一下sizeof,strlen以及數組的內容。

sizeof

首先需要明確的是,sizeof是操作符,即它並不是函數,它的作用對象是數據類型,因此,它作用於變量時,也是對其類型進行操作。得到的結果是該數據類型佔用空間大小,即size_t類型。 例如:

struct test  {      int a;      char b;  };  sizeof(int);//得到4  sizeof(test);//4位元組對齊時,得到8

需要注意的是,它在計算數據類型佔用空間大小時,會考慮位元組對齊,關於位元組對齊,可以參考《理一理位元組對齊的那些事》。 另外sizeof的時間複雜度是O(1)。

strlen

strlen是函數

size_t strlen(const char *s);

它用於計算字符串的長度。它的計算原則是: 從參數s所指向的內存開始往後計數,直到內存中的內容是0(即』』,參考《NULL,0,'',「0」,""你真的分得清嗎?》)為止。 例如:

#include<stdio.h>  #include<string.h>  int main()  {        char *p = "hello";      printf("%lun",strlen(p));//得到5      return 0;  }

這裡字符串hello的長度就是5,但是佔用空間是多少呢?

sizeof("hello");//得到6

是6,而不是5。 註:strlen的時間複雜度為O(N)。

數組

關於數組,更多內容可以參考《數組之謎》。

字符串

字符串是以''結尾的字符數組。

解析

實際上了解以上內容之後,很多問題迎刃而解。

test0

char str[] = "hello";  printf("test0 %lu %lunn", sizeof(str), strlen(str)); //6 5

上面的初始化方法等價於下面的方式:

char str[] = {'h','e','l','l','o',''};

它實際上就是一個字符數組,只不過上面這種賦值方式會在末尾加上''。

既然如此,那麼用sizeof求得佔用空間大小也就很明顯了是6。而strlen是遇到'',就結束,因此其求得長度為5。

test1

/*test 1*/  char str1[8] = "hello";  printf("test1 %lu %lunn", sizeof(str1), strlen(str1)); //8 5

test1類似,只不過它佔用空間是8,而長度仍然是5。

test2

/**test 2*/  char str2[] = {'h','e','l','l','o'};  printf("test2 %lu %lunn", sizeof(str2), strlen(str2)); //5 10

sizeof求str2的大小很明顯是5,而為啥那麼strlen得到的是10呢?還記得strlen的原則嗎,遇到''則結束,但是''在哪裡?至少我在str2中沒有看到,所以你可能看到的結果是10,也可能是另外一個莫名其妙的值,甚至可能導致程序崩潰。

test3

/**test 3*/  char *str3 = "hello";//最後有一個」隱形「的''  printf("test3 %lu %lunn", sizeof(str3), strlen(str3)); //8 5

為什麼前者是8?很顯然,str3並不是一個數組,而是一個字符指針,既然是指針類型,自然佔著指針的大小,而64位程序中,它的大小就是你看到的8。後者還是從str3指向的地址開始,直到遇到'',即得到長度5。

test4

/*test 4*/  char str4[] = "hello";  testArr(str4);//8 5

這在《數組之謎》中也提到過,當數組作為參數時,實際上只是一個指針,所以用sizeof計算時,會得到8。

test5

/*test 5*/  char str5[] = "hello";  printf("test5 %lu %lun", sizeof(str5), strlen(str5)); //7 4

同理,str5的初始化等價於下面:

char str5[] = {'h','e'.'l','l','','o',''};

所以不用解釋你也明白,sizeof得到的結果是7。而strlen遇到第一個''就停止繼續計算了,因此得到4。

test6

/*test 6*/  char str6[10] = {0};  printf("test6 %lu %lunn", sizeof(str6), strlen(str6)); //10 0

相信這個也好理解,佔用空間10,但是由於都是0,因此strlen得到長度為0。

test7

/*test 7*/  char str7[5] = "hello";  printf("test7 %lu %lunn", sizeof(str7), strlen(str7)); //5 10

這也是非常危險的,佔用空間是5,它沒有空間容納最後的'',因此導致strlen計算的結果和test2一樣,可能會是任意值。

test8

/*test 8*/  char str8[5] = {0};  strncpy(str8,"hello",5);  printf("%sn",str8);//hellohello

這裡在實際編程中最容易遇到的問題之一,數組大小為5,但是拷貝了5個位元組大小的數據。如果你把它當成字符數組使用也沒什麼問題,但是由於它最後沒有空間去容納'',因此你使用strlen,或者使用printf去打印的時候,可能發生難以預料的結果。

所以你可能會在你的項目代碼中看到類似這樣的寫法,將字符數組的最後一個位置賦值為0:

str8[4] = '';

總結

文本關鍵點如下:

  • sizeof計算類型佔用空間大小,時間複雜度O(1)
  • sizeof計算大小時會考慮位元組對齊
  • strlen計算字符串長度,時間複雜度O(N)
  • strlen作用對象是字符串(以''結尾)
  • strlen遇到''作罷,如果沒有遇到,則不可預料
  • 格外小心數組作為參數

另外注意下面兩種方式hello存儲的區域不一致:

char str[] = "hello";  char *str2 = "hello";//存儲在數據區,只讀