英文原版:P277
本章將介紹處理字符串的便捷方法十酣,包括字符串字面量和字符串變量,其中字符串變量會(huì)隨著程序的執(zhí)行而改變党晋。
- 13.1節(jié)介紹有關(guān)字符串字面量的規(guī)則斧抱,包括如何在字符串字面量中嵌入轉(zhuǎn)義序列,如何分隔較長(zhǎng)的字符串字面量掐禁。
- 13.2節(jié)介紹如何聲明字符串變量:字符串變量就是字符數(shù)組怜械,最后一個(gè)字符是空字符
null
,用來標(biāo)記字符串的結(jié)束傅事。 - 13.3節(jié)介紹了若干種讀寫字符串的方法缕允。
- 13.4節(jié)描述了如何編寫函數(shù)來處理字符串。
- 13.5節(jié)介紹了在C語言庫(kù)里的一些字符串處理函數(shù)蹭越。
- 13.6節(jié)列舉了處理字符串時(shí)常用的編程約定障本。
- 13.7節(jié)描述了如何創(chuàng)建數(shù)組元素是指向不同長(zhǎng)度字符串的指針的數(shù)組,及介紹了在C語言中如何使用這類數(shù)組來為程序提供命令行的信息响鹃。
13.1節(jié) 字符串字面量
字符串字面量就是用雙引號(hào)
""
括起來的字符序列驾霜。
理解字符串字面量
- C語言將字符串字面量當(dāng)做字符數(shù)組。
-
C編譯器認(rèn)為字符串字面量的類型為
char *
茴迁。 -
"abc"
是一個(gè)指針寄悯,該指針指向一個(gè)內(nèi)存位置,該內(nèi)存位置包含字符abc堕义,及一個(gè)額外的空字符null
猜旬,用于標(biāo)記該字符串字面量的結(jié)束。 -
"abc"
等價(jià)于初始化式{'a', 'b','c', '\0'}
倦卖; - 經(jīng)常作為格式化字符串出現(xiàn)在
scanf
和printf
的調(diào)用中洒擦;
例1 字符串字面量
"When you come to a fork in the road, take it."
13.1.1節(jié) 在字符串字面量中的轉(zhuǎn)義序列
C語言允許字符串字面量中像字符常量一樣包含轉(zhuǎn)義序列。
例2 在字符串字面量中出現(xiàn)轉(zhuǎn)義序列
"Candy\nIs dandy\nBut liquor\n Is Quicker.\n --Ogden Nash\n"
注意:
- 雖然八進(jìn)制數(shù)和十六進(jìn)制數(shù)的轉(zhuǎn)移序列在字符串字面量中是合法的怕膛,但是它們沒有字符轉(zhuǎn)義序列常用熟嫩。
- 八進(jìn)制數(shù)的轉(zhuǎn)義序列會(huì)在3個(gè)數(shù)字后或者首個(gè)非8進(jìn)制字符處結(jié)束;比如"\1234"包含兩個(gè)字符(\123和4)褐捻,"\189"包含3個(gè)字符(\1掸茅、8椅邓、9)。
- 對(duì)16進(jìn)制的轉(zhuǎn)義序列的長(zhǎng)度沒有限制昧狮,它會(huì)持續(xù)直到出現(xiàn)首個(gè)非16進(jìn)制字符處結(jié)束景馁;比如"\xfc"包含一個(gè)字符,"Z\fcrich"包含6個(gè)字符(Z逗鸣、\fc合住、r、i撒璧、c透葛、h),"\xfcber"包含2個(gè)字符(\xfcbe卿樱、r)僚害。
13.1.2節(jié) 延續(xù)字符串常量
方法一:首行以反斜線\
結(jié)尾,且字符串必須從下一行的起始處開始殿如。
例3 使用反斜線來分隔較長(zhǎng)的字符串字面量
printf("When you come to a fork in the road, take it. \n
--Yogi Berra");
方法二:僅使用空格來分隔兩個(gè)或者多個(gè)毗鄰的字符串字面量
例4 使用空格來分隔多個(gè)字符串常量
printf("When you come to a fork in the road, take it."
"Yogi Berra");
13.1.3節(jié) 如何存儲(chǔ)字符串字面量
C語言將字符串字面量當(dāng)做字符數(shù)組贡珊。
C編譯器認(rèn)為字符串字面量的類型為char *
。
當(dāng)C編譯器遇到一個(gè)長(zhǎng)度為的字符串字面量時(shí)涉馁,它會(huì)為該字符串分配大小為
個(gè)字節(jié)的存儲(chǔ)空間门岔。
這塊存儲(chǔ)空間包含在字符串字面量里的所有字符,及一個(gè)額外的空字符null
烤送,用于標(biāo)記該字符串字面量的結(jié)束寒随。
空字符null
是一個(gè)所有位都是0的字節(jié),可用\0
來表示帮坚。
注意
區(qū)分空字符null
(\0
)和0字符('0'
)
- 空字符
null
的ASCII碼值為0妻往; - 0字符的ASCII碼值為48;
例5 理解printf
調(diào)用時(shí)是如何傳遞字符串字面量的
// printf函數(shù)的首個(gè)參數(shù)值類型就是char *
// 調(diào)用printf時(shí)试和,是將字符串字面量"abc"的地址(即指向首字符'a'的內(nèi)存地址的指針)傳遞給printf函數(shù)讯泣。
printf("abc");
13.1.4節(jié) 字符串字面量有哪些操作?
C語言允許:在任何可以使用char *
的地方阅悍,都可以使用字符串字面量好渠,比如
- 賦值運(yùn)算符的右邊
- 對(duì)字符串字面量取下標(biāo)操作
注意:
盡量避免去修改字符串字面量,因?yàn)閲L試去修改字符串字面量會(huì)導(dǎo)致未定義的行為节视。
例1 賦值
char *p;
// 賦值并沒有拷貝字符串"abc"中的任意字符拳锚;
//賦值只是使得p指向該字符串的首個(gè)字符
p = "abc";
例2 下標(biāo)運(yùn)算
char ch;
//ch的值是字符'b'
ch = "abc"[1];
例3 將0到15的數(shù)字轉(zhuǎn)換成等價(jià)的十六進(jìn)制字符
char digit_to_hex_char(int digit)
{
return "0123456789abcdef"[digit];
}
例4 錯(cuò)誤示例:修改字符串字面量
char *p = "abc";
//修改字符串字面量可能會(huì)導(dǎo)致程序崩潰或者出錯(cuò)
*p = 'd';
13.1.5節(jié) 字符串字面量 VS 字符常量
-
'a'
:表示的是一個(gè)數(shù)字,即該字符的ASCII碼寻行; -
"a"
:表示的是一個(gè)指針霍掺,該指針指向一個(gè)內(nèi)存地址,該內(nèi)存地址里包含字符a
和空字符null
;
注意:
- 永遠(yuǎn)不要在需要使用字符串字面量的地方使用字符常量杆烁。
例5 錯(cuò)誤示例
//這個(gè)是非法的
printf('\n');
13.2節(jié) 字符串變量
如何存儲(chǔ)字符串變量牙丽?
-
只要保證最后一個(gè)字符是空字符
null
,則任何一個(gè)一維字符數(shù)組都可用來存儲(chǔ)字符串兔魂。 -
當(dāng)聲明使用一個(gè)字符數(shù)組來存儲(chǔ)字符串時(shí)剩岳,通常會(huì)使得數(shù)組的長(zhǎng)度比字符串多一個(gè)字符,因?yàn)镃語言約定每個(gè)字符串以空字符
null
結(jié)尾入热。 -
如果沒有預(yù)留出空字符的位置,則程序的行為是不可預(yù)測(cè)的晓铆,因?yàn)樵贑語言庫(kù)里的函數(shù)都假設(shè)字符串是以
null
結(jié)尾的勺良。
缺點(diǎn):
- 很難區(qū)分字符串?dāng)?shù)組是否被當(dāng)做字符串來使用;
- 如果要編寫自己的字符串處理函數(shù)骄噪,則需要特別小心地處理空字符
null
尚困; - 除了一個(gè)接一個(gè)字符地遍歷搜索外,沒有更快地方法來判斷一個(gè)字符串的長(zhǎng)度链蕊。
例1 存儲(chǔ)一個(gè)最多有80個(gè)字符的字符串
#define STR_LEN 80
char str[STR_LEN+1];
解釋:
- 由于字符串在結(jié)尾處需要一個(gè)空字符
null
事甜,則需聲明一個(gè)長(zhǎng)度為81的字符數(shù)組。 - 聲明一個(gè)字符數(shù)組的長(zhǎng)度為
STR_LEN+1
滔韵,并不意味著該字符數(shù)組包含一個(gè)長(zhǎng)度為STR_LEN
的字符逻谦; - 一個(gè)字符串的長(zhǎng)度取決于空字符
null
的位置,而不是存儲(chǔ)字符串的數(shù)組的大信泸摺邦马; - 一個(gè)長(zhǎng)度為
STR_LEN+1
的字符數(shù)組可持有的字符串的長(zhǎng)度的范圍是0到STR_LEN
,對(duì)應(yīng)的是空字符串到長(zhǎng)度為STR_LEN
的字符串宴卖;
13.2.1節(jié) 字符串變量初始化
- 初始化式的長(zhǎng)度+1等于字符數(shù)組長(zhǎng)度:正常存儲(chǔ)滋将,以空字符
null
結(jié)束 - 初始化式的長(zhǎng)度小于字符數(shù)組長(zhǎng)度:以空字符
null
結(jié)束,并在末尾處補(bǔ)\0
- 初始化式的長(zhǎng)度大于字符數(shù)組長(zhǎng)度:由于沒給空字符
null
預(yù)留位置症昏,則忽略空字符null
- 如果初始化式的長(zhǎng)度太長(zhǎng)的話随闽,就省略一個(gè)字符串變量的長(zhǎng)度,因?yàn)閿?shù)長(zhǎng)度很容易出錯(cuò)
例1 初始化式的長(zhǎng)度+1等于字符數(shù)組長(zhǎng)度
char date1[8] = "June 14";
例2 初始化式的長(zhǎng)度小于字符數(shù)組長(zhǎng)度
char date2[9] = "June 14";
例3 初始化式的長(zhǎng)度大于字符數(shù)組長(zhǎng)度
char date3[7] = "June 14";
例4 省略字符串?dāng)?shù)組的長(zhǎng)度
char date4[] = "June 14"
解釋:C編譯器會(huì)自動(dòng)計(jì)算長(zhǎng)度肝谭,為date4
分配8個(gè)字符的空間掘宪,夠存儲(chǔ)在字符串"June 14"
里的字符,加1個(gè)空字符null
分苇。
13.3.2節(jié) 字符數(shù)組 VS 字符指針
例1 比較字符和數(shù)組和字符指針
char date[] = "June 14";
char *date = "June 14";
字符數(shù)組和字符指針的不同之處:
- 在數(shù)組版本里添诉,存儲(chǔ)在
date
里的字符是可被修改的。在指針版本里医寿,date
指向的是一個(gè)字符串字面量栏赴,不應(yīng)該被修改。 - 在數(shù)組版本里靖秩,
date
是一個(gè)數(shù)組名须眷。在指針版本里竖瘾,date
是一個(gè)變量,該變量在程序執(zhí)行過程中可指向其他字符串花颗。
如果我們需要一個(gè)字符串可被修改捕传,則有3種方法:
方法一:創(chuàng)建一個(gè)字符數(shù)組來存儲(chǔ)該字符串。
方法二:聲明一個(gè)字符指針p
扩劝,并使p
指向一個(gè)字符數(shù)組庸论。比如:
char str[STR_LEN+1], *p;
p = str;
方法三:聲明一個(gè)字符數(shù)組,使其之下寧一個(gè)動(dòng)態(tài)分配字符串棒呛。
注意:
永遠(yuǎn)不要將一個(gè)未初始化的指針變量作為字符串使用聂示。
例1 錯(cuò)誤示例
char *p;
//由于指針p沒有被初始化,所以如下對(duì)p的操作會(huì)到導(dǎo)致未定義的行為
p[0] = 'a';
p[1] = 'b';
p[2] = 'c';
p[3] = '\0';
13.3節(jié) 字符串的讀寫
寫字符串
-
printf
函數(shù)和puts
函數(shù)
讀字符串
- 在一步內(nèi)讀取一個(gè)字符串:
scanf
函數(shù)和gets函數(shù)
- 一次讀取字符串里的一個(gè)字符
13.3.1節(jié) 使用printf
和puts
來寫字符串
printf
函數(shù)是如何輸出字符串的簇秒?
-
printf
函數(shù)會(huì)逐個(gè)輸出字符串里的字符鱼喉,直到遇見空字符null
為止。 - 如果該字符串缺失空字符
null
趋观,則printf
將會(huì)越過該字符串的末尾扛禽,直到在內(nèi)存的某處找到空字符null
為止。
puts
函數(shù)是如何寫字符串的皱坛?
- 格式:
puts(str)
编曼; - 在寫完字符串后,
puts
函數(shù)會(huì)額外添加一個(gè)換行符麸恍;
轉(zhuǎn)換說明%s
:輸出整個(gè)字符串灵巧;
%.ps
:輸出字符串的前p個(gè)字符;
%ms
:在大小為m的域內(nèi)顯式字符串
- 如果字符串的長(zhǎng)度大于m抹沪,則會(huì)輸出整個(gè)字符串刻肄;
- 如果字符串的長(zhǎng)度小于m,則會(huì)右對(duì)齊輸出字符串融欧;
%-ms
:在大小為m的域內(nèi)顯式字符串
- 如果字符串的長(zhǎng)度大于m敏弃,則會(huì)輸出整個(gè)字符串;
- 如果字符串的長(zhǎng)度小于m噪馏,則會(huì)左對(duì)齊輸出字符串麦到;
%m.ps
:在大小為m的域中輸出字符串的前p個(gè)字符
例1 使用%s
來寫字符串
#include <stdio.h>
int main(void)
{
char str[] = "Are we having fun yet?";
printf("printf函數(shù)效果展示\n");
printf("%s\n", str);
printf("%.6s\n", str);
printf("%10s\n", str);
printf("%24s\n", str);
printf("%-24s\n", str);
printf("%24.6s\n", str);
printf("%-24.6s\n", str);
puts("put函數(shù)效果展示");
puts(str);
return 0;
}
輸出
printf函數(shù)效果展示
Are we having fun yet?
Are we
Are we having fun yet?
Are we having fun yet?
Are we having fun yet?
Are we
Are we
put函數(shù)效果展示
Are we having fun yet?
13.3.2節(jié) 使用scanf
和gets
函數(shù)來讀字符串
轉(zhuǎn)換說明%s
允許scanf
將字符串讀入字符數(shù)組。
scanf
是如何讀字符串的欠肾?
- 當(dāng)調(diào)用
scanf
時(shí)瓶颠,scanf
會(huì)跳過空白字符,然后逐個(gè)將字符讀入字符數(shù)組str
刺桃,直到遇見空白字符為止粹淋。 -
scanf
通常會(huì)在字符串的末尾存儲(chǔ)一個(gè)空字符null
。 - 用
scanf
讀取的字符串永遠(yuǎn)不會(huì)包含空白符。 - 通常不使用
scanf
來讀取一整行的輸入桃移,因?yàn)閾Q行符屋匕、空格符、tab符會(huì)使scanf
停止讀入借杰。
gets
函數(shù)是如何讀字符串的过吻?
-
gets
函數(shù)將輸入字符讀入到一個(gè)字符數(shù)組中,然后在末尾存儲(chǔ)一個(gè)空字符null
蔗衡。
gets
函數(shù)和scanf
函數(shù)有兩點(diǎn)不同:
-
gets
函數(shù)在開始讀字符串之前不會(huì)跳過空白符纤虽,而scanf
會(huì)跳過; -
gets
函數(shù)會(huì)逐個(gè)讀入字符直到發(fā)現(xiàn)一個(gè)換行符停止绞惦,然后忽略換行符廓推,不把換行符存儲(chǔ)到數(shù)組中,取而代之的是存儲(chǔ)一個(gè)空字符null
翩隧。而scanf
會(huì)在任何一個(gè)空白符處停止。
例2 比較scanf
函數(shù)和gets
函數(shù)
#include <stdio.h>
#define SENT_LEN 80
//To C, or not to C: that is the question
int main(void)
{
char sentence[SENT_LEN+1];
printf("Enter a sentence:\n");
//scanf將sentence當(dāng)做是一個(gè)指針呻纹,因?yàn)閟entence是一個(gè)數(shù)組名
scanf("%s", sentence);
printf("%s\n", sentence);
return 0;
}
輸出
Enter a sentence:
To C, or not to C: that is the question.
To
輸入
#include <stdio.h>
#define SENT_LEN 80
//To C, or not to C: that is the question
int main(void)
{
char sentence[SENT_LEN+1];
printf("Enter a sentence:\n");
gets(sentence);
printf("%s\n", sentence);
return 0;
}
輸出
Enter a sentence:
warning: this program uses gets(), which is unsafe.
To C, or not to C: that is the question.
To C, or not to C: that is the question.
注意:
- 使用
scanf
和gets
來將字符串讀入數(shù)組時(shí)堆生,scanf
和gets
是沒法檢測(cè)該數(shù)組是否已經(jīng)滿了的。因此雷酪,scanf
和gets
可能越過字符數(shù)組的末尾來存儲(chǔ)字符淑仆,從而導(dǎo)致未定義的行為。 - 通過在
scanf
中使用%ns
可使得scanf
更安全哥力。 - 一般使用
fgets
替代使用gets
蔗怠。
13.3.3節(jié) 逐個(gè)字符地讀取字符串
標(biāo)準(zhǔn)庫(kù)函數(shù)沒有提供這個(gè)功能,需要C程序員手動(dòng)編寫輸入函數(shù)來實(shí)現(xiàn)吩跋。
在設(shè)計(jì)逐個(gè)字符讀入字符串時(shí)寞射,需要考慮3個(gè)基本問題:
- 在開始讀字符串前是否要跳過空白符?
- 哪些字符會(huì)導(dǎo)致讀停止:換行符還是任何一個(gè)空白符锌钮?該字符是被存儲(chǔ)在字符串中還是丟掉桥温?
- 如果輸入字符串過于長(zhǎng)該怎么辦:丟掉額外的字符還是留給下一次輸入?
例1 實(shí)現(xiàn)read_line
函數(shù)
目標(biāo):
- 不跳過空白字符梁丘;
- 在第一個(gè)換行符處停止侵浸,但不存儲(chǔ)該換行符;
- 丟掉額外的字符氛谜;
函數(shù)原型
int read_line(char str[], int n);
函數(shù)定義
int read_line(char str[], int n)
{
int ch, i=0;
while((ch = getchar()) != '\n'){
if (i<n){
str[i++] = ch;
}
}
str[i] = '\0';
return i;
}
13.4節(jié) 如何訪問字符串中的字符掏觉?
由于字符串是存儲(chǔ)在字符數(shù)組中的,所以可以使用下標(biāo)來訪問字符串中的字符值漫。
例1 統(tǒng)計(jì)在字符串中的空白符的個(gè)數(shù)
下標(biāo)版本:
int count_spaces(const char s[])
{
int count=0, i;
for(i=0; s[i] != '\0';i++){
if (s[i] == ' ') {
count++;
}
}
return count;
}
指針版本:
int count_spaces(char *s)
{
int count=0;
for(;*s != '\0';s++){
if (*s == ' ') {
count++;
}
}
return count;
}
編寫字符串函數(shù)時(shí)需要思考3個(gè)基本問題:
- 訪問在字符串中的字符澳腹,是用數(shù)組操作更好,還是用指針操作更好?
都可以遵湖。
C程序員更傾向于使用指針來處理字符串悔政。 - 字符串參數(shù)是該聲明為數(shù)組還是指針?
沒有區(qū)別延旧,因?yàn)榫幾g器會(huì)將數(shù)組聲明當(dāng)做指針處理谋国。 - 形式參數(shù)的格式(
s[]
或者*s
)會(huì)對(duì)實(shí)際參數(shù)產(chǎn)生影響嗎?
不會(huì)迁沫,因?yàn)檎{(diào)用count_space
函數(shù)時(shí)芦瘾,可以傳遞數(shù)組名、指針變量集畅、字符串字面量作為實(shí)際參數(shù)近弟。
13.5節(jié) 如何使用C語言的字符串庫(kù)?
- 在C語言中挺智,不能使用運(yùn)算符來處理字符串祷愉,因?yàn)樽址划?dāng)做數(shù)組,對(duì)數(shù)組操作的限制同樣適用于字符串赦颇,比如不能使用運(yùn)算符來拷貝和比較字符串等二鳄。
- 在C語言中可使用字符串庫(kù)
<string.h>
中提供的函數(shù)來對(duì)字符串進(jìn)行操作。 - 進(jìn)行字符串操作的程序需要使用預(yù)處理指令
#include <string.h>
媒怯。
例1 不能使用運(yùn)算符來拷貝和比較字符串
//錯(cuò)誤示例1
char str1[10], str2[10];
//數(shù)組名不能用作賦值運(yùn)算符的左操作數(shù)
str1 = "abc";
str2 = str1;
// 來自于12.3節(jié)的錯(cuò)誤示例2
//可以使用數(shù)組名作為指針订讼,但不能給數(shù)組名賦新的值
while(*a != 0){
a++;
}
// 來自于12.3節(jié)的正確示例1
//使用數(shù)組名作為指針,將a拷貝給一個(gè)指針變量p扇苞,然后對(duì)指針p進(jìn)行修改
p = a;
while (*p != 0) {
p++;
}
//正確示例2
//可在聲明語句中使用等號(hào)來給數(shù)組初始化
char str1[10] = "abc";
13.5.1 字符串拷貝函數(shù)strcpy
和strncpy
函數(shù)strcpy
原型:
char *strcpy(char *s1, const char *s2);
使用函數(shù)strcpy
的注意事項(xiàng):
- 在調(diào)用
strcpy(str1, str2)
時(shí)欺殿,函數(shù)strcpy
是沒法檢測(cè)str2
指向的字符串跟str1
指向的字符串的長(zhǎng)度是否匹配的。 - 如果
str1
指向的是長(zhǎng)度為n的字符串鳖敷,str2
指向的是長(zhǎng)度不超過n-1的字符串脖苏,則拷貝就會(huì)成功。 - 如果
str1
指向的是長(zhǎng)度為n的字符串定踱,str2
指向的是長(zhǎng)度大于n的字符串帆阳,則拷貝就會(huì)導(dǎo)致未定義的行為。因?yàn)楹瘮?shù)strcpy
會(huì)一直拷貝直到遇見一個(gè)空字符null
停止屋吨,所以此時(shí)函數(shù)strcpy
會(huì)越過str1
指向的數(shù)組的末尾蜒谤。
例1 拷貝字符串
#include <stdio.h>
#include <string.h>
int main(void)
{
char str1[10], str2[10];
strcpy(str2, "abc");
printf("%s\n", str2);
strcpy(str1, str2);
printf("%s\n", str1);
return 0;
}
函數(shù)strncpy
原型:
char * strncpy(char *s1, const char *s2, int n);
函數(shù)strncpy
調(diào)用:
strncpy(str1, str2, sizeof(str1));
函數(shù)strncpy
使用注意事項(xiàng):
- 只要
str1
足夠大可以持有str2
指向的字符串(包括空字符null
),則拷貝就會(huì)成功至扰。 - 如果
str2
指向的字符串的長(zhǎng)度大于等于str1
數(shù)組鳍徽,則函數(shù)strncpy
會(huì)使str1
指向的字符串保存時(shí)沒有以空字符null
結(jié)尾。
例2 拷貝字符串
#include <stdio.h>
#include <string.h>
int main(void)
{
char str1[8], str2[12];
strncpy(str2, "hello world", sizeof(str2)-1);
str2[sizeof(str2) - 1] = '\0';
printf("%s\n", str2);
strncpy(str1, str2, sizeof(str1));
str1[sizeof(str1)-1] = '\0';
printf("%s\n", str1);
return 0;
}
輸出
hello world
hello w
13.5.2節(jié) 函數(shù)strlen
函數(shù)原型:
size_t strlen(const char *s);
解釋:
- 類型
size_t
是在C語言庫(kù)中定義的一種typedef
別名敢课,表示的是一類無符號(hào)整數(shù)阶祭。 - 函數(shù)
strlen
的返回的是字符串s
的長(zhǎng)度:在s
中的第一個(gè)空字符null
之前的所有字符的數(shù)量绷杜。
例3 輸出字符串的長(zhǎng)度
#include <stdio.h>
#include <string.h>
int main(void)
{
int len;
char str1[10];
len = strlen("abc");
printf("%d\n", len);
len = strlen("");
printf("%d\n", len);
strcpy(str1, "abc");
len = strlen(str1);
printf("%d\n", len);
return 0;
}
輸出
3
0
3
函數(shù)strcat
函數(shù)原型:
char *strcat(char *s1, char *s2);
功能:
函數(shù)strcat
將字符串s2
的內(nèi)容添加到字符串s1
的末尾。
返回值
函數(shù)strcat
返回s1
濒募,是一個(gè)指向結(jié)果字符串的指針鞭盟。
例1 字符串拼接
#include <stdio.h>
#include <string.h>
int main(void)
{
char str1[10];
char str2[10];
//字符串字面量的拼接
//strcpy(str1, "abc");
//strcat(str1, "def");
//printf("%s\n",str1);
//字符串變量的拼接
strcpy(str1, "abc");
strcpy(str2, "def");
strcat(str1, str2);
printf("%s\n", str1);
return 0;
}
使用strcat
有幾個(gè)注意事項(xiàng):
- 如果
str1
指向的數(shù)組的長(zhǎng)度不夠容納來自str2
的額外的字符時(shí),則調(diào)用strcat(str1, str2)
的結(jié)果是未定義的瑰剃。
例2 比較安全地使用字符串拼接:使用函數(shù)strncat
strncat(str1, str2, sizeof(str1) - strlen(str1) - 1);
解釋:
- 函數(shù)
strncat
會(huì)讓str1
以空字符nul
結(jié)尾齿诉,該空字符不包含在第三個(gè)參數(shù)中。 - 第三個(gè)參數(shù)首先計(jì)算在
str1
中的剩余空間晌姚,然后再減一預(yù)留出位置來存儲(chǔ)空字符null
粤剧。
函數(shù)strcmp
函數(shù)原型
int strcmp(const *s1, const *s2);
功能
函數(shù)strcmp比較的是字符串s1和s2的內(nèi)容。
如果字符串s1小于字符串s2挥唠,則返回小于0的值抵恋;
如果字符串s1等于字符串s2,則返回0宝磨;
如果字符串s1大于字符串s2弧关,則返回大于0的值;
函數(shù)strcmp比較字符串大小的規(guī)則:
- 函數(shù)strcmp比較字符串的字符時(shí),實(shí)際上比較的是表示該字符的數(shù)值碼唤锉;
- 如果s1和s2的前i個(gè)字符都是相同的梯醒,但是s1的第(i+1)個(gè)字符小于s2的第(i+1)個(gè)字符,則s1小于s2腌紧。比如"abc"小于"bcd","abd"小于"abe"畜隶。
- 如果s1的所有字符都跟s2匹配壁肋,但s1的長(zhǎng)度小于s2,則s1小于s2籽慢。比如"abc"小于"abcd"浸遗。
有關(guān)ASCII字符集的相關(guān)規(guī)律:
- 字符序列A-Z、a-z箱亿、0-9都有連續(xù)的數(shù)值碼跛锌。
- 所有的大寫字母都小于小寫字母。大寫字母的范圍是65-90届惋,小寫字母的范圍是97-122髓帽。
- 數(shù)字小于字母。數(shù)字的范圍是48-57脑豹。
- 空格符小于所有打印字符郑藏。空格符的數(shù)值碼是32瘩欺。
示例程序:輸出一個(gè)月的提醒列表
輸出示例:
Enter day and reminder: 24 Susan's birthday
Enter day and reminder: 5 6:00 - Dinner with Marge and Russ
Enter day and reminder: 26 Movie - "Chinatown"
Enter day and reminder: 7 10:30 - Dental appointment
Enter day and reminder: 12 Movie - "Dazed and Confused"
Enter day and reminder: 5 Saturday class
Enter day and reminder: 12 Saturday class
Enter day and reminder: 0
Day Reminder
5 Saturday class
5 6:00 - Dinner with Marge and Russ
7 10:30 - Dental appointment
12 Saturday class
12 Movie - "Dazed and Confused"
24 Susan's birthday
26 Movie - "Chinatown"
源文件remind.c
#include <stdio.h>
#include <string.h>
#define MAX_REMIND 50
#define MSG_LEN 60
int read_line(char str[], int n);
// 輸出一個(gè)月的提醒列表
// 輸出示例:
// Enter day and reminder: 24 Susan's birthday
// Enter day and reminder: 5 6:00 - Dinner with Marge and Russ
// Enter day and reminder: 26 Movie - "Chinatown"
// Enter day and reminder: 7 10:30 - Dental appointment
// Enter day and reminder: 12 Movie - "Dazed and Confused"
// Enter day and reminder: 5 Saturday class
// Enter day and reminder: 12 Saturday class
// Enter day and reminder: 0
// Day Reminder
// 5 Saturday class
// 5 6:00 - Dinner with Marge and Russ
// 7 10:30 - Dental appointment
// 12 Saturday class
// 12 Movie - "Dazed and Confused"
// 24 Susan's birthday
// 26 Movie - "Chinatown"
int main(void)
{
char reminders[MAX_REMIND][MSG_LEN+3];
char day_str[3], msg_str[MSG_LEN+1];
int day, i, j, num_remind = 0;
for(;;){
if (num_remind == MAX_REMIND) {
printf("-- No space left --\n");
break;
}
printf("Enter day and reminder: ");
scanf("%2d", &day);
if (day == 0) {
break;
}
sprintf(day_str, "%2d", day);
read_line(msg_str, MSG_LEN);
//i表示要插入的提醒的位置
//為新輸入的提醒查找到合適的插入位置必盖,將原位置的所有提醒統(tǒng)一向后移動(dòng)一位
for(i=0;i<num_remind;i++){
if(strcmp(day_str, reminders[i]) < 0){
break;
}
}
for(j=num_remind;j>i;j--){
strcpy(reminders[j], reminders[j-1]);
}
strcpy(reminders[i], day_str);
strcat(reminders[i], msg_str);
num_remind++;
}
printf("\nDay Reminder\n");
for(i=0;i<num_remind;i++){
printf(" %s\n", reminders[i]);
}
return 0;
}
int read_line(char str[], int n)
{
int ch, i=0;
while((ch = getchar()) != '\n'){
if(i<n){
str[i++] = ch;
}
}
str[i] = '\0';
return i;
}