程序清单4.1 talkback.c程序
// talkback.c -- 演示与用户交互
#include <stdio.h>
#include <string.h> // 提供strlen()函数的原型
#define DENSITY 62.4 // 人体密度(单位:磅/立方英尺)
int main()
{
float weight, volume;
int size, letters;
char name[40]; // name是一个可容纳40个字符的数组
printf("Hi! What's your first name?\n");
scanf("%s", name);
printf("%s, what's your weight in pounds?\n", name);
scanf("%f", &weight);
size = sizeof name;
letters = strlen(name);
volume = weight / DENSITY;
printf("Well, %s, your volume is %2.2f cubic feet.\n",
name, volume);
printf("Also, your first name has %d letters,\n",
letters);
printf("and we have %d bytes to store it.\n", size);
return 0;
}
该程序包含以下特性。
用数组(array)储存字符串(character string)。在该程序中,用户输入的名被储存在数组中,该数组占用内存中40个连续的字节,每个字节储存 一个字符值。
.
使用%s转换说明来处理字符串的输入和输出。注意,在scanf()中, name没有&前缀,而weight有(稍后解释,&weight和name都是地址)。
.
用C预处理器把字符常量DENSITY定义为62.4。
.
用C函数strlen()获取字符串的长度。
4.2 字符串简介
字符串(character string)是一个或多个字符的序列,如下所示:
“Zing went the strings of my heart!”
双引号不是字符串的一部分。双引号仅告知编译器它括起来的是字符串,正如单引号用于标识单个字符一样。
4.2.1 char类型数组和null字符
C语言没有专门用于储存字符串的变量类型,字符串都被储存在char类型的数组中。数组由连续的存储单元组成,字符串中的字符被储存在相邻的存储单元中,每个单元储存一个字符(见图4.1)。
注意图4.1中数组末尾位置的字符\0。这是空字符(null character),C语言用它标记字符串的结束。空字符不是数字0,它是非打印字符,其ASCII码值是(或等价于)0。C中的字符串一定以空字符结束,这意味着数组的容量必须至少比待存储字符串中的字符数多1。因此,程序清单4.1中有40个存储单元的字符串,只能储存39个字符,剩下一个字节留给空字符。
那么,什么是数组?可以把数组看作是一行连续的多个存储单元。用更正式的说法是,数组是同类型数据元素的有序序列。程序清单4.1通过以下声明创建了一个包含40个存储单元(或元素)的数组,每个单元储存一个char类型的值:
char name[40];
字符串看上去比较复杂!必须先创建一个数组,把字符串中的字符逐个放入数组,还要记得在末尾加上一个\0。还好,计算机可以自己处理这些细节。
4.2.2 使用字符串
试着运行程序清单4.2,使用字符串其实很简单。
程序清单4.2 praise1.c程序
/* praise1.c -- 使用不同类型的字符串 */
#include <stdio.h>
#define PRAISE "You are an extraordinary being."
int main(void)
{
char name[40];
printf("What's your name? ");
scanf("%s", name);
printf("Hello, %s.%s\n", name, PRAISE);
return 0;
}
%s告诉printf()打印一个字符串。%s出现了两次,因为程序要打印两个字符串:一个储存在name数组中;一个由PRAISE来表示。运行praise1.c,其输出如下所示:
What's your name? Angela Plains
Hello, Angela.You are an extraordinary being.
你不用亲自把空字符放入字符串末尾,scanf()在读取输入时就已完成这项工作。也不用在字符串常量PRAISE末尾添加空字符。稍后我们会解释#define指令,现在先理解PRAISE后面用双引号括起来的文本是一个字符串。编译器会在末尾加上空字符。
注意(这很重要),scanf()只读取了Angela Plains中的Angela,它在遇到第1个空白(空格、制表符或换行符)时就不再读取输入。因此,scanf()在读到Angela和Plains之间的空格时就停止了。一般而言,根据%s转换说明,scanf()只会读取字符串中的一个单词,而不是一整句。C语言还有其他的输入函数(如,fgets()),用于读取一般字符串。后面章节将详细介绍这些函数。
字符串和字符
字符串常量”x”和字符常量’x’不同。区别之一在于’x’是基本类型(char),而”x”是派生类型(char数组);区别之二是”x”实际上由两个字符组成:’x’和空字符\0(见图4.3)。
4.2.3 strlen()函数
上一章提到了 sizeof 运算符,它以字节为单位给出对象的大小。strlen()函数给出字符串中的字符长度。因为 1 字节储存一个字符,读者可能认为把两种方法应用于字符串得到的结果相同,但事实并非如此。请根据程序清单4.3,在程序清单4.2中添加几行代码,看看为什么会这样。
程序清单4.3 praise2.c程序
/* praise2.c */
// 如果编译器不识别%zd,尝试换成%u或%lu。
#include <stdio.h>
#include <string.h> /* 提供strlen()函数的原型 */
#define PRAISE "You are an extraordinary being."
int main(void)
{
char name[40];
printf("What's your name? ");
scanf("%s", name); //Serendipity Chance
printf("Hello, %s.%s\n", name, PRAISE);
//Hello, Serendipity.You are an extraordinary being.
printf("Your name of %zd letters occupies %zd memory cells.\n",
strlen(name), sizeof name);
//Your name of 11 letters occupies 40 memory cells
printf("The phrase of praise has %zd letters ",
strlen(PRAISE));
printf("and occupies %zd memory cells.\n", sizeof PRAISE);
//The phrase of praise has 31 letters and occupies 32 memory cells.
return 0;
}
如果使用ANSI C之前的编译器,必须移除这一行:
#include <string.h>
一般而言,C 把函数库中相关的函数归为一类,并为每类函数提供一个头文件。例如,printf()和scanf()都隶属标准输入和输出函数,使用stdio.h头文件。string.h头文件中包含了strlen()函数和其他一些与字符串相关的函数(如拷贝字符串的函数和字符串查找函数)。
注意,程序清单4.3使用了两种方法处理很长的printf()语句。第1种方法是将printf()语句分为两行(可以在参数之间断为两行,但是不要在双引号中的字符串中间断开);第 2 种方法是使用两个printf()语句打印一行内容,只在第2条printf()语句中使用换行符(\n)。
sizeof运算符报告,name数组有40个存储单元。但是,只有前11个单元用来储存Serendipity,所以strlen()得出的结果是11。name数组的第12个单元储存空字符,strlen()并未将其计入。
对于 PRAI