你不見得會計算C字符串長度

我我的一直比較喜歡 C 這門編程語言,大學的時候學習了單片機,剛開始學的時候一臉懵逼,特別是那個彙編語言學得簡直讓人懷疑人生。後面開始接觸使用 C 語言編寫單片機程序,而後就這麼一直愛着它了。平時工做中用到的 C 編程語言不算多,可是我一直沒有放棄它,而是在默默的學習它、研究它,人嗎怎的有個小愛好吧?!linux

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";
複製代碼

看下面另一種聲明方式:bash

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個字節)的編碼形式爲前提的。咱們先來看下面這個例子,以下:ui

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 編碼,上面例子的輸出結果以下所示:spa

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, 其參數不一樣時,其返回的值也不同,以下:

一、數組:編譯時分配的數組空間大小; 二、指針:存儲該指針所用的空間大小(32位機器上是4,64位機器上是8); 三、類型:該類型所佔的空間大小; 四、對象:對象的實際佔用空間大小(這個指的是在 C++ 中); 五、函數:函數的返回類型所佔的空間大小。函數的返回類型不能是 void 類型;

那咱們再回頭看看上面的例子,我把要說明的寫在註釋上面了。

// 注意這裏是指針
char *greeting1 = "hello";

// 沒有結束符 '\0',其 strlen 結果不肯定
char greeting2[] = {'h', 'e', 'l', 'l', 'o'};

char greeting3[] = {'h', 'e', 'l', 'l', 'o', '\0'};
char greeting4[] = "hello";
    
/* 結果是 八、5 */
/* greeting1是指針,sizeOf計算的是其存儲該指針所用的空間大小,由於我使用的是64位 macOS,因此輸出是8 */
/*strlen 計算的是字符個數可是不包括結束符 '\0'*/
printf("greeting1 sizeOf: %ld, strlen: %ld\n", sizeof(greeting1), strlen(greeting1));

/* 結果是 五、7 */
/* sizeof 計算的是編譯時分配的數組空間大小,這裏是5 */
/* greeting2沒有結束符,strlen 的計算結果不肯定 */
printf("greeting2 sizeOf: %ld, strlen: %ld\n", sizeof(greeting2), strlen(greeting2));

/* 結果是 六、5 */
/* sizeof 計算的是編譯時分配的數組空間大小,這裏是6,由於多告終束符 */
/*strlen 計算的是字符個數可是不包括結束符 '\0'*/
printf("greeting3 sizeOf: %ld, strlen: %ld\n", sizeof(greeting3), strlen(greeting3));

/* 結果是 六、5,這裏相似上面的狀況,再也不贅述 */
printf("greeting4 sizeOf: %ld, strlen: %ld\n", sizeof(greeting4), strlen(greeting4));
複製代碼

小結

一、sizeof 是一個操做符,而 strlen 是 C 語言的庫函數。

二、sizeof 的參數能夠是任意數據類型或者表達式,而 strlen 只能以結尾爲 '\0' 的字符串做參數。

三、sizeof 的結果在編譯時就計算出了,而 strlen 必須在運行時才能計算出來。

四、sizeof 計算數據類型佔內存的大小,strlen 計算字符串實際長度,要記住 strlen 計算出來的結果不包括結束符 '\0'

五、sizeof 反應的並不是真實字符串長度而是所佔空間大小,因此memset 初始化字符串的時候用 sizeof 較好。

六、系統函數返回值是 char * (字符指針)類型的會在末尾加上結束符 '\0'

七、不管是 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
複製代碼

咱們知道的東西是有限的,咱們不知道的東西則是無窮的。

在這裏插入圖片描述
相關文章
相關標籤/搜索