第13章 字符串

英文原版: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)在scanfprintf的調(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)度為n的字符串字面量時(shí)涉馁,它會(huì)為該字符串分配大小為n+1個(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";
初始化式的長(zhǎng)度+1=字符數(shù)組長(zhǎng)度.png

例2 初始化式的長(zhǎng)度小于字符數(shù)組長(zhǎng)度

char date2[9] = "June 14";
初始化式的長(zhǎng)度<字符數(shù)組長(zhǎng)度.png

例3 初始化式的長(zhǎng)度大于字符數(shù)組長(zhǎng)度

char date3[7] = "June 14";
初始化式的長(zhǎng)度>字符數(shù)組長(zhǎng)度.png

例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é) 使用printfputs來寫字符串

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é) 使用scanfgets函數(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.

注意:

  • 使用scanfgets來將字符串讀入數(shù)組時(shí)堆生,scanfgets是沒法檢測(cè)該數(shù)組是否已經(jīng)滿了的。因此雷酪,scanfgets可能越過字符數(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è)基本問題:

  1. 在開始讀字符串前是否要跳過空白符?
  2. 哪些字符會(huì)導(dǎo)致讀停止:換行符還是任何一個(gè)空白符锌钮?該字符是被存儲(chǔ)在字符串中還是丟掉桥温?
  3. 如果輸入字符串過于長(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è)基本問題:

  1. 訪問在字符串中的字符澳腹,是用數(shù)組操作更好,還是用指針操作更好?
    都可以遵湖。
    C程序員更傾向于使用指針來處理字符串悔政。
  2. 字符串參數(shù)是該聲明為數(shù)組還是指針?
    沒有區(qū)別延旧,因?yàn)榫幾g器會(huì)將數(shù)組聲明當(dāng)做指針處理谋国。
  3. 形式參數(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ù)strcpystrncpy

函數(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;
}

13.6節(jié) 字符串慣用法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拌牲,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子歌粥,更是在濱河造成了極大的恐慌塌忽,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,126評(píng)論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件失驶,死亡現(xiàn)場(chǎng)離奇詭異土居,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門胡陪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來施符,“玉大人,你說我怎么就攤上這事埂奈。” “怎么了定躏?”我有些...
    開封第一講書人閱讀 169,941評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵账磺,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我痊远,道長(zhǎng)垮抗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,294評(píng)論 1 300
  • 正文 為了忘掉前任碧聪,我火速辦了婚禮冒版,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逞姿。我一直安慰自己辞嗡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,295評(píng)論 6 398
  • 文/花漫 我一把揭開白布滞造。 她就那樣靜靜地躺著续室,像睡著了一般。 火紅的嫁衣襯著肌膚如雪谒养。 梳的紋絲不亂的頭發(fā)上挺狰,一...
    開封第一講書人閱讀 52,874評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音买窟,去河邊找鬼丰泊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛始绍,可吹牛的內(nèi)容都是我干的趁耗。 我是一名探鬼主播,決...
    沈念sama閱讀 41,285評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼疆虚,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼苛败!你這毒婦竟也來了满葛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,249評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤罢屈,失蹤者是張志新(化名)和其女友劉穎嘀韧,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缠捌,經(jīng)...
    沈念sama閱讀 46,760評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锄贷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,840評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了曼月。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谊却。...
    茶點(diǎn)故事閱讀 40,973評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖哑芹,靈堂內(nèi)的尸體忽然破棺而出炎辨,到底是詐尸還是另有隱情,我是刑警寧澤聪姿,帶...
    沈念sama閱讀 36,631評(píng)論 5 351
  • 正文 年R本政府宣布碴萧,位于F島的核電站,受9級(jí)特大地震影響末购,放射性物質(zhì)發(fā)生泄漏破喻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,315評(píng)論 3 336
  • 文/蒙蒙 一盟榴、第九天 我趴在偏房一處隱蔽的房頂上張望曹质。 院中可真熱鬧,春花似錦擎场、人聲如沸羽德。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至银觅,卻和暖如春礼饱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背究驴。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評(píng)論 1 275
  • 我被黑心中介騙來泰國(guó)打工镊绪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人洒忧。 一個(gè)月前我還...
    沈念sama閱讀 49,431評(píng)論 3 379
  • 正文 我出身青樓蝴韭,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親熙侍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子榄鉴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,982評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容