4. 字符串

之前我一直对字符串避而不谈,不做详细解释,现在已经具备了必要的基础知识,可以深入讨论一下字符串了。字符串可以看作一个数组,它的元素是字符型的,例如字符串"Hello, world.\n"图示如下:

图 8.2. 字符串

字符串

注意末尾有一个字符'\0'表示字符串结束。这里的\0是ASCII码的八进制表示,也就是ASCII码为0的那个字符。前面用过的数组都有一个数组名,数组元素可以通过数组名加下标的方式访问。而字符串字面值也可以像数组名一样使用,可以加下标访问其中的字符:

char c = "Hello, world.\n"[0];

但是通过下标修改其中的字符却是不允许的:

"Hello, world.\n"[0] = 'A';

这行代码会产生编译错误,说字符串字面值是只读的,不允许修改。字符串字面值还有一点和数组名类似,做右值使用时自动转换成指向首元素的指针,所以printf("hello world")其实是传一个指针参数给printf

前面讲过数组可以像结构体一样初始化,如果是字符数组,也可以用一个字符串字面值来初始化:

char str[10] = "Hello";

相当于:

char str[10] = { 'H', 'e', 'l', 'l', 'o', '\0' };

str的后四个元素没有指定,自动初始化为0,即'\0'字符。注意,虽然字符串字面值"Hello"是只读的,但用它初始化的数组str却是可读可写的。数组str保存了一串字符,以'\0'结尾,也可以叫字符串。在本书中字符串这个概念指的是以'\0'结尾的一串字符,可能是像str这种数组,也可能是像"Hello"这种字符串字面值。

如果用于初始化的字符串字面值比数组还长,比如:

char str[10] = "Hello, world.\n";

则数组str只包含字符串的前10个字符,不包含'\0'。这种情况编译器一般会给出警告。如果要用一个字符串字面值准确地初始化一个字符数组,最好的办法是不指定数组的长度,而让编译器自动计算:

char str[] = "Hello, world.\n";

字符串字面值的长度包括'\0'在内一共15个字符,编译器会确定数组str的长度为15。

补充一点,printf函数的格式化字符串中可以用%s表示字符串的占位符。在学字符数组以前,我们用%s没什么意义,因为

printf("string: %s\n", "Hello");

还不如写成

printf("string: Hello\n");

但现在字符串可以保存在一个数组里面,用%s来打印就很有必要了:

printf("string: %s\n", str);

printf会从数组str的开头一直打印到'\0'字符为止('\0'本身不打印)。这其实是一个危险的信号:如果数组str中没有'\0',那么printf就会打印出界,后果和前面讲的数组访问越界一样诡异:有时候打印出乱码,有时候看起来没错误,有时候引起程序崩溃。