用来存放字符数据的数组是字符数组。字符数组中一个元素存一个字符。
一、字符数组的定义
定义方法与前而介绍的类似。例如:
char c[10];
c [o]=’I’;c[1]=’ ’,c[2]=’a’,c[3]=’m’;c[4]=’ ’;c[5]=’h’;c[6]=’a’;c[7]=’p’; c[8]=’p’;c[9]=’y’;
定义c为字符数组,包含10元素。在复制以后数组的状态如下图所示。
c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] c[8] c[9]
I | a | m | h | a | p | p | y |
由于字符型与整型是互相通用的,因此上面的定义也可改为:
int c[10]; /*合法,但浪费存储空间*/
二、字符数组的初始化
对字符数组初始化,最容易理解的方式是逐个字符赋给数组中各元素。如:
char c[10]= {’I’,’ ’ ,’a’ ,’m’,’ ’,’h’,’a’ ,’p’,’p’,’y’);
把10个字符分别赋给c[0]到c[9]10个元素。
如果花括弧中提供的初值个数(即字符个数)大于数组长度,则按语法错误处理。如果初值个数小于数组长度,则只将这些字符赋给数组中前面那些元素,其余的元素自动定为空字符(即'\0')。如:
char c[10]= {’c’,’ ’,’p’,’r’,’o’,’g’,’r’ ,’a’,’m’} ;
数组状态如下图所示。
c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] c[8] c[9]
c | p | r | o | g | r | a | m | \0 |
如果提供的初值个数与预定的数组长度相同,在定义时可以省略数组长度,系统会自动根据初值个数确定数组长度。如:
char c[]={’I’,’ ’,’a’ ,’m’,’ ’,’h’,’a’ ,’p’,’p’,’y’};
数组c的长度自动定为10。用这种方式可以不必人工数字符的个数,尤其在赋初值的字符个数较多时,比较方便。
也可以定义和初始化一个二维字符数组,如:
char diamond[5][5]={{’ ’,’ ’,’*’},{’ ’,’*’,’ ’,’*’},{’*’,’ ’,’ ’,’ ’,’*’},{’ ’,’*’,’ ’,’*’},{’ ’,’ ’,’*’}};
用它代表一个钻石形的平面图形,见下图。
*
* *
* *
* *
*
三、字符数组的引用
可以引用字符数组中的一个元素,得到一个字符。
例6输出一个字符串。
main()
{char c[10]={’I’,’ ’,’a’ ,’m’,’ ’,’a’,’ ’ ,’b’,’o’,’y’};
int i;
for (i=0;i<10;i++)
printf("%c",c[i]);
printf("\n");
运行结果: I am a boy
例输出一个钻石图形。
main()
{char diamond[][5]={{’ ’,’ ’,’*’},{’ ’,’*’,’ ’,’*’},{’*’,’ ’,’ ’,’ ’,’*’},{’ ’,’*’,’ ’,’*’},{’ ’,’ ’,’*’}};
int i,j;
for (i=0;i<5;i++)
{for(j=0;j<5;j++)
printf("%c",diamond[i][j]);
printf("\n");
}
}
运行结果为:
*
* *
* *
* *
*
四、字符串和字符串结束标志
在C语言中,将字符串作为字符数组来处理。例6就是用一个一维的字符数组存放一个字符串"I am a boy"中的字符。这个字符串的实际长度与数组长度相等。有时,人们关心的是有效字符申的长度而不是字符数组的长度。例如,定义一个字符数组长度为100,而实际有效字符只有40个。为了测定字符串的实际长度,C语言规定了一个“字符串结束标志”,以字符’\0’代表。如果有一个字符串,其中第10个字符为’\0’,则此字符串的有效字符为9个。也就是说,在遇到字符\0时,表示字符串结束,由它前面的字符组成字符串。
系统对字符串常量也自动加一个’\0’作为结束符。例如”C Program”共有9个字符,但在内存中占10个字节,最后一字节’\0’是系统自动加上的。字符作为一维数组放在内存中。
有了结束标志’\0’后,字符数组的长度就显得不那么重要了。在程序中往往依靠检测’\0’的位置来判定字符串是否结束,而不是根据数组的长度来决定字符串长度。当然在定义字符数组时应估计实际字符串长度,保证数组长度始终大于字符串实际长度。如果在一个字符数组中先后存放多个不同长度的字符串,则应使数组长度大于最长的字符串的长度。
说明:’\0’代表ASCHI码为0的字符,从ASCII码表中可以查到,ASCII码为0的字符不是一个可以显示的字符,而是一个“空操作符”,即它什么也不干。用它来作为字符串结束标志不会产生附加的操作或增加有效字符,只起一个供辨别的标志。
我们以前曾用过以下的语句:
printf("How do you do? \n");
即输出一个字符串。在执行此语句时系统怎么知道应该输出到哪里为止呢?实际上,在内存中存放时,系统自动在最后一个字符’\n’的后面加了一个’\0’作为字符串结束标志,在执行printf函数时,每输出一个字符检查一次,看下一个字符是否’\0’。遇’\0’就停止输出。
对C语言处理字符串的方法有以上的了解后,我们再对字符数组初始化的方法补充一种方法,可以用字符串常量来使字符数组初始化。例如
char c[]={"I am happy"};
也可以省略花括弧,直接写成
char c[]="I am happy";
不是用单个字符作为初值,而是用一个字符串(注意字符串的两端是用双撇号而不是单撇号括起来的)作为初值。显然,这种方法直观、方便、符合人们的习惯。数组c的长度不是10,而是11,这点务请注意。因为字符串常量的最后由系统加上一个’\0’。因此,上面的初始化与下面的初始化等价。
char c[] ={’I’,’ ’,’a’ ,’m’,’ ’,’h’,’a’ ,’p’,’p’,’y’,’\0’};
而不与下面的等价;
char c[]={’I’,’ ’,’a’ ,’m’,’ ’,’h’,’a’ ,’p’,’p’,’y’};
前者的长度为11,后者的长度为10。如果有
char c[10]={“China”};
数组c的前5个元索为’C’,’h’,’i’,’n’,’a’第6个元素为’\0’,后4个元素为空字符,见图
C | h | i | n | a | \0 | \0 | \0 | \0 | \0 |
需要说明的是:字符数组并不要求它的最后一个字符为’\0’,甚至可以不包含’\0’。像以下这样写完全是合法的:
char c[5]={’C’,’h’,’i’,’n’,’a’);
是否需要加’\0’,完全根据需要决定。但是由于系统对字符串常量自动加一个’\0’。因此,人们为了使处理方法一致,便于测定字符串的实际长度,以及在程序中作相应的处理,在字符数组也常常人为地加上一个'\0'。如:
char c[6]= (’C’,’h’,’i’,’n’,’a’,' \0' } ;
五、字符数组的输入输出
字符数组的输人输出可以有两种方法:
(1)逐个字符输入输出。用格式符“%c”输人或输出一个字符,如例6。
(2)将整个字符串一次输入或输出。用“%s”格式符,意思是输出字符串(String)。例如:
char c[]={“China”};
printf(“%s”,c);
在内存中数组c的状态如图所示。输出时,遇结束符’\0’就停止输出。输出结果为:
China
C | h | i | n | a | \0 |
请注意:
(1)输出字符不包括结束符\0’。
(2)用“%s”格式符输出字符串时,printf函数中的输出项是字符数组名,而不是数组元素名。写成下面这样是不对的;
printf("%s",c[0]);
(3)如果数组长度大于字符串实际长度,也只输出到遇'\0'结束。如
char c[10]= {"China"} ;
printf("%s",c);
也只输出“China"5个字符,而不是出10个字,这就是用字符串结束标志的好处。
- 如果一个字符数组中包含一个以上'\0',遇第一时输结束。
可以用scanf函数输入一个字符申。例如
scanf("%s",c);
scanf函数中的输人项c是字符数组名,它应该在先前已被定义。从键盘输人的字符串应短于已定文的字符数组的长度。例如,已定义
char c[6];
从键盘输人:
China
系统自动在后面加一个'\0'结束符。如果利用一个scanf函数输入多个字符串,则以空格分隔。例如:
char str1[5],str2[5],str3[5];
scanf(“%s%s%s”,str1,str2,str3);
输入数据: How are you?
输入后str1、str2、str3 数组状态。数组中未被赋值的元素的值自动置'\o'若改为
char str[13]:
scanf("%s".str);
如果输入以下12个字符
How are you?
实际上并不是把这12个字符加上'\0'送到数组str中,而只将空格前的字符“How”送到str中,由于把“How”作为一个字符串处理,因此在其后加'\0'。str数组状态见图
H | o | w | \0 | \0 | \0 | \0 | \0 | \0 | \0 | \0 | \0 |
需要注意:scanf函数中的输入项是字符数组名。输入项为字符数组名时,不要再加地址符&,因为在C语言中数组名代表该数组的起始地址。下面写法不对:
printf("%o",c);
可以输出数组c的起始地址2000。
前面介绍的输出字符串的方法:
printf("%s",c);
实际上是这样执行的:按字符数组名c找到其数组起始地址, 然后逐个输出其中的字符,直到遇到'\0'为止。
由于C语言用一维字符数组存放字符串,而且允许用数组名进行输入或输出一字符串,因此,可以把一维字符数组看作相当于其他语言(如BASIC)中的”字符串变量”。
六、字符串处理函数
在C的函数库中提供了一些用来处理字符串的函数,使用方便。几乎所有版本的C都提供这些函数。下面介绍几种常用的函数。
1.puts(字符数组)
其作用是:将一个字符串(以'\0'结束的字符序列)输出到终端。假如已定义str是一个字符数组名,且该数组已被初始化为"China"。则执行
puts(str);
其结果是在终端上输出China.由于可以用printf数输出字符串,因此puts函数用的不多。用puts函数输出的字符串中可以包含转义字符。例如:
char str[]-{"China\nBeijing"} ;
puts(str);输出: China Beijing
在输出时将字符串结束标志'\o'转换成'\n',即输出完字符串后换行。
2.gets(字符数组)
其作用是:从终端输人一个字符串到字符数组,并且得到一个函数值。该函数值是字符数组的起始地址。如执行下面的函数:
gets(str)
从键盘输入:Computer
将输入的字符串"Computer"送给字符数组str(请注意送给数组的共有9个字符,而不是8个字符),函数值为字符数组str的起始地址。一般利用gets函数的目的是向字符数组输入一个字符串,而不大关心其函数值。
注意:用puts和gets函数只能输入或输出一个字符串,不能写成
puts(str1,str2)或gets(strl,str2)
3.streat(字符数组1,字符数组2)
strcat 是STRing CATenate(字符串连接)的缩写。其作用是:连接两个字符数组中的字符串,把字符串2接到字符串1的后面,结果放在字符数组1,数调用后得到一个函数值-字符数组1的地址。例如:
char str1[30]={"People's Republic of ")
char str2[]={“China”}:
printf("%s",strcat(strl,str2));
输出:
People's Republic of China
说明:
(1)字符数组必须足够大,以便容纳接后的新字符串。本例中定义strl的长度为30,是足够大的,如果在定义时改用
str1[]=("People's Rebuplic of");就会出问题,因长度不够。
(2)连接前两个字符串的后面都有一个'\0',连接时将字符串1后面的'\0'取消,只在新串最后保留一个'\0'。
4.strcpy(字符数组1字符串2)
strcpy是STRing CoPY(字符串复制)的缩写。它是“字符串复制函数”。作用是将字符串2复制到字符数组1中去。例如:
char str1[10],str2[]=("China"};
strcpy(str1,str2);
执行后,strl的状态如图
C | h | i | n | a | \0 | \0 | \0 | \0 | \0 |
说明:
(1)字符数组1必须定义得足够大,以便容纳被复制的字符串。字符数组1的长度不应小于字符串2的长度。
(2)“字符数组1”必须写成数组名形式(如str1),“字符串2”可以是字符数组名,也可以是一个字符串常量。如
strcpy(strl,"China");作用与前相同。
- 复制时连同字符串后面的'\0'一起复制到字符数组1中。
- 不能用赋值语句将一个字符申常量或字符数组直接给一个字符数组。如下面两行都是不合法的: str1={"China"};
str1=str2;
而只能用strcpy函数处理。用赋值语句只能将一个字符赋给一个字符型变量或字符数组元素。如下面是合法的:
char a[5],c1,c2;
c1='A';c2='B’;’C’,’h’,’i’,’n’,’a’
a[0]='C';a[1]=’h’';a[2]=’i’;a[3]=’n’;a[4]=’a’;
(5)可以用strncpy函数将字符串2中前面n个字符复制到字符数组1中去。例如: strncpy(strl,str2,2);
作用是将str2中前面2个字符复制到 str1 中去,取代str1中前面的2个字符。
5.strcmp(字符串1,字符串2)
strcmp是STRing CoMPare(字符串比较)的缩写。作用是比较字符串1和字符串2。例如:
strcmp(str1,str2);
strcmp("China","Korea");
strcmp(str1,"Beijing");
字符串比较的规则与其他语言中的规则相同,即对两个字符串自左至右逐个字符相比(按ASCII码值大小比较),直到出现不同的字符或遇到'\0'为止。如全部字符相同,则认为相等;若出现不相同的字符,则以第一个不相同的字符的比较结果为准。例如:
"A"<"B","a">"A","computer">"compare","these">"that","36+54" >"! $&#","CHINA">"CANADA" ,"DOG"<"cat".
如果参加比较的两个字符串都由英文字母组成,则有一个简单的规律:在英文字典中位置在后面的为“大”。例如computer在字典中的位置在compare之后。但应注意小写字母比大写字母“大”,所以“DOG”<“cat”。
比较的结果由函数值带回。
(1)如果字符串1=字符串2,函数值为0。
(2)如果字符串1>字符串2,函数值为一正整数。
(3)如果字符串 1<字符串2,函数值为一负整数。
注意:对两个字符串比较,不能用以下形式:
if(str1==str2) printf("yes");
而只能用
If(strcmp(str1,str2)==0)printf("yes");
6. strlen(字符数组)
strlen是STRing LENght(字符串长度)的缩写。它是测试字符串长度的函数。函数的值为字符串中的实际长度,不包括\0'在内。如:
char str[10]={“China”};
printf("%d",strlen(str));
输出结果不是10,也不是6,而是5。也可以直接测试字符串常量的长度,如
strlen(“China”):
7.strlwr(字符串)
strlwr是STRing LoWeRcase(字符串小写)的缩写。函数的作用是将字符串中大写字母换成小写字母。
8. strupr(字符串)
strupr是STRing UPpeRcase(字符串大写)的缩写。函数的作用是将字符串中小写字母换成大写字母。
以上介绍了常用的8种字符串处理函数,应当再次强调:库函数并非C语言本身的组成部分,而是人们为使用方便而编写、提供大家使用的公共函数。每个系统提供的函数数量和函数名、函数功能都不尽相同,使用时要小心,必要时查一下库函数手册。当然,有一些基本的函数(包括函数名和函数功能),不同的系统所提供的是相同的,这就为程序的通用性提供了基础。
七、字符数组应用举例
例输入一行字符,统计其中有多少个单词,单词之间用空格分隔开。程序如下:
#include <stdio.h>
main()
{
char string[81];
Int i,num=0,word=0;
char c;
gets(string);
for(i=0;(c=string[i])!=\0'i++)
if(c==’ ’)word=0;
else if(word==0)
{word=1;
num++;
}
printf("There are %d words in the line. \n",num);
运行情况如下: I am a boy.
There are 4 words in the line.
程序中变量i作为循环变量,num用来统计单词个数,word作为判别是否单词的标志,若word==0表示未出现单词,如出现单词word置成1。
解题的思路是这样的:单词的数目可以由空格出现的次数决定(连续的若干个空格作为出现一次空格;一行开头的空格不统计在内)。如果测出某一个字符为非空格,而它的前面的字符是空格,则表示“新的单词开始了”,此时使num(单词数)累加1。如果当前字符为非空格而其前面的字符也是非空格,则意味着仍然是原来那个单词的继续,num不应再累加1。前面一个字符是否空格可以从word的值看出来,若word等于0,则表示前一个字符是空格;如果word等于1,意味着前一个字符为非空格。
程序中for语句中的“循环条件”为(c=string[i])!=’\0’
它的作用是先将字符数组的某一元素(一个字符)赋给字符变量c。此时赋值表达式的值就是该字符,然后再判断它是否结束符。这个“循环条件”包含了一个赋值操作和一个关系运算。可以看到用for循环可以使程序简练。
如前所述,可以把str[0]、str[1]str[2]看作3个一维字符数组,它们各有20元素。可以把它们如同一维数组那样进行处理。可以用gets函数分别读入3个字符串。经过二次比较,就可得到值最大者,把它放在一维字符数组string中。
程序如下:
#include<stdio.h>
#include<string. h>
main()
{
char string[20];
char str[3] [20];
int i;
for (i=0;i<3;i++)
gets (str[i]);
if (strcmp(str[0],str[1])>0)
strcpy(string,str[0]);
else strcpy(string;str[1]);
if(strcmp(str[2],string)>0)
strcpy(string,str[2]);
printf("\nthe largest string is:\n%s\n",string);
}
运行结果如下; CHINA
HOLLAND
AMERICA
the largest string is :
HOLLAND
当然,这个也可以不采用二维数组,而设3个一维字符数组来处理。