10.2 字符串的输入与存储
10.2.1 为输入的字符串准备存储空间
字符串文字量解决了在程序运行之前就可以确定的、运行期间不发生变化的字符串的存储问题,但是当在程序运行时需要输入字符串时,为这个字符串事先安排存储空间是字符串文字量本身无法胜任的任务。
在这种情况下,代码编写者必须要为输入的字符串事先安排足够的存储空间,目前这只能通过定义字符数组来实现。比如:
输入字符串可以调用标准库函数scanf()完成。字符串的输入格式是“%s”。而且,由于是通过调用函数改变“str”数组的值,所以实参必须是指针。
注意对于数组名“str”不需要再做“&”,运算,因为“str”在这里已经是scanf()函数“%s”格式所要求的“char *”类型了。
特别要注意的是“str”数组不可以太小,否则一旦发生越界存储,后果将很难预料。
另一个库函数gets()也可以实现类似的功能,这个函数的原型是:
调用时也是把数组名“str”作为实参,也就是把指向字符的指针作为实参。
这个函数调用返回的值依然是“str”,如果输入不成功,返回的是“NULL”。其函数原型也在头文件“stdio.h”中。
使用scanf()函数和gets()函数输入字符串的区别在于,前者是把非空白字符作为字符串的开始,把空白字符作为字符串的结束,而gets()是把回车换行作为字符串的结束,并把遇到的第一个'\n'转变成'\0'存储。比如下面的键盘输入。
对scanf()函数来说,这是两个字符串"abv"和"aas",而对gets()函数来说,这是一个字符串"abv aas"。
初学者很容易忽略为程序运行时输入的字符串预备存储空间。下面是一个很常见的错误。
从指针的角度讲,“p”没有被赋初值,这样“p”可能指向内存中任意一个位置,由gets()输入的字符串可能被随意地放在了内存中任意的一个空间之内。这种错误的危害不言而喻,更可怕的是,这样写的程序有时可以似乎“正确”地运行。
所以在通过指针变量输入字符串时,一定要特别注意为字符串预备存储空间。比如:
这样就不会发生把字符串放在内存任意位置中的错误了。不过有一点要说明,上面的代码中,指针变量“p”是多余的,没有这个“p”程序也能正常完成任务。如:
这与前面代码段的功效是一样的。
10.2.2 puts()函数
这个标准库函数也可以用来输出字符串。其头文件也是“stdio.h”。它的函数原型是:
返回值可能为一个非负值或“EOF”(这是一个在“stdio.h”中定义的一个符号常量,它的值通常为“-1”),后者表示在输出过程中发生错误。
这个函数与printf()函数最大的区别在于它会把'\0'转化为'\n'输出。
puts("ABC");和printf("%s\n","ABC");的输出效果是一样的。
由此可见,puts()函数的功能是输出其实参所指向的字符以及其后的各个字符,遇到'\0'输出字符'\n'然后结束函数调用。
10.2.3 字符数组的初始化
字符数组具备一般的数组的所有性质,但是有一个特有的初始化方法。这种初始化的方法就是使用字符串文字量初始化。
可以简单地写为:
初学者在使用前一种方法时,特别容易犯的一个错误是忘记写字符串结尾的'\0'。这往往会导致输出字符序列之后,额外再输出一些不相干的字符,这是因为相应的函数会一直输出,直到遇到'\0'为止。如果涉及写操作,这个错误的性质就十分严重了。