超詳解答:C語言|字符數(shù)組和字符串
發(fā)布時(shí)間:2023-11-13 14:15:49
用來存放字符的數(shù)組稱為字符數(shù)組,例如:
char a[10]; //一維字符數(shù)組
char b[5][10]; //二維字符數(shù)組
char c[20]={'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a','m'}; // 給部分?jǐn)?shù)組元素賦值
char d[]={'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a', 'm' }; //對全體元素賦值時(shí)可以省去
字符數(shù)組實(shí)際上是一系列字符的集合,也就是字符串(String)。在C語言中,沒有專門的字符串變量,沒有string類型,通常就用一個(gè)字符數(shù)組來存放一個(gè)字符串。
C語言規(guī)定,可以將字符串直接賦值給字符數(shù)組,例如:
char str[30] = {"c.biancheng.net"};
char str[30] = "c.biancheng.net"; //這種形式更加簡潔,實(shí)際開發(fā)中常用
數(shù)組第 0 個(gè)元素為'c'
,第 1 個(gè)元素為'.'
,第 2 個(gè)元素為'b'
,后面的元素以此類推。
為了方便,你也可以不指定數(shù)組長度,從而寫作:
char str[] = {"c.biancheng.net"};char str[] = "c.biancheng.net"; //這種形式更加簡潔,實(shí)際開發(fā)中常用
給字符數(shù)組賦值時(shí),我們通常使用這種寫法,將字符串一次性地賦值(可以指明數(shù)組長度,也可以不指明),而不是一個(gè)字符一個(gè)字符地賦值,那樣做太麻煩了。
這里需要留意一個(gè)坑,字符數(shù)組只有在定義時(shí)才能將整個(gè)字符串一次性地賦值給它,一旦定義完了,就只能一個(gè)字符一個(gè)字符地賦值了。請看下面的例子:
char str[7];
str = "abc123"; //錯誤
//正確
str[0] = 'a'; str[1] = 'b'; str[2] = 'c';
str[3] = '1'; str[4] = '2'; str[5] = '3';
字符串結(jié)束標(biāo)志
字符串是一系列連續(xù)的字符的組合,要想在內(nèi)存中定位一個(gè)字符串,除了要知道它的開頭,還要知道它的結(jié)尾。找到字符串的開頭很容易,知道它的名字(字符數(shù)組名或者字符串名)就可以;然而,如何找到字符串的結(jié)尾呢?C語言的解決方案有點(diǎn)奇妙,或者說有點(diǎn)奇葩。
在C語言中,字符串總是以'\0'
作為結(jié)尾,所以'\0'
也被稱為字符串結(jié)束標(biāo)志,或者字符串結(jié)束符。
'\0'
是 ASCII 碼表中的第 0 個(gè)字符,英文稱為 NUL,中文稱為“空字符”。該字符既不能顯示,也沒有控制功能,輸出該字符不會有任何效果,它在C語言中唯一的作用就是作為字符串結(jié)束標(biāo)志。
C語言在處理字符串時(shí),會從前往后逐個(gè)掃描字符,一旦遇到'\0'
就認(rèn)為到達(dá)了字符串的末尾,就結(jié)束處理。'\0'
至關(guān)重要,沒有'\0'
就意味著永遠(yuǎn)也到達(dá)不了字符串的結(jié)尾。
由" "
包圍的字符串會自動在末尾添加'\0'
。例如,"abc123"
從表面看起來只包含了 6 個(gè)字符,其實(shí)不然,C語言會在最后隱式地添加一個(gè)'\0'
,這個(gè)過程是在后臺默默地進(jìn)行的,所以我們感受不到。
下圖演示了"C program"
在內(nèi)存中的存儲情形:
需要注意的是,逐個(gè)字符地給數(shù)組賦值并不會自動添加'\0'
,例如:
char str[] = {'a', 'b', 'c'};
數(shù)組 str 的長度為 3,而不是 4,因?yàn)樽詈鬀]有'\0'
。
當(dāng)用字符數(shù)組存儲字符串時(shí),要特別注意'\0'
,要為'\0'
留個(gè)位置;這意味著,字符數(shù)組的長度至少要比字符串的長度大 1。請看下面的例子:
char str[7] = "abc123";
"abc123"
看起來只包含了 6 個(gè)字符,我們卻將 str 的長度定義為 7,就是為了能夠容納最后的'\0'
。如果將 str 的長度定義為 6,它就無法容納'\0'
了。
當(dāng)字符串長度大于數(shù)組長度時(shí),有些較老或者不嚴(yán)格的編譯器并不會報(bào)錯,甚至連警告都沒有,這就為以后的錯誤埋下了伏筆,讀者自己要多多注意。
有些時(shí)候,程序的邏輯要求我們必須逐個(gè)字符地為數(shù)組賦值,這個(gè)時(shí)候就很容易遺忘字符串結(jié)束標(biāo)志'\0'
。下面的代碼中,我們將 26 個(gè)大寫英文字符存入字符數(shù)組,并以字符串的形式輸出:
#include <stdio.h>
int main(){
char str[30];
char c;
int i;
for(c=65,i=0; c<=90; c++,i++){
str[i] = c;
}
printf("%s\n", str);
return 0;
}
在 VS2015 下的運(yùn)行結(jié)果:
ABCDEFGHIJKLMNOPQRSTUVWXYZ口口口口i口口0 ?
口
表示無法顯示的特殊字符。
大寫字母在 ASCII 碼表中是連續(xù)排布的,編碼值從 65 開始,到 90 結(jié)束,使用循環(huán)非常方便。
在很多編譯器下,局部變量的初始值是隨機(jī)的,是垃圾值,而不是我們通常認(rèn)為的“零”值。局部數(shù)組(在函數(shù)內(nèi)部定義的數(shù)組,本例中的 str 數(shù)組就是在 main() 函數(shù)內(nèi)部定義的)也有這個(gè)問題,很多編譯器并不會把局部數(shù)組的內(nèi)存都初始化為“零”值,而是放任不管,愛是什么就是什么,所以它們的值也是沒有意義的,也是垃圾值。
在函數(shù)內(nèi)部定義的變量、數(shù)組、結(jié)構(gòu)體、共用體等都稱為局部數(shù)據(jù)。在很多編譯器下,局部數(shù)據(jù)的初始值都是隨機(jī)的、無意義的,而不是我們通常認(rèn)為的“零”值。這一點(diǎn)非常重要,大家一定要謹(jǐn)記,否則后面會遇到很多奇葩的錯誤。
本例中的 str 數(shù)組在定義完成以后并沒有立即初始化,所以它所包含的元素的值都是隨機(jī)的,只有很小的概率會是“零”值。循環(huán)結(jié)束以后,str 的前 26 個(gè)元素被賦值了,剩下的 4 個(gè)元素的值依然是隨機(jī)的,不知道是什么。
printf() 輸出字符串時(shí),會從第 0 個(gè)元素開始往后檢索,直到遇見'\0'