1.字符串的引用方式
在C语言程序中,字符串是存放在字符数组中的,想引用一个字符串,可以用以下俩种方法
(1)用字符数组存放一个字符串,可以通过数组名和下标引用字符串其中一个字符,也可以用数组名和格式声明“%s”输出字符串
【例题】定义一个字符数组,在其中存放字符串“I love China!”输出该字符串和第八个字符
#include<stdio.h>
int main()
{
char string[]="I love China!"; //定义字符数组
printf("%s\n",string); //输出整个字符串
printf("%c",string[7]); //输出第八个字符
return 0;
}
运行结果:
在定义字符串数组时没有指定长度,但是对它初始化了,所以它的长度是确定的虽然只存放了“I love China!”13个字符,但是应该是14个,因为最后还有一个字符串结束符‘\0’ ,数组名string代表字符数组首元素地址,实际上string[7]就是*(string+7),string+7就是一个地址,它指向“C”。
(2)用字符指针变量指向一个字符串常量,通过字符指针变量指向字符串常量
【例题】通过字符指针变量输出一个字符串
#include<stdio.h>
int main()
{
char *string="I love China!"; //定义字符指针变量并初始化
printf("%s\n",string); //输出整个字符串
return 0;
}
运行结果:
在程序中没有定义字符数组,只定义了一个字符指针变量,用字符常量“I love China!” 对其进行初始化,C语言对字符串常量是按字符数组处理的,在内存中开辟一个字符串数组来存放字符串常量,但是这个字符数组是没有名字的,因此不能通过数组名来引用,只能通过指针变量来引用。
注意:string被定义为一个指针常量,基类型为字符型,请注意它只能指向一个字符类型数据,而不能同时指向多个字符数据,更不是把“I love China!”这些存放到string中(指针变量只能存放地址),也不是把字符串赋值给*string。只是把“I love Chian!”的第一个字符的地址赋值给指针变量。
//可以对指针变量进行再次赋值,例如
string = "I am a student.";
%s是输出字符串时所用的格式符,在输出时能确定输出的字符到何时结束,也就是对一个字符串进行整体的输入输出。
通过字符数组名或字符指针变量可以输出一个字符串,而对于一个数值型数组,是不能用数组名输出它的全部元素的,例如:
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++){ //a[i]的值赋值给b[i]
*(b+i)=*(a+i);
}
*(b+i)='\0';
printf("string a is:%s\n",a); //输出数组a的元素
printf("string b is:");
for(i=0;b[i]!='\0';i++){
printf("%c",b[i]);
}
return 0;
}
运行结果:
也可以用数组的方法输出b的值,如:
printf("string b is:%s\n",b);
也可以用另一种方法----用指针变量访问字符串,通过改变指针变量的值使它指向字符串的不同字符,如下:
#include<stdio.h>
int main()
{
char a[]="I am a boy.",b[20],*p1,*p2; //定义字符数组
p1=a,p2=b; //p1,p2分别指向a,b数组的第一个元素
for(;*p1!='\0';p1++,p2++){ //p1,p2自加
*p2=*p1; //将p1指向的值赋值给p2
}
*p2='\0';
printf("string a is:%s\n",a);
printf("string b is:%s\n",b);
return 0;
}
运行结果:
2.字符指针作函数参数
如果想把一个字符串从一个函数传递到另一个函数,可以用地址传递的方法,即用字符数组名作参数,也可以字符指针变量作参数。在被调用的函数中可以改变字符串的内容,在主调函数中可以引用改变后的字符串。
【例题】用函数调用实现字符串的复制
【1】用字符数组名作函数参数
#include<stdio.h>
void copy(char from[],char to[]); //函数声明
int main()
{
char a[]="I am a teacher.";
char b[]="You are a student.";
printf("string a = %s\nstring b = %s",a,b);
printf("\ncopy string a to string b:\n");
copy(a,b); //用字符数组名作函数参数
printf("string a = %s\nstring b = %s",a,b);
return 0;
}
void copy(char from[],char to[]) //形参为字符数组
{
int i;
while(from[i]!='\0'){
to[i]=from[i];
i++;
}
to[i]='\0';
}
运行结果:
在调用copy函数时,将a和b的第一个字符的地址分别传递给形参数组名from和to。由于数组b原来的长度大于a数组,因此将a数组复制到b数组后,未能全部覆盖,在输出时按照%s输出,遇到'\0'就停止了,如果按照%c,则后面未被覆盖的还可以输出。
【2】用字符型指针变量作实参
#include<stdio.h>
void copy(char from[],char to[]); //函数声明
int main()
{
char a[]="I am a teacher.";
char b[]="You are a student.";
char *from = a,*to =b;
printf("string a = %s\nstring b = %s",a,b);
printf("\ncopy string a to string b:\n");
copy(from,to); //用字符数组名作函数参数
printf("string a = %s\nstring b = %s",a,b);
return 0;
}
void copy(char from[],char to[]) //形参为字符数组
{
int i;
while(from[i]!='\0'){
to[i]=from[i];
i++;
}
to[i]='\0';
}
一样可以达到目的
【3】用字符指针变量作形参和实参
#include<stdio.h>
void copy(char *from,char *to); //函数声明
int main()
{
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",a,b);
printf("\ncopy string a to string b:\n");
copy(a,p); //用字符数组名作函数参数
printf("string a = %s\nstring b = %s",a,b);
return 0;
}
void copy(char *from,char *to) //形参为字符数组
{
for(;*from!='\0';from++,to++){
*to=*from;
}
*to='\0';
}
程序改进:
copy 函数还可以改写的更精炼一些,如下:
(1)
void copy(char * from,char * to)
{
while((* to=*from)!='\0'){
to++,from++;
}
}
(2)
void copy(char *from,char* to)
{
while(*from!='\0'){
*to++=*from++;
}
*to='\0';
}
先将*from赋值给*to,然后自加
(3)
void copy(char * from,char * to)
{
while((* to++=*from++)!='\0'){
}
}
(4)
还可以用ascll码来表示
{
while(*from){ //与*from!='\0'等价
*to++=*from++;
}
*to='\0';
}
(5)
上述函数体还可以写成
while(*to++=*from++);
等价于:
while((*to++=*from++)!='\0');
(6)也可以用for循环语句
for(;(*to++=*from++)!='\0';);
或:
for(;*to++=*from++;);
(7)也可以用字符数组名作函数形参
void copy(char from[],char to[])
{
char *p1,*p2;
p1=from,p2=to;
while((* p2++=*p1++)!='\0'){
}
}
3.使用字符指针变量和字符数组的比较
用字符数组和字符指针变量都能实现字符串的存储和运算,但他们二者之间是有区别的:
(1)字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第一个字符的地址),绝不是将字符串放到字符指针变量中。
(2)赋值方式。可以对字符指针变量赋值,但不能对数组名赋值:
可以用下面方法对字符指针变量赋值:
char *a; //a为字符指针变量
a = "I love Chian!" //将字符串首元素地址赋给指针变量,合法
不能用以下办法对字符数组名赋值:
char str[14];
str[0]='I'; //对字符数组元素赋值,合法;
str="I love China!"; //数组名是地址,是常量,不能被赋值
(3)初始化的含义,对字符指针变量赋初值:
char * a="I love China!"; //定义字符指针变量a,并把字符串第一个元素地址赋值给a
等价于:
char *a; //定义字符指针变量a
a ="I love China!"; //把字符串第一个元素地址赋值给a
对数组初始化:
char str[14]="I love China!"; //定义字符数组并赋初值
不等价于:
char str[14]; //定义字符数组
str[]="I love Chian!"; //企图把字符串赋给数组中的各个元素
数组可以在定义时对各个元素赋初值,但不能用赋值语句对字符数组中全部元素整体赋值
(4)储存单元的内容
编译时为字符数组分配若干储存单元,以存放各个元素的值,而对字符指针变量,只分配一个存储单元,例如下面的用法
char *a; //定义字符指针变量
scanf("%s",a); //企图从键盘中输入一个字符串,使a指向它,错误
在编译时,给指针变量分配了存储单元,即a的地址已经指定了,但a的存储单元中是一个不可预料的值,在输入时可能导致系统出错,应当在定义指针变量后,及时指定其指向。如:
char *a,str[10]; //定义字符指针变量和字符数组
a=str; //使a指向str的首元素
scanf("%s",a); //从键盘中输入一个字符串存放到a所指向的一段储存单元中
先使a有确定的值,然后再输入。
(5)指针变量的值是可改变的,而字符数组名代表一个固定的值(数组首元素地址),不能改变
【例题】改变指针变量的值
#include<stdio.h>
int main()
{
char *a="I love China!";
a=a+7;
printf("%s",a);
return 0;
}
运训结果是:“ China! ”
指针变量指向的初始值变了,所以最终输出结果变了,而数组名虽然代表地址,但它是常量,它的值是不能改变的,下面做法是错误的:
char str[]={"I love China!"};
str = str+7;
printf("%s",str);