字符串(character string)是一个或多个字符的序列,如下所示:
"Zing went the strings of my heart!"
双引号不是字符串的一部分。双引号仅告知编译器它括起来的是字符串,正如单引号用于标识单个字符一样。
一、char 类型数组和 null 字符
C 语言没有专门用于存储字符串的变量类型,字符串都被存储在 char 类型的数组中。数组由连续的存储单元组成,字符串中的字符被存储在相邻的存储单元中,每个单元存储一个字符(见图 1)。
注意图 1 中数组末尾位置的字符 \0
。这是空字符(null character),C 语言用它标记字符串的结束。空字符不是数字 0,它是非打印字符,其 ASCII 码值是(或等价于)0。C 中的字符串一定以空字符结束,这意味着数组的容量必须至少比待存储字符串中的字符数多 1。因此,程序清单 1 中有 40 个存储单元的字符串,只能存储 39 个字符,剩下一个字节留给空字符。
程序清单 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;
}
运行 talkback.c 程序,输入结果如下:
Hi! What's your first name?
Christine
Christine, what's your weight in pounds?
154
Well, Christine, your volume is 2.47 cubic feet.
Also, your first name has 9 letters,
and we have 40 bytes to store it.
那么,什么是数组?可以把数组看作是一行连续的多个存储单元。用更正式的说法是,数组是同类型数据元素的有序序列。程序清单 1 通过以下声明创建了一个包含 40 个存储单元(或元素)的数组,每个单元存储一个 char 类型的值:
char name[40];
name 后面的方括号表明这是一个数组,方括号中的 40 表明该数组中的元素数量。char 表明每个元素的类型(见图 2)。
字符串看上去比较复杂!必须先创建一个数组,把字符串中的字符逐个放入数组,还要记得在末尾加上一个\0。还好,计算机可以自己处理这些细节。
二、使用字符串
试着运行程序清单 2,使用字符串其实很简单。
程序清单 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
(见图 3)。
三、strlen() 函数
strlen() 函数给出字符串中的字符长度。因为 1 字节存储一个字符,读者可能认为把两种方法应用于字符串得到的结果相同,但事实并非如此。请根据程序清单 3,在程序清单 2 中添加几行代码,看看为什么会这样。
程序清单 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);
printf("Hello, %s. %s\n", name, PRAISE);
printf("Your name of %zd letters occupies %zd memory cells.\n",
strlen(name), sizeof name);
printf("The phrase of praise has %zd letters ",
strlen(PRAISE));
printf("and occupies %zd memory cells.\n", sizeof PRAISE);
return 0;
}
如果使用 ANSI C 之前的编译器,必须移除这一行:
#include <string.h>
string.h 头文件包含多个与字符串相关的函数原型,包括 strlen()。
一般而言,C 把函数库中相关的函数归为一类,并为每类函数提供一个头文件。例如,printf() 和 scanf() 都隶属标准输入和输出函数,使用 stdio.h 头文件。string.h 头文件中包含了 strlen() 函数和其他一些与字符串相关的函数(如拷贝字符串的函数和字符串查找函数)。
注意,程序清单 3 使用了两种方法处理很长的 printf() 语句。第 1 种方法是将 printf() 语句分为两行(可以在参数之间断为两行,但是不要在双引号中的字符串中间断开);第 2 种方法是使用两个 printf() 语句打印一行内容,只在第 2 条 printf() 语句中使用换行符(\n)。运行该程序,其交互输出如下:
What's your name? Serendipity Chance
Hello, Serendipity. You are an extraordinary being.
Your name of 11 letters occupies 40 memory cells.
The phrase of praise has 31 letters and occupies 32 memory cells.
sizeof 运算符报告,name 数组有 40 个存储单元。但是,只有前 11 个单元用来存储 Serendipity,所以 strlen() 得出的结果是 11。name 数组的第 12 个单元存储空字符,strlen() 并未将其计入。图 4 演示了这个概念。
对于 PRAISE,用 strlen() 得出的也是字符串中的字符数(包括空格和标点符号)。然而,sizeof 运算符给出的数更大,因为它把字符串末尾不可见的空字符也计算在内。该程序并未明确告诉计算机要给字符串预留多少空间,所以它必须计算双引号内的字符数。
C99 和 C11 标准专门为 sizeof 运算符的返回类型添加了 %zd
转换说明,这对于 strlen() 同样适用。对于早期的 C,还要知道 sizeof 和 strlen() 返回的实际类型(通常是 unsigned 或 unsigned long)。
另外,还要注意一点:sizeof 可以使用圆括号。何时使用圆括号取决于运算对象是类型还是特定量。运算对象是类型时,圆括号必不可少,但是对于特定量,圆括号可有可无。也就是说,对于类型,应写成 sizeof(char) 或 sizeof(float);对于特定量,可写成 sizeof name 或 sizeof 6.28。尽管如此,还是建议所有情况下都使用圆括号,如 sizeof(6.28)。
程序清单 3 中使用 strlen() 和 sizeof,完全是为了满足读者的好奇心。在实际应用中,strlen() 和 sizeof 是非常重要的编程工具。例如,在各种要处理字符串的程序中,strlen() 很有用。
原文:C 语言字符串简介
(完)