用字元數組和字元指標變數都能實現字串的儲存和運算,但是二者之間還是有很多區別,主要有一下幾點
1.字元數組由若干個元素組成,每個元素放一個字元,而字元指標變數中存放的是字串第一個元素的地址,絕不是將字串放到字元指標變數中。請看如下代碼:
char *a = "I love China!";char b[] = "You are a student.";printf("%4d%4d", sizeof a, sizeof b); //使用sizeof來確定a和b所佔的分配空間
得到的結果為4和19,足可以說明這一點。而這一點是導致很多編寫字串處理常式出現錯誤的根源。
2.賦值方式。可以對字元指標變數賦值,但是不能對數組名賦值。可以採用下面方法對字元指標變數賦值:
char *a; //a為字元指標變數a = "I love China!"; //將字串首元素地址賦給指標變數,合法。 //但賦給a的不是字串,二十字串首元素的地址
不能用一下方法對字元數組名賦值:
char str[14];str[0] = 'I'; //對字元數組元素賦值,合法str = "I love China!"; //數組名是地址,是常量,不能被賦值,非法
3.初始化的含義。對字元指標變數賦初值:
char *a = "I love China!" //定義字元指標變數a,並把字串第一個元素的地址賦給a
等價於
char *a; //定義字元指標變數aa = "I love China!"; //把字串第一個元素的地址賦給a
而對數組的初始化:
char str[] = "I love China!"; //定義字元數組str,並把字串賦給數組中各元素
不等價於
char str[14]; //定義字元數組strstr[] = "I love China!"; //企圖把字串賦給數組中各元素,錯誤
數組可以在定義時對各個元素賦初值,但是不能用指派陳述式對字元數組中的全部元素整體賦值。
4.儲存單元的內容。編譯時間為字元數組分配若干儲存單元以存放各元素的值,而對字元指標變數,只分配一個儲存單元(Vistual C++為指標變數分配4個位元組)。
如果定義了字元數組,但未對其賦值,這是數組中的元素的值勢不可預料的。可以引用這些值,結果顯然沒有意義,但是不會造成嚴重後果,容易發現且改正。
但是如果定義了字元指標變數,應當及時把一個字元變數(或字元數組元素)的地址賦給它,使它指向一個字元型資料,如果未對它賦予一個地制值,它未指向一個確定的對象,此時如果向該指標變數所指向的對象(其實此時指標並沒有指向對象,但是形式上還是可以這樣使用它)輸入資料,可能會造成嚴重後果。例如
char *a; //定義字元指標變數a scanf("%s", a); //企圖從鍵盤輸入一個字串,使a指向該字串,錯誤
又如
int *a; //定義指標變數a*a = 5; //在a指向的地址存入5,錯誤,因為a的指向不明
出現此類問題時,Visual C++中編譯只是發出“警告”資訊,提醒未給指標變數指定初始值,雖然也能勉強運行,但是這種方法是危險的。由於a沒有被初始化,因此它的值是隨機的,不知道5會被儲存到什麼位置。這個位置或許也許對系統危害不大,但也許會覆蓋程式資料或者代碼,甚至導致系統崩潰。應當絕對防止這種情況的出現,應當在定義指標後及時指定其指向。如:
char *a, str[10]; //定義了字元指標變數a和字元數組stra = str; //使a指向str數組的首元素scanf("%s", a); //從鍵盤輸入一個字串存放到a所指向的一段單元中,正確
先使a有確定值,使a指向一個數組元素,然後輸入一個字串,把它存放在以該地址開始的若干單元中。
5.指標變數的值是可以改變的,而數組名代表一個固定的值(數組首元素的地址),不能改變。
char *a = "I love China!";a = a + 7; //改變指標變數的值,即改變指標變數的指向printf("%s", a); //輸出從a指向的字元開始的字串
運行結果:China!
下面是錯的:
char str[] = "I love China!";str = str + 7; //錯誤 printf("%s", str);
6.字元數組中各元素的值是可以改變的(可以對它們進行再賦值),但字元指標變數指向的字串常量中的內容是不可以被取代的(不能對它們再賦值)。如
char a[] = "House";char *b = "House";a[2] = 'r'; //合法,r取代a數組元素a[2]的原值ub[2] = 'r'; //不合法,字串常量不能改變
一個完整的例子:
#include <stdio.h>int main(){void copy_string(char *from, char *to);char *a = "I am a teacher.";char b[] = "You are a student."; //此句不能換為char *b = "You are a student.",否則出現嚴重錯誤;char *p = b; //p指向b數組首元素printf("string a = %s\nstring b = %s\n", a, b); //輸出原a和b printf("\ncopy string a to string b:\n");copy_string(a, p); printf("string a = %s\nstring b = %s\n", a, b); //輸出改變後的a和breturn 0;}void copy_string(char *from, char *to){for(;*from != '\0'; from++, to++){*to = *from;}*to = '\0';}
該例中若char b[]換為char *b會出現嚴重錯誤。
7.引用數組元素。對字元數組可以用下標引用數組元素(如a[5]),也可以使用地址(如*(a+5))。如果定義了字元指標變數p,並使它指向數組a的首元素,則可以用同樣的兩種方法引用元素。
但是如果指標變數沒有指向數組,則明顯無法使用p[5]和*(p+5)這樣的形式引用數組中的元素。如果用了,也是無意義的,應當避免這種情況。
若字元指標變數p指向字串常量,就可以用指標變數帶下標的形式引用所指向的字串中的字元。如有
char *a = "I love China!" //定義字元指標變數a,並把字串第一個元素的地址賦給a
則有a[5]的值是a指向的字串“I love China!”中的第6個字元。
8.用指標變數指向一個格式字串,可以用它代替printf函數中的格式字串,例如:
char *format;format = "a = %d, b = %n"; //使format指向一個字串printf(format, a, b);
它相當於
printf("a = %d, b = %d\n", a, b);
因此只要改變指標變數format所指向的字串,就可以改變輸入輸出的格式,這種printf函數稱為可變格式輸出函數。
也可以用字元數組實現。例如:
char format[] = "a = %d, b = %n"; printf(format, a, b);
但使用字元數組時,只能採用在定義數組時初始化或逐個對元素賦值的方法,而不能用指派陳述式對數組整體賦值。如:
char format[]; format = "a = %d, b = %n";
因此,使用指標變數指向字串更為方便。
結語,說了這麼多,其實只要搞清楚字元數組分配的儲存每個數組元素空間,而數組名是以常量,其值為第一個數組元素的地址,而字元指標變數是指向字元常量的第一個元素的指標。本文中的第4點和第6點尤其值得注意。有很多東西乍一看很不理解,多寫幾個例子程式體驗馬上就能搞清楚。