通過指針引用字符串詳解,以及字符指針變量和字符數組的比較、
在平常的案例中已大量地使用了字符串,如在 printf函數中輸出一個字符串。這些字符串都是以直接形式 (字面形式) 給出的,在一對雙引號中包含若干個合法的字符。在本節(jié)中將介紹使用字符串的更加靈活方便的方法:通過指針引用字符串。
目錄
一、字符串的引用方式
1.1引入
二、字符指針作函數參數
2.1舉例說明
2.2程序改進
三、使用字符指針變量和字符數組的比較
一、字符串的引用方式
1.1引入
在 C 程序中,字符串是存放在字符數組中的。想引用一個字符串,可以用以下兩種方法。
(1)用字符數組存放一個字符串,可以通過數組名和下標引用字符串中一個字符,也可以通過數組名和格式聲明 “ %s ” 輸出該字符串。
舉例:定義一個字符數組,在其中存放字符串 " I love China! " ,輸出該字符串和第8個字符。
#include <stdio.h>
int main()
{
char string[] = "1 love China!"; //定義字符數組string
printf("%s\n", string); //用%s格式聲明輸出string,可以輸出整個字符串
printf("%c\n", string[7]); //用%c格式輸出一個字符數組元素
return 0;
}
運行結果:
程序分析:
在定義字符數組 string 時未指定長度,由于對它初始化,因此它的長度是確定的,長度應為14,其中 13 個字節(jié)存放 " l love China! " 13個字符,最后一個字節(jié)存放字符串結束符 '\0'。數組名 string? 代表字符數組首元素的地址。題目要求輸出該字符串第 8 個字符,由于數組元素的序號從 0 起算,所以應當輸出 string[7],它代表數組中序號 7 的元素的值(它的值是字母 C )。實際上 string[7] 就是 *(string+7),string+7 是一個地址,它指向字符 “ C ”。
(2)用字符指針變量指向一個字符串常量,通過字符指針變量引用字符串常量。
舉例:通過字符指針變量輸出一個字符串。
#include <stdio.h>
int main()
{
char* string = "1 love China!"; //定義指針變量string
printf("%s\n", string); //用%s格式輸出整個字符串
printf("%c\n", *(string + 7)); //用%c格式輸出一個元素
return 0;
}
運行結果:
程序分析:
在程序中沒有定義字符數組,只定義了一個 char* 型的指針變量(字符指針變量) string,用字符串常量 " I love China! " 對它初始化。C 語言對字符串常量是按字符數組處理的,在內存中開辟了一個字符數組用來存放該字符串常量,但是這個字符數組是沒有名字的,因此不能通過數組名來引用,只能通過指針變量來引用。
對字符指針變量 string 初始化,實際上是把字符串第 1 個元素的地址(即存放字符串的字符數組的首元素地址)賦給指針變量 string,使 string 指向字符串的第 1 個字符,由于字符串常量 " I love China! " 已由系統(tǒng)分配在內存中連續(xù)的 14 個字節(jié)中,因此,string 就指向了該字符串的第一個字符。在不致引起誤解的情況下,為了簡便,有時也可說 string 指向字符串 " I love China! " ,但應當理解為 “ 指向字符串的第 1 個字符 ”。
注意:
string 被定義為一個指針變量,基類型為字符型。請注意它只能指向一個字符類型數據,而不能同時指向多個字符數據,更不是把 " I love China! " 這些字符存放到 string 中(指針變量只能存放地址),也不是把字符串賦給 *string。只是把 " I love China! " 的第 1 個字符的地址賦給指針變量。
%s 是輸出字符串時所用的格式符,在輸出項中給出字符指針變量名 string,則系統(tǒng)會輸出 string 所指向的字符串第 1 個字符,然后自動使 string 加 1,使之指向下一個字符,再輸出該字符……如此直到遇到字符串結束標志 '\0' 為止。在內存中,字符串的最后被自動加了一個 '\0' ,因此在輸出時能確定輸出的字符到何時結束。
說明:
通過字符數組名或字符指針變量可以輸出一個字符串,而對一個數值型數組,是不能企圖用數組名輸出它的全部元素的。
例如:
int a[10];
...
printf("%d\n",a);
是不行的,它輸出的是數組首元素的地址。對于數值型數組的元素值只能逐個輸出。
舉例:將字符串 a 復制為字符串 b,然后輸出字符串b。
#include <stdio.h>
int main()
{
char a[] = "I am a student.", b[20];//定義字符數組
int i;
for (i = 0; *(a + i) != '\0'; i++)
*(b + i) = *(a + i); //將a[i的值賦給b[i
*(b + i) = '\0'; //在b數組的有效字符之后加'\0'
printf("string a is:%s\n", a); //輸出a數組中全部有效字符
printf("string b is:");
for (i = 0; b[i] != '\0'; i++)
printf("%c", b[i]); //逐個輸出b數組中全部有效字符
printf("\n");
return 0;
}
運行結果:
程序分析:
程序中 a 和 b 都定義為字符數組,今通過地址訪問其數組元素。在 for 語句中,先檢查 a[i] 是否為 '\0' ( a[i] 是以 *(a+i) 形式表示的)。如果不等于 '\0',表示字符串尚未處理完,就將 a[i] 的值賦給b[i],即復制一個字符。在 for 循環(huán)中將 a 串中的有效字符全部復制給了 b 數組。最后還應將 '\0' 復制過去,作為字符串結束標志。
在第 2 個 for 循環(huán)中輸出 b 數組中的元素,在 printf 函數中用下標法表示一個數組元素(即一個字符)。也可以用輸出 a 數組的方法輸出b數組。用以下一行代替程序的10-13行。
printf("string b is:%s\n”,b);
程序中用逐個字符輸出的方法只是為了表示可以用不同的方法輸出字符串。
也可以用另一種方法:用指針變量訪問字符串。通過改變指針變量的值使它指向字符串中的不同字符。
舉例:用指針變量來處理問題。
#include<stdio.h>
int main()
{
char a[] = "I am a boy.", b[20], * pl, * p2;
pl = a; p2 = b; //pl, p2分別指向a數組和b數組中的第一個元素
for (; *pl != '\0'; pl++, p2++) //pl,p2每次自加1
*p2 = *pl; //將pl所指向的元素的值賦給p2所指向的元素
*p2 = '\0'; //在復制完全部有效字符后加'\0'
printf("string a is:%s\n", a); //輸出a數組中的字符
printf("string b is:%s\n", b); //輸出b數組中的字符
return 0;
}
運行結果:
程序分析:
p1 和 p2 是指向字符型數據的指針變量。先使 p1 和 p2 分別指向字符串 a 和 b 的第1個字符。*p1 最初的值是字母 'I'。賦值語句 “ *p2 =?*p1; ” 的作用是將字符 'I' ( a 串中第 1 個字符)賦給 p2 所指向的元素,即 b[0]。然后 p1 和 p2 分別加 1,分別指向其下面的一個元素,直到 *p1 的值為 '\0' 止。
二、字符指針作函數參數
如果想把一個字符串從一個函數 “傳遞” 到另一個函數,可以用地址傳遞的辦法,即用字符數組名作參數,也可以用字符指針變量作參數。在被調用的函數中可以改變字符串的內容,在主調函數中可以引用改變后的字符串。
2.1舉例說明
舉例:用函數調用實現(xiàn)字符串的復制。
(1)用字符數組名作為函數參數
#include <stdio.h>
int main()
{
void copy_string(char from[], char to[]);
char a[] = "I am a teacher.";
char b[] = "You are a student.";
printf("string a=%s\nstring b=%s\n", a, b);
printf("\ncopy string a to string b:\n");
copy_string(a, b); //用字符數組名作為函數實參
printf("string a = % s\nstring b = % s\n", a, b);
return 0;
}
void copy_string(char from[], char to[]) //形參為字符數組
{
int i = 0;
while (from[i] != '\0')
{
to[i] = from[i];
i++;
}
to[i] = '\0';
}
運行結果:
程序分析:
a 和 b 是字符數組。copy_string() 函數的作用是將 from[i] 賦給 to[i],直到 from[i] 的值等于 '\0' 為止。在調用 copy_string() 函數時,將 a 和 b 第 1?個字符的地址分別傳遞給形參數組名 from 和? to 。因此 from[i] 和 a[i] 是同一個單元,to[i] 和 b[i] 是同一個單元。程序執(zhí)行完以后,由于 b 數組原來的長度大于?a 數組,因此在將 a 數組復制到 b 數組后,未能全部覆蓋 b 數組原有內容。b 數組最后 3 個元素仍保留原狀。在輸出 b 時由于按 %s (字符串)輸出,遇 '\0' 即告結束,因此第一個 '\0' 后的字符不輸出。如果用 %c 逐個字符輸出是可以輸出后面這些字符的。
(2)用字符型指針變量作實參
#include <stdio.h>
int main()
{
void copy_string(char from[], char to[]); //函數聲明
char a[] = "I am a teacher."; //定義字符數組a并初始化
char b[] = "You are a student."; //定義字符數組b并初始化
char* from = a, * to = b; //from指向a數組首元素,to指向b數組首元素
printf("string a=%s\nstring b=%s\n", a, b);
printf("\ncopy string a to string b:\n");
copy_string(from, to); //實參為字符指針變量
printf("string a=%s\nstring b=%s\n", a, b);
return 0;
}
void copy_string(char from[], char to[]) //形參為字符數組
{
int i = 0;
while (from[i] != '\0')
{
to[i] = from[i];
i++;
}
to[i] = '\0';
}
運行結果:
程序分析:
指針變量 from 的值是 a 數組首元素的地址,指針變量 to 的值是 b 數組首元素的地址。它們作為實參,把 a 數組首元素的地址和 b 數組首元素的地址傳遞給形參數組名 from 和 to (它們實質上也是指針變量)。
(3)用字符指針變量作形參和實參
#include <stdio.h>
int main()
{
void copy_string(char* from, char* to);
char* a = "I am a teacher. "; //a是 char *型指針變量
char b[] = "You are a student. "; //b是字符數組
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); //調用copy_string函數,實參為指針變量
printf("string a=%s\nstring b=%s\n", a, b);//輸出改變后的a串和b串
return 0;
}
void copy_string(char* from, char* to) //定義函數,形參為字符指針變量
{
for (; *from != '\0'; from++, to++)
{
*to = *from;
}
*to = '\0';
}
運行結果:
程序分析:
形參改用 char* 型變量(即字符指針變量)。在程序(1)和(2)中 copy_string 函數的形參用字符數組名,其實編譯系統(tǒng)是把字符數組名按指針變量處理的,只是表示形式不同。copy_string 函數中不是用下標法引用數組元素,而是通過移動指針變量的指向,找到并引用數組元素。
main 函數中的 a 是字符指針變量,指向字符串 "I am a teacher." 的首字符。b 是字符數組,在其中存放了字符串 "You are a student." 。p 是字符指針變量,它的值是 b 數組第一個元素的地址,因此也指向字符串 "You are a student.” 的首字符。copy_string 函數的形參 from 和 to 是字符指針變量。在調用 copy_string 時,將數組 a 首元素的地址傳給 from,把指針變量 p 的值(即數組 b 首元素的地址)傳給 to。因此 from 指向 a 串的第一個字符 a[0],to 指向 b[0]。在 for 循環(huán)中,先檢查 from 當前所指向的字符是否為' \0',如果不是,表示需要復制此字符,就執(zhí)行 “ *to = *from ”,每次將 *from 的值賦給 *to,第 1 次就是將 a 串中第 1 個字符賦給 b 數組的第 1 個字符。每次循環(huán)中都執(zhí)行 “from++” 和 “to++”,使 from 和 to 分別指向 a 串和 b 數組的下一個元素。下次再執(zhí)行 “ *to = *from ” 時,就將 a 串中第 2 個字符賦給b[1]……最后將 '\0' 賦給 *to,注意此時 to 指向哪個單元。
2.2程序改進
對 copy_string 函數還可以改寫得更精練一些,可以作以下一些改動:
(1)將 copy_string 函數改寫為
void copy_string(char* from, char* to)
{
while ((*to = *from) != '\0')
{
to++; from++;
}
}
在本程序中將 “ *to = *from ” 的操作放在 while 語句括號內的表達式中,而且把賦值運算和判斷是否為 '\0' 的運算放在一個表達式中,先賦值后判斷。在循環(huán)體中使 to 和 form 增值,指向下一個元素……直到 *from 的值為 '\0' 為止。
(2)copy_string 函數的函數體還可以改為
{
while ((*to++ = *from++) != '\0');
}
把上面程序的 to++和 from++ 運算與 *to = *from合并,它的執(zhí)行過程是,先將 *from 賦給 *to,然后使 to 和 from 增值。顯然這又簡化了。
(3)copy_string 函數的函數體還可以改為
{
while (*from!='\0')
{
*to++ = *from++;
}
*to != '\0';
}
當 *from 不等于 '\0' 時,將 *from 賦給 *to,然后使 to 和 from 增值。
(4)由于字符可以用其ASCII碼來代替
例如,“ ch=' a' ”可用 “ ch= 97 ”代替,“ while(ch!='a') ” 可以用 “ while(ch!=97) ”代替。因此,“ while(*from!='\0') ” 可以用 “ while(*from!=0) ” 代替( '\0' 的 ASCII 代碼為 0 )。而關系表達式 “ *from!=0 ” 又可簡化為 “ *from ”,這是因為若 *from 的值不等于0,則表達式 “ *from ” 為真,同時 “ *from!=0 ” 也為真。因此 “ while(*from!=0) ” 和 “ while(*from) ”是等價的。
所以函數體可簡化為
{
while (*from)
{
*to++ = *from++;
}
*to != '\0';
}
(5)上面的 while 語句還可以進一步簡化為
while (*to++ = *from++);
它與下面的語句等價
while ((*to++ = *from++) != '\0');
(6)函數體中也可以改為只用一個 for 語句
for (; (*to++ = *from++) != '\0'; );
或
for (; *to++ = *from++; );
(7)也可以用字符數組名做函數形參,在函數中另定義兩個指針變量p1,p2。函數 copy_string 可寫為
void copy_string(char from[], char to[])
{
char* pl, * p2;
pl = from; p2 = to;
while ((*p2++ = *pl++) != '\0');
}
以上各種用法,使用十分靈活,程序精練,比較專業(yè),初學者看起來不太習慣,覺得含義不直觀。初學者要很快地寫出它們可能會有些困難,也容易出錯。但應能看懂以上的用法。在對C熟練后,以上形式的使用是比較多的。
歸納起來,用字符指針作為函數參數時,實參與形參的類型有以下幾種對應關系
實參 | 形參 |
字符數組名 | 字符數組名 |
字符數組名 | 字符指針變量 |
字符指針變量 | 字符指針變量 |
字符指針變量 | 字符數組名 |
三、使用字符指針變量和字符數組的比較
用字符數組和字符指針變量都能實現(xiàn)字符串的存儲和運算,但它們二者之間是有區(qū)別的,不應混為一談,主要有以下幾點。
(1)字符數組由若干個元素組成,每個元素中放一個字符,而字符指針變量中存放的是地址(字符串第 1 個字符的地址),絕不是將字符串放到字符指針變量中。
(2)賦值方式??梢詫ψ址羔樧兞抠x值,但不能對數組名賦值。
(3)初始化含義。數組可以在定義時對各元素賦初值,但不能用賦值語句對字符數組中全部元素整體賦值。
char str[14] = "1 love China!";
不等價于
char str[14];
str[] = "1 love China!"; //把字符串賦給數組中的各元素,錯誤
(4)存儲單元的內容。編譯時為字符數組分配若干存儲單元,以存放各元素的值,而對字符指針變量,只分配一個存儲單元( Visual C++為指針變量分配 4 個字節(jié))。
如果定義了字符數組,但未對它賦值,這時數組中的元素的值是不可預料的。可以引用(如輸出)這些值,結果顯然是無意義的,但不會造成嚴重的后果,容易發(fā)現(xiàn)和改正。
(5)指針變量的值是可以改變的,而字符數組名代表一個固定的值(數組首元素的地址),不能改變。
(6)字符數組中各元素的值是可以改變的(可以對它們再賦值),但字符指針變量指向的字符串常量中的內容是不可以被取代的(不能對它們再賦值)。
(7)引用數組元素。對字符數組可以用下標法(用數組名和下標)引用一個數組元素(如 a[5] ),也可以用地址法 ( 如 *(a+5) ) 引用數組元素 a[5]。如果定義了字符指針變量 p,并使它指向數組 a 的首元素,則可以用指針變量帶下標的形式引用數組元素 (?如 p[5] ),同樣,可以用地址法(如 *(p+5) )引用數組元素 a[5]。
(8)用指針變量指向一個格式字符串,可以用它代替 printf 函數中的格式字符串。
char* format;
format = "a=%d,b=%f\n"; //使format指向一個字符串
printf(format, a, b);
它相當于
printf("a= %d,b=%f\n", a, b);
因此只要改變指針變量 format 所指向的字符串,就可以改變輸入輸出的格式。這種 printf 函數稱為可變格式輸出函數。
C語言指針操作系列文章:
C語言指針操作(一)地址,指針,指針變量是什么
C語言指針操作(二)指針變量作為函數參數
C語言指針操作(三)通過指針引用數組
C語言指針操作(四)用數組名作函數參數
C語言指針操作(五)通過指針引用多維數組
C語言指針操作(六)指向函數的指針
C語言指針操作(七)通過指針引用字符串
C語言指針操作(八)返回指針值的函數
C語言指針操作(九)指針數組和多重指針
C語言指針操作(十)動態(tài)內存分配與指向它的指針變量文章來源:http://www.zghlxwxcb.cn/news/detail-769766.html
C語言指針操作(十一)有關指針的小結文章來源地址http://www.zghlxwxcb.cn/news/detail-769766.html
到了這里,關于C語言指針操作(七)通過指針引用字符串的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!