我個人一直比較喜歡 C 這門程式語言,大學的時候學習了微控制器,剛開始學的時候一臉懵逼,特別是那個組合語言學得簡直讓人懷疑人生。後面開始接觸使用 C 語言編寫微控制器程式,然後就這麼一直愛著它了。平時工作中用到的 C 程式語言不算多,但是我一直沒有放棄它,而是在默默的學習它、研究它,人嗎怎的有個小愛好吧?!
C 字串
在 C 語言中,字串實際上是使用字元 '\0'
終止的一維字元陣列。
以下幾種方式表示的都是 C 字串的正確表達方式。
// 要以 '\0' 結尾
char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
// 要以 '\0' 結尾
char greeting[] = {'H', 'e', 'l', 'l', 'o', '\0'};
// 預設會在末尾增加'\0'
char greeting[] = {"Hello"};
// 上面的簡寫形式
char greeting[] = "Hello";
// 預設會在末尾增加'\0'
char *greeting = "Hello";
複製程式碼
看下面另外一種宣告方式:
char greeting[] = {'h', 'e', 'l', 'l', 'o'};
printf("greeting: %s\n", greeting);
複製程式碼
輸出結果:
greeting: hello\376
複製程式碼
這個結果在不同編譯器下面可能還會不一樣,總之輸出都不是我們想要的結果。這種方式建立的字串沒有 '\0'
,不算是真正的 C 字串,所以建議大家在宣告 C 字串的時候使用字元指標(char *)的方式。
string.h
裡面宣告瞭很多關於操作 C 字串的庫函式。
字串長度
這裡在說計算字串長度的前提是字元編碼都是按照UTF-8(中文佔用3個位元組,英文佔用1個位元組)的編碼形式為前提的。我們先來看下面這個例子,如下:
char *greeting1 = "hello";
char greeting2[] = {'h', 'e', 'l', 'l', 'o'};
char greeting3[] = {'h', 'e', 'l', 'l', 'o', '\0'};
char greeting4[] = "hello";
printf("greeting1 sizeOf: %ld, strlen: %ld\n", sizeof(greeting1), strlen(greeting1));
printf("greeting2 sizeOf: %ld, strlen: %ld\n", sizeof(greeting2), strlen(greeting2));
printf("greeting3 sizeOf: %ld, strlen: %ld\n", sizeof(greeting3), strlen(greeting3));
printf("greeting4 sizeOf: %ld, strlen: %ld\n", sizeof(greeting4), strlen(greeting4));
複製程式碼
如果你能說出上面 printf
的結果,基本上關於計算字串長度的問題就迎刃而解了。
按照 UTF-8 編碼,上面例子的輸出結果如下所示:
greeting1 sizeOf: 8, strlen: 5
greeting2 sizeOf: 5, strlen: 7
greeting3 sizeOf: 6, strlen: 5
greeting4 sizeOf: 6, strlen: 5
複製程式碼
如果輸出結果令你無法相信,可以選擇繼續往下看或者你自己寫程式碼試試。
sizeof、strlen
在 linux.die 可以查到 strlen 的說明,如下:
Synopsis:
#include <string.h>
size_t strlen(const char *s);
Description:
The strlen() function calculates the length of the string s, excluding the terminating null byte (aq\0aq).
Return Value:
The strlen() function returns the number of bytes in the string s.
複製程式碼
函式 strlen
返回字串裡的字元數,不包括終止字元 '\0'
,這裡注意 strlen
是一個 C 的函式,而 sizeof
只是一個操作符。
我們知道,sizeof
操作符的引數可以是陣列、指標、型別、物件、函式等,函式 strlen
的引數只能是字串。
對於 sizeof
, 其引數不同時,其返回的值也不一樣,如下:
1、陣列:編譯時分配的陣列空間大小; 2、指標:儲存該指標所用的空間大小(32位機器上是4,64位機器上是8); 3、型別:該型別所佔的空間大小; 4、物件:物件的實際佔用空間大小(這個指的是在 C++ 中); 5、函式:函式的返回型別所佔的空間大小。函式的返回型別不能是 void 型別;
那我們再回頭看看上面的例子,我把要說明的寫在註釋上面了。
// 注意這裡是指標
char *greeting1 = "hello";
// 沒有結束符 '\0',其 strlen 結果不確定
char greeting2[] = {'h', 'e', 'l', 'l', 'o'};
char greeting3[] = {'h', 'e', 'l', 'l', 'o', '\0'};
char greeting4[] = "hello";
/* 結果是 8、5 */
/* greeting1是指標,sizeOf計算的是其儲存該指標所用的空間大小,因為我使用的是64位 macOS,所以輸出是8 */
/*strlen 計算的是字元個數但是不包括結束符 '\0'*/
printf("greeting1 sizeOf: %ld, strlen: %ld\n", sizeof(greeting1), strlen(greeting1));
/* 結果是 5、7 */
/* sizeof 計算的是編譯時分配的陣列空間大小,這裡是5 */
/* greeting2沒有結束符,strlen 的計算結果不確定 */
printf("greeting2 sizeOf: %ld, strlen: %ld\n", sizeof(greeting2), strlen(greeting2));
/* 結果是 6、5 */
/* sizeof 計算的是編譯時分配的陣列空間大小,這裡是6,因為多了結束符 */
/*strlen 計算的是字元個數但是不包括結束符 '\0'*/
printf("greeting3 sizeOf: %ld, strlen: %ld\n", sizeof(greeting3), strlen(greeting3));
/* 結果是 6、5,這裡類似上面的情況,不再贅述 */
printf("greeting4 sizeOf: %ld, strlen: %ld\n", sizeof(greeting4), strlen(greeting4));
複製程式碼
小結:
1、sizeof
是一個操作符,而 strlen
是 C 語言的庫函式。
2、sizeof
的引數可以是任意資料型別或者表示式,而 strlen
只能以結尾為 '\0'
的字串作引數。
3、sizeof
的結果在編譯時就計算出了,而 strlen
必須在執行時才能計算出來。
4、sizeof
計算資料型別佔記憶體的大小,strlen
計算字串實際長度,要記住 strlen
計算出來的結果不包括結束符 '\0'
。
5、sizeof
反應的並非真實字串長度而是所佔空間大小,所以memset
初始化字串的時候用 sizeof
較好。
6、系統函式返回值是 char *
(字元指標)型別的會在末尾加上結束符 '\0'
。
7、無論是 sizeof
還是 strlen
計算結果的單位都是位元組。
我們還需要注意一點,strlen
函式,當陣列名作為引數傳入時,實際上陣列就退化成指標了。舉個例子,如下圖所示:
探索無止境
在文章的開始,我給出了幾種 C 字串的正確表達方式,那我們再來看另外一種。
char greeting[4] = "blog";
複製程式碼
這種方式看起來好像很完美的樣子,其實是不對的,寫個例子給大家,如下:
int main(int argc, const char *argv[])
{
char greeting[4] = "blog";
size_t len = strlen(greeting);
printf("greeting len: %ld\n", len);
printf("greeting: %s\n", greeting);
return 0;
}
複製程式碼
編譯執行,結果如下:
greeting len: 10
greeting: blog\330\365\277\357\376
複製程式碼
蒼天呀,這結果讓人無語。。。
對於 char greeting[4] = "blog"
其實是定義一個長度為 4 的字元陣列,但是字串 "blog"
實際是要包括結束符 \0
的,也就是說下面的程式碼
char greeting[4] = "blog";
複製程式碼
本質和下面程式碼是一樣的,如下:
char greeting[] = {'b', 'l', 'o', 'g'};
複製程式碼
顯然是不正確的,那我們修改一下程式碼,如下:
int main(int argc, const char *argv[])
{
// 注意這裡是 5
char greeting[5] = "blog";
size_t len = strlen(greeting);
printf("greeting len: %ld\n", len);
printf("greeting: %s\n", greeting);
return 0;
}
複製程式碼
或者這樣寫:
int main(int argc, const char *argv[])
{
// 這裡乾脆不要寫數字
char greeting[] = "blog";
size_t len = strlen(greeting);
printf("greeting len: %ld\n", len);
printf("greeting: %s\n", greeting);
return 0;
}
複製程式碼
這樣修改後,再編譯執行結果就對了,如下:
greeting len: 4
greeting: blog
複製程式碼
我們知道的東西是有限的,我們不知道的東西則是無窮的。