20{
21//通过printf输出char_array[]中的字符串
22printf("char_array的第%d个是%d\n",i,char_array[i]);
23}
24}
运行结果如图7-6所示。
图7-6运行结果
在例7-2中,定义了一个字符数组char_array,该字符数组中保存的是字符串“hello”。
从图7-6中可以看出,字符数组char_array输出了6个字符,并且最后一个字符是空字符‘\0’。
3、获取字符串长度
字符串用数组来存储,第2章中曾学过用sizeof()运算符来求各种数据类型的长度,那么sizeof()运算符也可以用来求字符串的度。
例如sizeof(“abcde”)。
除了可以使用sizeof运算符获取字符串长度外,还可以使用strlen()函数来获取,strlen()函数声明如下:
unsignedintstrlen(char*s);
在上述声明中,s是指向字符串的指针,返回值是字符串的长度。
需要注意的是使用strlen()函数得到的字符串的长度,并不包括末尾的空字符‘\0’。
接下来通过一个案例来演示如何使用strlen()函数获取用户输入字符串的长度,如例7-3所示。
25#include
26#include
27voidmain()
28{
29charstr[10]="hello";
30printf("%d\n",strlen(str));
31printf("%d\n",strlen("hello"));
32printf("%d\n",sizeof(str));
33printf("%d\n",sizeof("hello"));
34}
运行结果如图7-7所示。
图7-7获取用户输入字符串的长度
从图7-7中可以看出,strlen(“str”)和strlen(“hello”)的大小都为5,都只包含字符串中的字符个数,不包括末尾的’\0’。
sizeof(str)的大小为10,是数组在内存所占的大小;sizeof(“hello”)的大小为6,包括”hello”字符串中的5个字符和末尾的’\0’。
通过例7-3可以看出sizeof()与strlen()在求字符串时是有所不同的,我们来简单总结一下strlen()函数与sizeof运算符的区别,具体如下:
(1)sizeof是运算符;strlen()是C语言标准库函数,包含在string.h头文件中。
(2)sizeof运算符功能是获得所建立的对象的字节大小,计算的是类型所占内存的多少;strlen()函数是获得字符串所占内存的有效字节数。
(3)sizeof运算符的参数可以是数组、指针、类型、对象、函数等;strlen()函数的参数必须是字符型指针,即它的参数必须是以字符串为目标,且必须是以’\0’结尾。
(4)sizeof运算符计算大小是在编绎时就完成,因此不能用来计算动态分配内存的大小;strlen()结果要在运行时才能计算出来。
多学一招:
字符与字符串的转换
C语言中的字符串实际上是字符数组,而C语言中的字符则是一种基本数据类型。
因此在字符和字符串之间进行转换是很容易的。
接下来,将chara='A'转为字符串,具体步骤如下:
(1)创建一个长度为2的字符数组
chara_str[2];
(2)将第一个元素设置为对应的字符,第二个元素设置为空字符。
a_str[0]=a;
a_str[1]='\0';
同理,将字符串转为字符也很简单,具体示例如下:
chara_str[]="AB";
chara=a_str[0];
charb=a_str[1];
在上述代码中,定义了一个字符数组a_str,该字符数组中保存的是字符串“AB”。
只要将字符串中的每个字符赋给字符变量a和b,就可以完成字符串转为字符的操作。
脚下留心:
空字符‘\0’的使用
通过前面的讲解不难发现,字符串其实就是一个以空字符‘\0’结尾的字符数组。
先来看一个例子,如例7-4所示。
35#include
36voidmain()
37{
38charchar_array[]={'h','e','l','l','o','\0','W','o','r','l','d'};
39printf("%s\n",char_array);
40}
运行结果如图7-8所示。
图7-8运行结果
从图7-8中可以看出,字符数组char_array中存储的字符串只输出了一部分。
这是因为程序中字符串的结束是通过‘\0’来检测的。
在例7-4中,字符数组char_array的第6个字符是‘\0’,程序会认为到了字符串的末尾,从而输出“hello”。
同样,如果定义一个字符数组时,忘记在字符数组的末尾添加空字符‘\0’,程序也会出现一些奇怪的现象,接下来,通过一个案例来演示,如例7-5所示。
41#include
42voidmain()
43{
44charchar_array[]={'h','e','l','l','o'};
45printf("%s\n",char_array);
46}
运行结果如图7-9所示。
图7-9没有空字符结尾的字符串
从图7-9中可以看出,程序不仅输出了我们想要的“hello”,还输出了一些奇怪的字符。
这是因为在使用printf()函数输出字符串时,如果没有遇到空字符,就会继续后面的内存数据,直到遇到第一个空字符‘\0’。
因此,在定义字符数组时,一定不能忘记空字符‘\0’。
字符串与指针
在C语言中,字符型指针用char*来定义,它不仅可以指向一个字符型常量,还可以指向一个字符串。
为了描述字符串与指针之间的关系,先来看一段示例代码,具体如下:
charchar_array[]="hello";
char*chr=char_array;
上述代码定义了一个字符型指针chr,该指针指向字符串“hello”,字符指针chr和字符串“hello”的关系如图7-10所示。
图7-10指针chr指向字符串hello
从图7-10中可以看出,字符指针chr既指向了字符‘h’,又指向了字符串“hello”。
这是因为字符‘h’位于字符串“hello”的起始处,因此,chr也是指向字符‘h’的字符指针。
在输出这块内存中的数据时,根据输出格式的不同会输出不同的数据。
如果以%c输出,则只输出当前指针指向的字符即可。
如果以%s输出,则会输出后面连续的内存空间的数据,直到遇到’\0’停止。
为了帮助读者更好地理解指针,接下来,通过一个案例来演示如何使用指针修改字符串中的字符,如例7-6所示。
47#include
48intmain()
49{
50charchar_array[]="helloworld!
";
51char*ptr;
52printf("修改前的字符串如下:
%s\n",char_array);
53ptr=char_array;//将一个字符型指针指向字符型数组char_array的首地址
54//开始修改字符串
55while(*ptr!
='\0')//检测是否到串末尾
56{
57if(*ptr=='')
58{
59*ptr='!
';
60}
61ptr++;
62}
63printf("修改后的字符串如下:
%s\n",char_array);
64return0;
65}
运行结果如图7-11所示。
图7-11运行结果
在例7-6中,由于字符串总是以空字符‘\0’结尾,因此在使用while循环遍历字符串时,只需要判断当前位置的字符是否为空即可。
从图7-11中可以看出,使用自增的字符指针可以实现字符串的访问和修改。
字符数组与字符指针
字符串用字符数组存储,也可以取地址赋值给字符型指针。
字符数组与字符指针围绕字符串就发生了千丝万缕的联系,这一节来总结一下两者的区别与联系:
1、存储方式
字符数组在用字符串初始化时,这个字符串就存放在了字符数组开辟的内存空间中;而字符指针变量在用字符串常量初始化时,指针变量中存储的是字符串的首地址,但字符串存储在常量区。
上面的文字描述稍显晦涩难懂,下面通过一段示例代码来辅助理解,具体如下:
charstr[6]="hello”;
char*p="hello";
上面两行代码在内存区的存储方式如图7-12所示。
图7-12字符数组与字符指针的字符串的存储方式
从图7-12可以看出,字符数组str在使用字符串常量“hello”初始化时,字符串存储在栈区,而指针变量p在使用字符串常量“hello”初始化时,栈区只存放了字符串的首地址,而字符串却存储在常量区。
存储在栈区、堆区、静态区上的数据都是可更改的,但是常量区的数据是不可变的,一量赋值就不能再改变。
2、初始化及赋值方式
(1)初始化方式
可以对字符指针变量赋值,但不能对数组名赋值。
示例代码如下:
char*p="hello";//等价于char*p;p="hello";
charstr[6]="hello";//charstr[6];str="hello";这种写法错误
(2)赋值方式
字符数组(或字符串)之间只能单个元素赋值或使用复制函数;字符指针则无此限制。
示例代码如下:
char*p1="hello",*p2;p2=p1;
charstr1[6]="hello",str2[6];//str2=str1;错误,数组赋值,数组章节讲解过,这里不再赘述
3、字符串指针与数组名
字符指针变量的值是可以改变的,而数组名是一个指针常量,其值不可以改变。
代码示例如下:
char*p="chuanzhiboke,niu!
";
p+=7;
而对字符数组charstr[6]=“hello”;来说,数组名是常量指针,是不可以改变的。
4、字符串中字符的引用
数组可以用下标法和地址法引用数组元素;字符指针也可以用地址法、指针变量加下标法来引用字符串的字符元素。
示例代码如下:
char*str[100]="chuanzhiboke,niu!
";
charch1=str[6];
char*p="chuanzhiboke,niu!
";
charch2=p[6];//等价于charch2=*(p+6);
关于字符串、字符数组、字符指针的区别与联系的诸多细节,需要读者在学习应用当中慢慢体会。
字符串的输入输出
在第四章中大家学习了printf()和scanf()函数,它们分别用于向控制台中输出内容和从控制台上接收用户的输入。
针对字符串的读取和输出,C语言还专门提供了puts()函数gets()函数,本节将针对这两个函数进行详细地讲解。
gets()函数
gets()函数用于从控制台读入用户输入的字符串,其语法格式如下:
char*gets(char*str);
从上面的语法格式可以看出,函数gets()接收一个字符指针作为参数,该字符指针应指向已经分配好空间的一个字符数组。
当调用gets()函数时,将读取到的字符串赋值给该字符数组。
当使用gets()函数读入用户输入的字符串时,会把换行符之前所有的字符读取进来(不包括换行符本身),并在字符串的末尾添加一个空字符‘\0’用来标记字符串的结束,读取到的字符串会以指针形式返回。
为了帮助大家更好地理解gets()函数的用法,接下来通过一个具体的案例实现读取用户输入的电话号码,如例7-7所示。
66#include
67#pragmawarning
68voidmain()
69{
70charphoneNumber[12];
71printf("请输入手机号码:
");
72gets(phoneNumber);
73printf("您的手机号码是:
%s\n",phoneNumber);
74}
运行结果如图7-13所示。
图7-13使用gets()函数读入手机号码
从图7-13中可以看出,当用户输入电话号码后,程序会自动在控制台输出号码,这是因为程序的第6行代码使用了gets()函数,该函数可以读取用户输入的手机号码,由此可见使用gets()函数可以实现字符串的输入操作。
注意:
1、在VS开发工具中,在例7-7的代码中加入预处理指令#pragmawarning(disable:
4996)是要消除警告。
因为gets()是不安全的函数,如果不加上此条预处理指令,VS会发出警告不让通过编绎。
读者可以换用安全的fgets()函数,此函数与gets()函数用法一样。
2、使用gets()函数时,用户定义的字符数组的长度必须大于用户输入的字符串长度,否则程序会发生“缓冲区溢出”错误。
puts()函数
puts()函数用于向控制台输出一整行字符串,其语法格式如下:
intputs(constchar*str);
从上面的语法格式中可以看出,函数puts()接收的参数是一个字符串指针,该指针指向要输出的字符串,并且会自动在字符串末尾追加换行符‘\n’。
如果调用成功后返回一个int类型的正数,否则返回EOF。
为了让大家更深入的理解puts()函数的用法,接下来通过具体的案例来演示,如例7-8所示。
75#include
76voidmain()
77{
78charbuf[100];
79puts("请输入一个字符串:
");
80gets(buf);
81puts("您输入的是:
\n");
82puts(buf);
83}
运行结果如图7-14所示。
图7-14运行结果
从图7-14的运行结果可以看出,在“您输入的是:
\n”后面出现了两次换行,这是因为使用puts()函数输出字符串时,会在字符串的末尾追加一个换行符‘\n’,而输出的字符串本身也有一个换行符‘\n’。
多学一招:
printf()与puts()的区别
与puts()函数相比,printf()函数不会一次输出一整行字符串,而是根据格式化字符串输出一个个“单词”。
由于进行了额外的数据格式化工作,因此在性能上,printf()比puts()慢。
另外printf()还可以直接输出各种不同类型的数据,从这个角度来说,它比puts()使用的更广泛。
字符串函数
在程序中,经常需要对字符串进行操作,如字符串的比较、查找、替换等。
在C语言中,提供了许多操作字符串的函数,这些函数都位于string.h文件中。
本节将针对这些函数进行详细地讲解。
字符串比较函数
在程序中,经常需要对字符串进行比较,如判断用户输入的密码是否正确,为此C语言提供了strcmp()函数和strncmp()函数。
关于这两个函数的相关讲解,具体如下:
1、strcmp()函数
在C语言中,strcmp()函数用于比较两个字符串的内容是否相等,其语法格式如下所示:
intstrcmp(constchar*str1,constchar*str2);
在上述语法格式中,参数str1和str2代表要进行比较的两个字符串。
如果两个字符串的内容相同,strcmp()返回0,否则返回非零值。
为了帮助大家更好地理解strcmp()函数的用法,接下来通过案例来演示用户名和密码的比较,具体如例7-9所示。
84#include
85#include
86voidmain()
87{
88charusername[100];//定义存放用户名的字符数组
89charpassword[100];//定义存放密码的字符数组
90printf("登录\n");
91printf("请输入用户名:
");
92gets(username);//获取用户输入的用户名
93printf("请输入密码:
");