字符串的指针和指向字符串的指针变量
§8.4 字符串的指針和指向字符串的指針變量
8.4.1 字符串的表現形式
在C程序中,可以用兩種方法實現一個字符串。
1. 用字符數組實現。
[例8.11]
void main(void)
{static char string [ ]="I Love China!";
printf("%s\n",string);
}
運行時輸出: I Love China!
和前面介紹的數組屬性一樣,string是數組名,它代表字符數組的首地址,(見圖8.17)。string[4]代表數組中序號為4的元素(v),實際上string[4]就是*(string+4),string+4是指向字符“v”指針。
2. 用字符指針實現。
可以不定義字符數組,而定義一個字符指針。用字符指針指向字符串中的字符。
[例8.12]
void main(void)
{char *string="I Love China!";
printf("%s\n",string);
}
在這里沒有定義字符數組,但C語言對字符串常量是按字符數組處理的,實際上在內存開辟了一個字符數組用來存放字符串數組。在程序中定義了一個字符指針變量string。并把字符串首地址(即存放字符串的字符數組的首地址)賦給它(見圖8.18)。有人認為string是一個字符串變量,以為定義時把"I Love China!"賦給該字符串變量,這是不確切的。定義string的部分:
char *string="I Love China!";
等價于下面兩行:
char *string;
string="I Love China!";
可以看到:string被定義為一個指針變量,它指向字符型數據,請注意只能指向一個字符變量或其它字符類型數據,不能同時指向多個字符數據,更不是把"I Love China!"這些字符存放到string中。只是把"I Love China!"的首地址賦給指針變量string(不是把字符串賦給*string)。因此不要認為上述定義行等價于:
char *string;
*string="I Love China!";
在輸出時,用
printf("%s\n",string);
%s表示輸出一個字符串,給出字符指針變量名string,則系統先輸出它所指向的一個字符數據,然后自動使string加1,使之指向下一個字符,然后再輸出一個字符,……,如此直到遇到字符串結束標志‘\0’為止。注意,在內存中,字符串的最后被自動加了一個‘\0’(如圖8.18所示),因此在輸出時能確定字符串的終止位置。
通過字符數組名或字符指針變量可以輸出一個字符串。而對一個數值型數組,是不能企圖用數組名輸出它的全部元素的。如:
int i[10]
:
printf("%d\n",i);
是不行的,只能逐個元素輸出。顯然,可以把字符串看作為一個整體來處理,可以對一個字符串進行整體的輸入輸出。
對字符串中字符的存取,可以用下標方法,也可以用指針方法。
[例8.13]將字符串a復制得字符串b。
void main(void)
{char a[ ]= "I am a boy. ",b[20];
int i;
for(i=0;*(a+i)!=‘\0’;i++)
*(b+i)=*(a+i);
*(b+i)=‘\0’;
printf("string a is:%s\n",a);
printf("string b is: ");
for(i=0;b[i]!=‘\0’;i++)
printf("%c",b[i]);
printf("\n");
}
程序運行結果為:
string a is:I am a boy.
string b is:I am a boy.
程序中a和b都定義為字符數組,可以用地址方法表示數組元素。在for語句中,先檢查a[i]是否為’\0’(今a[i]是以*(a+i)形式表示的)。如果不等于’\0’,表示字符串尚未處理完,就將a[i]的值賦給b[i],即復制一個字符。在for循環中將a串全部復制給了b串。最后還應將’\0’復制過去,故有:*(b+i)=‘\0’;
此時的i的值是字符串有效字符的個數n加1。第二個for循環中用下標法表示一個數組元素(即一個字符)。也可以設指針變量,用它的值的改變來指向字符串中的不同的字符。
[例8.14] 用指針變量來處理例8.13問題。
void main(void)
{char a[ ]= "I am a boy. ",b[20],*p1,*p2;
int i;
p1=a;p2=b;
for(;*p1!=‘\0’;p1++,p2++)
*p2=*p1;
*p2=‘\0’;
printf("string a is:%s\n",a);
printf("string b is: ");
for(i=0;b[i]!=‘\0’;i++)
printf("%c",b[i]);
printf("\n");
}
p1,p2是指針變量,它指向字符型數據,先使p1和p2的值分別為字符串a和b的首地址。*p1最初的值為‘i’,賦值語句“*p2=*p1;”的作用是將字符’I’(a串中第一個字符)賦給p2所指向的元素,直到 *p1的值為’\0’止。注意p1和p2的值是不斷在改變的,程序必須保證使p1和p2同步移動。
8.4.2 字符串指針作函數參數
將一個字符串從一個函數傳遞到另一個函數,可以用地址傳遞的辦法,即用字符數組名作參數或用指向字符串的指針作參數。在被調用的函數中可以改變字符串的內容,在主調函數中可以得到改變了的字符串。
[例8.15] 用函數調用實現字符串的復制。
(1)用字符數組作參數
void copy_string (char from[ ],char to[ ])
{int i=0;
while (from[i]!=‘\0’)
{to[i]=from[i];i++;}
to[i]=‘\0’;
}
void main(void)
{char a[ ]= "I am a teacher. ";
char b[ ]= "You are a student. ";
printf("string_a=%s\n string_b=%s\n",a,b);
copy_string(a,b);
printf("\nstring_a=%s\n string_b=%s\n",a,b);
}
程序運行結果如下:
string_a=I am a teacher.
string_b=You are a student.
string_a=I am a teacher.
string_b=I am a teacher.
a和b是字符數組。初值如圖8.19(a)所示。copy_string函數的作用是將from[i]賦給to[i],直到from[i]的值為’\0’為止。在調用copy_string函數時,將a和b的首地址分別傳遞給形參數組form和to。因此from[i]和a[i]是同一個單元,to[i]和b[i]是同一個單元。程序執行完以后,b數組的內容如圖8.19(b)所示??梢钥闯?#xff0c;由于b數組原來的長度大于a數組,因此在a數組復制到b數組后,未能全部覆蓋b數組原有內容。b數組最后三個元素仍保留原狀。在輸出b時由于按%s(字符串)輸出,遇’\0’即告結束,因此第一個’\0’后的字符不輸出。如果不采取%s格式輸出而用%c逐個字符輸出是可以輸出后面這些字符的。
在main函數中也可以不定義字符數組,而用字符型指針變量。main函數可改寫如下:
void main(void)
{char *a=“I am a teacher.”;
char *b= “You are a student.”;
printf(“string_a=%s\n
string_b=%s\n”,a,b);
copy_string(a,b);
printf(“\nstring_a=%s\n
string_b=%s\n”,a,b);
}
與上面程序運行結果相同。
(2)形參用字符指針變量
程序如下:
void copy_string(char *from,char *to)
{
for(;*from!=‘\0’;from++,to++)
*to=*from;
*to=‘\0’;
}
void main(void)
{char *a="I am a teacher. ";
char *b= "You are a student. ";
printf("string_a=%s\n
string_b=%s\n",a,b);
copy_string(a,b);
printf("\nstring_a=%s\n string_b=%s\n",a,b);
}
形參form和to是字符指針變量。它們相當于例8.19中的p1和p2。算法也與例8.19完全相同。在調用copy_string時,將數組a的首地址傳給from,把數組b的首地址傳給to。在函數copy_string中的for循環中,每次將*from賦給*to,第1次就是將a數組中第1個字符賦給b數組的第1個字符。在執行from++和to++以后,from和to就分別指向a[1]和b[1]。再執行*to=*from,就將a[1]賦給b[1],……。最后將’\0’賦給*to,注意此時to指向哪個單元。
(3)對copy_string函數還可作簡化
1. 將copy_string函數改寫為:
void copy_string (char *from,char *to)
{
while ((*to=*from)!=‘\0’)
{to++;from++;}
}
請與上面一個程序對比。在本程序中將“*to=*from;”的操作放在while語句的表達式中,把賦值運算和判斷是否為’\0’的運算放在表達式中,先賦值后判斷。在循環體中to和from增值,指向下一個元素,……,直到*from的值為’\0’為止。
2. copy_string函數的函數體還可改為:
{
while((*to++=*from++)!=‘\0’);
}
把上面程序的to++和from++運算與*to=*from合并,它的執行過程是:先將*from賦給*to,然后使to和from增值。顯然這又簡化了。
3. 函數體還可寫成:
{
while(*from!=‘\0’)
*to++=*from++;
*to=‘\0’;
}
當*from不為’\0’時,使*from賦給*to,然后使to和from增值。
字符可以用其ASCII代碼來代替。例如:”ch=‘a’”可以用”ch=97”代替,”while(ch!=‘a’)”可以用”while(ch!=97)”代替。因此,”while(*from!=‘\0’)”可以用”while(*from!=0)代替(’\0’的ASCII代碼為0)。而關系表達式”*from!=0”又可簡化為”*from”,這是因為若*form的值不等于0,則表達式”*from”為真,同時”*from!=0”也為真。因此”while(*from!=0)”和”while(*from)”是等價的。所以函數體可簡化為:
{while(*from)
*to++=*from++;
*to=‘\0’;}
4. 上面的while語句還可以進一步簡化為下面的while語句:
while(*to++=*from++)
它與下面語句等價:
while((*to++=*from++)!=‘\0’);
將*from賦給*to,如果賦值后的*to值等于’\0’,則循環終止(’\0’已賦給*to)。
5. 函數體中while語句也可以改用for語句:
for(;(*to++=*from++)!=0;);
或 for(;*to++=*from;);
6. 也可用指針變量,函數copy_string可寫為:
void copy_string( char from[ ],char to[ ])
{char *p1,*p2;
p1=from;p2=to;
while((*p2++=*p1++)!=‘\0’);
}
以上各種用法,變化多端,使用十分靈活,初看起來不太習慣,含義不直觀。初學者會有些困難,也容易出錯。但對C熟練之后,以上形式的使用是比較多的,讀者應逐漸熟悉它,掌握它。
歸納起來,作為函數參數,有以下幾種情況:
實 參 形 參
1. 數 組 名 數 組 名
2. 數 組 名 字符指針變量
3. 字符指針變量 字符指針變量
4. 字符指針變量 數 組 名
8.4.3 字符指針變量與字符數組
雖然用字符數組和字符指針變量都能實現字符串的存儲和運算,但它們二者之間是有區別的,不應混為一談,主要有以下幾點:
1. 字符數組由若干個元素組成,每個元素中放一個字符,而字符指針變量中存放的是地址(字符串的首地址),決不是將字符串放到字符指針變量中。
2. 賦初值的方式。對數組賦初值要用static存儲類別,如
static str[ ]={ "I love China! ");
而對字符指針變量不必加static存儲類型,如
char *a="I love China! ";
這是因為并沒有對數組初始化,只是對指針變量初始化。
3. 賦值方式。對字符數組只能對各個元素賦值,不能用以下辦法對字符數組賦值。
char str[14];
str="I love China! ";
而對字符指針變量,可以采用下面方法賦值:
char *a;
a="I love China! ";
但注意賦給a的不是字符,而是字符串的首地址。
4. 賦初值時,對以下的變量定義和賦初值:
char *a="I love China! ";
等價于:
char *a;
a="I love China! ";
而對數組初始化時:
static char str[14]={ "I love China! "};
不是等價于
char str[14];
str[ ]= "I love China! ";
即數組可以在變量定義時整體賦初值,但不能在賦值語句中整體賦值。
5. 在定義一個數組時,在編譯時即已分配內存單元,有確定的地址。而定義一個字符指針變量時,給指針變量分配內存單元,在其中可以放一個地址值,也就是說,該指針變量可以指向一個字符型數據,但如果未對它賦以一個地址位,則它并未具體指向哪一個字符數據。如:
char str[10];
scanf("%s",str);
是可以的,而常有人用下面的方法:
char *a;
scanf("%s",a);
目的是輸入一個字符串,雖然一般也能運行,但這種方法是危險的,不宜提倡。因為編譯時雖然分配給指針變量a一個單元,a的地址(即&a)是已指定了,但a的值并未指定,在a單元中是一個不可預料的值。因此在scanf函數中要求將一個字符串輸入到a的值(地址)開始的存儲區(這是好的情況),也有可能指向已存放指令或數據的內存段,這就會破壞了程序,甚至會造成嚴重的后果。在程序規模小時,由于空白地帶多,往往可以正常運行,而程序規模大時,出現上述“沖突”的可能性就大多了。應當這樣:
char *a,str[10];
a=str;
scanf("%s",a);
先使a有確定值,也就是使a指向一個數組的開頭,然后輸入字符串到該地址開始的若干單元中。
6. 指針變量的值是可以改變的,如:
[例8.16]
void main(void)
{char *a="I love China! ";
a=a+7;
printf("%s",a);
}
運行結果如下:
China!
指針變量a的值可以變化,輸出字符串從a當時所指向的單元開始輸出各個字符,直到遇’\0’為止。而數組名雖然代表地址,但它的值是不能改變的。下面是錯的:
char str[ ]={ "I love China! "};
str=str+7;
printf("%s",str);
需要說明:若定義了一個指針變量,使它指向一個字符串后,可以用下標形式引用指針變量所指的字符串中的字符。如:
[例8.17]
void main(void)
{char *a="I LOVE CHINA. ";
int i;
printf("The sixth charcter is %c\n",a[5]);
for(i=0;a[i]!=‘\0’;i++)
printf("%c",a[i]);
}
運行結果如下:
The sixth charcter is E
I LOVE CHINA.
程序中雖然并未定義數組a,但字符串在內存中是以字符數組形式存放的。a[5]按*(a+5)執行,即從a當前所指向的元素下移5個元素的位置,取出其單元中的值。
7. 用指針變量指向一個格式字符串,可以用它代替printf函數中的格式字符串。如:
char *format;
format="a=%d,b=%f\n";
printf(format,a,b);
它相當于
printf("a=%d,b=%f\n",a,b);
因此只要改變指針變量format所指向的字符串,就可以改變輸入輸出的格式。這種printf函數稱為可變格式輸出函數。
也可以用字符數組。如:
char format[ ]= "a=%d,b=%f\n";
printf(format,a,b);
但由于不能采取賦值語句對數組整體賦值的形式,如:
char format[ ];
format="a=%d,b=%f\n";
因此用指針變量指向字符串的方式更為方便。
y3= 1.29
轉載于:https://www.cnblogs.com/inspurhaitian/archive/2008/09/05/1285027.html
總結
以上是生活随笔為你收集整理的字符串的指针和指向字符串的指针变量的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: as3corelib系列教程之一:Arr
- 下一篇: 简单文件的上传与保存