C語言 字符數組 和 字符串 詳解

C語言 字符數組 和 字符串 詳解

用來存放字符的數組稱爲字符數組,例如:

char a[10];  //一維字符數組
char b[5][10];  //二維字符數組
char c[20]={'c', '  ', 'p', 'r', 'o', 'g', 'r', 'a','m'};  // 給部分數組元素賦值
char d[]={'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a', 'm' };  //對全體元素賦值時可以省去長度

字符數組實際上是一系列字符的集合,也就是字符串(String)。在C語言中,沒有專門的字符串變量,通常就用一個字符數組來存放一個字符串。

C語言規定,可以將字符串直接賦值給字符數組,例如:

char str[30] = {"c.biancheng.net"};
char str[30] = "c.biancheng.net";  //這種形式更加簡潔,實際開發中常用
char str[] = {"c.biancheng.net"};
char str[] = "c.biancheng.net";  //這種形式更加簡潔,實際開發中常用

這裏需要留意一個,字符數組只有在定義時才能將整個字符串一次性地賦值給它,一旦定義完了,就只能一個字符一個字符地賦值了。請看下面的例子:

char str[7];
str = "abc123";  //錯誤
//正確如下
str[0] = 'a'; str[1] = 'b'; str[2] = 'c';
str[3] = '1'; str[4] = '2'; str[5] = '3';

字符串結束標誌

在C語言中,字符串總是以’\0’作爲結尾,所以’\0’也被稱爲字符串結束符。 '\0'是 ASCII 碼錶中的第 0 個字符,稱爲“空字符”。該字符既不能顯示,也沒有控制功能,它在C語言中唯一的作用就是作爲字符串結束標誌。

C語言在處理字符串時,會從前往後逐個掃描字符,一旦遇到'\0'就認爲到達了字符串的末尾,就結束處理。
" "包圍的字符串會自動在末尾添加'\0'。例如,"abc123"從表面看起來只包含了 6 個字符,其實C語言會在最後隱式地添加一個’\0’

需要注意,逐個字符地給數組賦值並不會自動添加’\0’,例如:

char str[] = {'a', 'b', 'c'};//數組 str 的長度爲 3,而不是 4,因爲最後沒有'\0'。

當用字符數組存儲字符串時,要爲’\0’留個位置;這意味着,字符數組的長度至少要比字符串的長度大 1。請看下面的例子:

char str[7] = "abc123";

而且,當字符串長度大於數組長度時,有些較老或者不嚴格的編譯器並不會報錯。

有些時候,程序的邏輯要求我們必須逐個字符地爲數組賦值,這個時候就很容易遺忘字符串結束標誌’\0’。下面的代碼中,我們將 26 個大寫英文字符存入字符數組,並以字符串的形式輸出:

#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;
}

在 VS下的運行結果:

ABCDEFGHIJKLMNOPQRSTUVWXYZ口口口口i口口0 ?//口表示無法顯示的特殊字符。

在很多編譯器下,局部變量的初始值是隨機的,而不是我們通常認爲的“零”值。局部數組(在函數內部定義的數組,本例中的 str 數組就是在 main() 函數內部定義的)也有這個問題,很多編譯器並不會把局部數組的內存都初始化爲“零”值,而是放任不管,所以它們的值也是沒有意義的。

函數內部定義的變量數組結構體共用體等都稱爲局部數據。在很多編譯器下,局部數據的初始值都是隨機的,這一點非常重要,一定要謹記,否則後面會遇到很多奇葩的錯誤。

本例中的 str 數組在定義完成以後並沒有立即初始化,所以它所包含的元素的值都是隨機的,只有很小的概率會是“零”值。循環結束以後,str 的前 26 個元素被賦值了,剩下的 4 個元素的值依然是隨機的。

printf() 輸出字符串時,會從第 0 個元素開始往後檢索,直到遇見’\0’才停止,然後把’\0’前面的字符全部輸出。本例中我們使用 printf() 輸出 str,按理說到了第 26 個元素就能檢索到’\0’,然而事實卻不是這樣,由於我們並未對最後 4 個元素賦值,所以第 26 個元素不是’\0’,第 27 個也不是,第 28 個也不是……可能到了第 50 個元素才遇到’\0’,printf() 把這 50 個字符全部輸出出來,就是上面的樣子,多出來的字符毫無意義,甚至不能顯示。

數組總共才 30 個元素,到了第 50 個元素不早就超出數組範圍了嗎?
是的,的確超出範圍了!然而,數組後面依然有其它的數據,printf() 也會將這些數據作爲字符串輸出。

要想避免這些問題也很容易,在字符串的最後手動添加’\0’即可。修改上面的代碼,在循環結束後添加’\0’:

#include <stdio.h>
int main(){
    char str[30];
    char c;
    int i;
    for(c=65,i=0; c<=90; c++,i++)
        str[i] = c;
    str[i] = 0;  //此處爲添加的代碼,也可以寫作 str[i] = '\0';
    printf("%s\n", str);
    return 0;
}

更加專業的做法是將數組的所有元素都初始化爲“零”值。再次修改上面的代碼:

#include <stdio.h>
int main(){
    char str[30] = {0};  //將所有元素都初始化爲 0,或者說 '\0'
    char c;
    int i;
    for(c=65,i=0; c<=90; c++,i++)
        str[i] = c;
    printf("%s\n", str);
    return 0;
}

字符串長度

所謂字符串長度,就是字符串包含了多少個字符(不包括最後的結束符’\0’)。例如"abc"的長度是 3,而不是 4。

在C語言中,我們使用string.h頭文件中的 strlen() 函數來求字符串的長度,它的用法爲:

length strlen(strname);
strname 是字符串的名字,或者字符數組的名字;length 是使用 strlen() 後得到的字符串長度,是一個整數。

下面是一個完整的例子:

#include <stdio.h>
#include <string.h>  //記得引入該頭文件
int main(){
    char str[] = "http://c.biancheng.net/c/";
    long len = strlen(str);
     printf("The lenth of the string is %ld.\n", len);
     return 0;
    }

運行結果:

The lenth of the string is 25.

字符串的輸出

在C語言中,有兩個函數可以在控制檯(顯示器)上輸出字符串,它們分別是:

puts():輸出字符串並自動換行,該函數只能輸出字符串。
printf():通過格式控制符%s輸出字符串,不能自動換行。

這裏再演示一下,請看下面的代碼:

#include <stdio.h>
int main(){
    char str[] = "http://c.biancheng.net";
    printf("%s\n", str);  //通過字符串名字輸出
    printf("%s\n", "http://c.biancheng.net");  //直接輸出
    puts(str);  //通過字符串名字輸出
    puts("http://c.biancheng.net");  //直接輸出
    return 0;
}

運行結果:

http://c.biancheng.net
http://c.biancheng.net
http://c.biancheng.net
http://c.biancheng.net

注意:輸出字符串時只需要給出名字,不能帶後邊的[ ],例如,下面的兩種寫法都是錯誤的:

printf("%s\n", str[]);
puts(str[10]);

字符串的輸入

在C語言中,有兩個函數可以讓用戶從鍵盤上輸入字符串,它們分別是:

scanf():通過格式控制符%s輸入字符串。
gets():直接輸入字符串,並且只能輸入字符串。

區別:
scanf() 讀取字符串時以空格爲分隔,遇到空格就認爲當前字符串結束了,所以無法讀取含有空格的字符串。
gets() 認爲空格也是字符串的一部分,只有遇到回車鍵時才認爲字符串輸入結束。

請看下面的例子:

   #include <stdio.h>
    int main(){
        char str1[30] = {0};
        char str2[30] = {0};
        char str3[30] = {0};
        //gets() 用法
        printf("Input a string: ");
        gets(str1);
        //scanf() 用法
        printf("Input a string: ");
        scanf("%s", str2);
        scanf("%s", str3);
       printf("\nstr1: %s\n", str1);
        printf("str2: %s\n", str2);
        printf("str3: %s\n", str3);
        return 0;
    }

運行結果:

//輸入
Input a string: C C++ Java Python↙
Input a string: PHP JavaScript↙
//輸出
str1: C C++ Java Python
str2: PHP
str3: JavaScript

第一次輸入的字符串被 gets() 全部讀取,並存入 str1 中。
第二次輸入的字符串,前半部分被第一個 scanf() 讀取並存入 str2 中,後半部分被第二個 scanf() 讀取並存入 str3 中。

注意:scanf() 在讀取數據時需要的是數據的地址,這一點是恆定不變的,所以對於 intcharfloat 等類型的變量都要在前邊添加&以獲取它們的地址。但是因爲字符串名字或者數組名字在使用的過程中一般都會轉換爲地址,所以再添加&就是多此一舉了。

就目前學到的知識而言,int、char、float 等類型的變量用於 scanf() 時都要在前面添加&,而數組或者字符串用於 scanf() 時不用添加&,

其實scanf()也有一些高級的用法,可以讀入空白字符,如果有興趣可以瞭解一下scanf的高級用法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章