1. 程式人生 > >C Primer Plus (第五版)中文版——第 11 章 字串和字串函式

C Primer Plus (第五版)中文版——第 11 章 字串和字串函式

11.1  字串表示和字串 I/O

11.1  在程式中定義字串

一、字串常量

字串常量(string constant)又稱字串文字(string literal),是指位於一對雙引號中的任何字元。字串常量屬於靜態儲存類。

可以用 #define 來定義字串常量,也可以直接作為函式 printf() 或 puts() 的引數。

#define HMG "Hello,Mr.Gold!"
printf("Hello,Mr.Gold!");
puts("Hello,Mr.Gold!");
  • 如果字串文字中間沒有間隔或間隔的是空格符,ANSI C 將會將其串聯起來。以下兩條語句等效:
char greeting[20] = "Hello," "Mr"".Gold!";
char greeting[20] = "Hello, Mr.Gold!";
  • 如果想在字串中使用雙引號,可以在雙引號前加一個反斜線符號(轉義序列)。

整個引號中的內容作為指向該字串儲存位置的指標。

printf("%s, %p, %c", "How", "are", *"you?");    //輸出為 How, 0x0040c010, y 

二、字元陣列及其初始化

const char try[20] = "Just have a try!";    //const表示這個字串不可修改

雙引號裡的字元加上編譯器自動提供的結束標誌 \0 字元,作為一個字串儲存在記憶體裡。字元陣列名也是陣列首元素的地址:

try == &try[0];    *try == try[0] == 'J';    *(try + 1) == try[1] == 'u';    //不考慮語法問題,三個關係式成立

指定陣列大小時,確保陣列元素比字串長度至少多1,用於存放空字元 \0。未被初始化的元素全都被自動初始化為空字元 \0。

初始化字元陣列時也可以省略陣列大小,由編譯器決定陣列大小。

const char try[] = "Just have a try!";

也可以使用指標符號建立字串:

const char *try = "Just have a try!";

 三、陣列與指標

陣列初始化是從靜態儲存區把一個字串複製給陣列,指標初始化只是複製字串的地址。

四、陣列和指標的差別

char heart[] = "Just have a try!";    //陣列名heart是常量,但陣列的元素是變數
char *head = "Just Have A Try!";      //指標head是變數,可以改變其指向的地址
putchar(heart[1]);     //都可以使用陣列符號
putchat(head[1]);
putchar(*(heart + 1)); //都可以使用指標加法
putchat(*(head + 1));
putchar(*(head++));    //只有指標可以使用增量運算子
head = heart;          //可以讓指標指向陣列,這樣指標就不再指向原來的字串
heart[5] = 'H';        //可以訪問單個的陣列元素
*(heart+5) = 'H';
const char * pt = "KingJames!";       //建議初始化一個指向字串常量的指標時使用const修飾符

五、字串陣列

const char *title[3] = {"Hello!","Mr.Gold","Just have a try!"};    //一個字串陣列

title 是由3個指向 char 的指標組成的陣列,即 title 是一維陣列, 包含3個元素,每個元素是一個 char 型別的地址。實際上,title 陣列並不存放字串,只存放字串的地址(字串存放在程式用來儲存常量的那部分記憶體中)。因此陣列的第一個元素 title[0] 指向第一個字串的第一個字元,即 *title[0] == 'H' 。由於陣列符號和指標的關係,也可以用 title[0][0] 表示第一個字串的第一個字元。這種方式不浪費任何儲存空間。

11.1.2  指標和字串

事實上,絕大多數的 C 字串操作使用的都是指標。

char * mesg = "Sunshine!";
char * copy;
cope = mesg;        //字串本身沒有被複制,而是將mesg的值(字串的地址)賦給copy,之後兩者指向同一地址
printf("%p, &p", mesg, copy);    //輸出兩個指標的值(即所指向的地址),輸出是一樣的,因為兩者指向同一個字串

11.2  字串輸入

11.2.1  建立儲存空間

最簡單的方法就是在宣告中明確指出陣列大小。另一種方法就是使用 C 庫裡分配儲存空間的函式。

11.2.2  gets() 函式

gets() 讀取字串直到遇到一個換行字元(\n),按回車鍵可以產生這個字元。它讀取換行符之前(不包括換行符)的所有字元,在這些字元後新增一個空字元(\0),然後把這個字串交給呼叫它的程式。

char name[81];
char * ptr;
ptr = gets(name);    //把字串放入name陣列中,並把字串的地址分配給ptr

如果一切順利,get() 返回讀入的字串的地址;如果出錯或遇到檔案結尾,它返回一個空地址,這個空地址稱為空指標 NULL。

while(gets(name) != NULL)    //既可檢查是否到了檔案尾,也可讀取一個值

11.2.3  fgets() 函式

gets() 函式的不足是它不檢查預留儲存區是否能夠容納實際輸入的資料。多出來的字元簡單地溢位到相鄰的記憶體區。fgets() 函式改進了這個問題,它讓您指定最大讀入字元數。fgets() 函式是為檔案 I/O 設計的,在處理鍵盤輸入時不如 gets() 方便。fgets() 與gets() 有三方面不同:

  • fgets() 需要第二個引數來說明最大讀入字元數。如果這個引數值為 n,fgets() 就會讀取最多 n-1 個字元或讀完一個換行符為止,以兩個條件中最先滿足的那個來結束輸入。
  • fgets() 讀到換行符,就會把它存到字串裡,而不是像 gets() 那樣丟棄它。
  • fgets() 還需要第三個引數來說明讀那個檔案。從鍵盤上讀資料時,可以使用 stdin 作為引數,這個識別符號在 stdio.h 中定義。

10.2.4  scanf() 函式

可以使用帶有 %s 格式的 scanf() 函式來讀入一個字串。scanf() 和 gets() 主要的區別在於它們如何決定字串合適結束。

  • scanf() 更傾向於獲取單詞(get word)而不是獲取字串(get string)。使用 %s 的scanf() 讀到(但不包括)下一個空白字元。
  • 若指定了欄位寬度,如 %10s,scanf() 就會讀入10個字元或直到遇到第一個空白字元,以兩個條件中最先滿足的那個來結束輸入。

11.3  字串輸出

11.3.1  puts() 函式

puts() 函式的使用很簡單,只需給出字串引數的地址。

char str1[20] = "You are my sunshine!";
consr char * str2 = "My only sunshine!";

puts(str1);
puts(str2);    //puts()顯示字串時自動在其後新增一個換行符

11.3.2  fputs() 函式

fputs() 函式是 puts() 函式的面向檔案版本。兩者之間的主要區別是:

  • fputs() 需要第二個引數來說明要寫的檔案。可以使用 stdout 作為引數來進行輸出顯示,stdout 在 stdio.h 中定義。
  • fputs() 並不為輸出自動新增換行符。

11.3.3  printf() 函式

printf() 不會自動在新行上輸出每一個字串。但 printf() 使在一行上輸出多個字串變得更為簡單。

11.4  自定義字串輸入 / 輸出函式

/*一個自定義字串處理函式——不新增換行符列印一個字串*/
#include <stdio.h>
void put1(const char * string)
{
        while(*string)
        {
                put1(*string++);    //先輸出string指向的值,再增加string本身
        }
}

11.5  字串函式

11.5.1  strlen() 函式

strlen() 函式以字元為單位給出字串的長度(不包括空字元)。

11.5.2  strcat() 函式

strcat() 函式接受兩個字串引數,它將第二個字串的一份拷貝新增到第一個字串的結尾(第二個字串的第一個字元會覆蓋第一個字串結尾處的空字元),從而使第一個字串成為一個新的組合字串,而第二個字串沒有改變。strcat() 函式是 char * 型別,它返回第一個引數的值。

11.5.3  strncat() 函式

strcat() 函式並不檢查第一個陣列能否容納第二個字串,如果沒有為第一個陣列分配足夠大的空間,多出來的字元溢位到相鄰儲存單元時就會出現問題。strncat() 函式需要另一個引數來指明最多允許新增的字元的數目。例如:

strncat(try, answer, 10);    
//把addon字串中的內容新增到bugs的結尾,直到加上10個字元或遇到空字元為止,以兩個條件中最先滿足的那個來終止新增過程

11.5.4  strcmp() 函式

strcmp() 函式比較兩個字串的內容 ,如果兩個字串引數相同,它就返回0。

while(strcmp(try, answer);    //比較兩個字串(只看try的第一個空白符之前的部分)

11.5.5  strncmp() 變種

strcmp() 函式比較兩個字串時,一直比較到找到不同的相應字元,搜尋可能要進行到字串結尾處。

strncmp() 函式比較兩個字串時,可以比較到字串不同處,也可以比較完由第三個引數指定的字元數。

strncmp(try, answer, 3);    //限定比較兩個字串的前3個字元

 

11.5.6  strcpy() 和 strncpy() 函式

strcpy() 函式可複製字串,它在字串運算中的作用等價於賦值運算子。strcpy() 函式接受兩個字串指標引數,指向源字串的第二個指標可以是一個已宣告的指標,陣列名或字串常量,指向目標字串的第一個指標應指向空間足夠容納該字串的資料物件,比如一個數組。

strcpy(try, answer);

一、strcpy() 的高階屬性

  • strcpy() 是 char * 型別,它返回的是第一個引數的值。
  • strcpy() 的第一個引數不必指向陣列的開始,這樣就可以只複製陣列的一部分。
  • strcpy() 從源字串複製空字元。

二、較為謹慎的選擇:strncpy()

strncpy() 需要第三個引數來指明最大可複製的字元數。

strcpy(try, answer, n);    //從answer把n個字元(或空字元之前的字元,由最先滿足的條件決定何時終止)複製到try

11.5.7  sprintf() 函式

sprintf() 函式作用和 printf() 一樣,但是它寫到字串裡而不是寫到輸出顯示。sprintf() 函式的第一個引數是目標字串的地址,其餘的引數和 printf() 函式一樣:一個轉換說明符,接著是要寫的專案的列表。

sprintf(try, "%s, %s\n", answer1, answer2);    //將兩個字串組合成一個單一的字串後存放在字串try中

11.7  ctype.h 字元函式和字串

ctype.h 字元函式不能被應用於整個字串,但可以應用於字串中的個別字元。例如將 toupper() 函式應用於一個字串的每個字元,就可以將整個字串轉換為大寫。

11.9  把字串轉換為數字

C 有一些函式專門用於把字串形式轉換為數字形式。