C有3个标准库函数用于打印字符串: put()、 fputs()和printf()。
puts()函数
每个字符串独占一行, 因为puts()在显示字符串时会自动在其末尾添加一个换行符。
用双引号括起来的内容是字符串常量, 且被视为该字符串的地址。 另外, 储存字符串的数组名也被看作是地址。
puts()函数在遇到空字符时就停止输出, 所以必须确保有空字符。
编译器把side_a数组储存在dont数组之后, 所以puts()一直输出至遇到side_a中的空字符。
fputs()函数
fputs()函数的第 2 个参数指明要写入数据的文件。 如果要打印在显示器上, 可以用定义在stdio.h中的stdout(标准输出) 作为该参数。
与puts()不同, fputs()不会在输出的末尾添加换行符。gets()丢弃输入中的换行符, 但是puts()在输出中添加换行符。 另一方面, fgets()保留输入中的换行符, fputs()不在输出中添加换行符。
如果gets()读到文件结尾会返回空指针。 对空指针求值为0(即为假)
如果混合使用 fgets()输入和puts()输出, 每个待显示的字符串末尾就会有两个换行符。puts()应与gets()配对使用, fputs()应与fgets()配对使用。
printf()函数
与puts()不同的是, printf()不会自动在每个字符串末尾加上一个换行符。 因此, 必须在参数中指明应该在哪里使用换行符。
printf("%s\n", string);
和下面的语句效果相同:
puts(string);
printf()的形式更复杂些, 需要输入更多代码, 而且计算机执行的时间也更长使用 printf()打印多个字符串更加简单。
自定义输入/输出函数
指向char的指针string最初指向传入参数的首元素。 因为该函数不会改变传入的字符串, 所以形参使用了const限定符。
打印了首元素的内容后, 指针递增1, 指向下一个元素。 while循环重复这一过程, 直到指针指向包含空字符的元素。++的优先级高于*, 因此putchar(*string++)打印string指向的值, 递增的是string本身, 而不是递增它所指向的字符。
每个字符串都以空字符结尾, 所以不用给函数传递字符串的大小。 函数依次处理每个字符, 直至遇到空字符。
如果要处理字符串, 实际参数可以是数组名、 用双引号括起来的字符串, 或声明为 char *类型的变量。 用const char * string可以提醒用户: 实际参数不一定是数组。
假设要设计一个类似puts()的函数, 而且该函数还给出待打印字符的个数。添加一个功能很简单。
使用一个简单的驱动程序测试put1()和put2(), 并演示了嵌套函数的调用。
程序中使用 printf()打印 put2()的值, 但是为了获得 put2()的返回值, 计算机必须先执行put2(), 因此在打印字符数之前先打印了传递给该函数的字符串。
字符串函数
C库提供了多个处理字符串的函数, ANSI C把这些函数的原型放在string.h头文件中。 其中最常用的函数有 strlen()、 strcat()、 strcmp()、strncmp()、 strcpy()和 strncpy()。 另外, 还有sprintf()函数, 其原型在stdio.h头文件中。
strlen()函数
strlen()函数用于统计字符串的长度。
fit()函数把第39个元素的逗号替换成'\0'字符。 puts()函数在空字符处停止输出, 并忽略其余字符。这些字符还在缓冲区中,puts(mesg + 8); 的函数调用把这些字符打印了出来。
表达式mesg + 39是mesg[39]的地址, 该地址上储存的是空格字符。 所以put()显示该字符并继续输出直至遇到原来字符串中的空字符。
strcat()函数
strcat()(用于拼接字符串) 函数接受两个字符串作为参数。 该函数把第2个字符串的备份附加在第1个字符串末尾, 并把拼接后形成的新字符串作为第1个字符串, 第2个字符串不变。strcat()函数的类型是char *(即, 指向char的指针) 。 strcat()函数返回第1个参数, 即拼接第2个字符串后的第1个字符串的地址。
flower改变了, 而addon保持不变。
strncat()函数
strcat()函数无法检查第1个数组是否能容纳第2个字符串。 如果分配给第1个数组的空间不够大, 多出来的字符溢出到相邻存储单元时就会出问题。用strlen()查看第1个数组的长度。或者, 用strncat(), 该函数的第3 个参数指定了最大添加字符数。
算上空字符(无论哪种情况都要添加空字符) , bugs数组应该足够大, 以容纳原始字符串(不包含空字符) 、 添加原始字符串在后面的13个字符和末尾的空字符。
strcat()和 gets()类似, 也会导致缓冲区溢出。
strcmp()函数
这个程序看上去没问题, 但是运行后却不对劲。 ANSWER和try都是指针, 所以try != ANSWER检查的不是两个字符串是否相等, 而是这两个字符串的地址是否相同。 因为ANSWE和try储存在不同的位置, 所以这两个地址不可能相同, 因此, 无论用户输入什么, 程序都提示输入不正确。
该函数要比较的是字符串的内容, 不是字符串的地址。
修改后
strcmp()函数比较的是字符串, 不是整个数组, 这是非常好的功能。 虽然数组try占用了40字节, 而储存在其中的"Grant"只占用了6字节(还有一个用来放空字符) , strcmp()函数只会比较try中第1个空字符前面的部分。 所以, 可以用strcmp()比较储存在不同大小数组中的字符串。
strcmp()的返回值
如果strcmp()比较的字符串不同, 它会返回什么值
strcmp()比较"A"和本身, 返回0; 比较"A"和"B", 返回-1; 比较"B"和"A", 返回1。 这说明, 如果在字母表中第1个字符串位于第2个字符串前面, strcmp()中就返回负数; 反之, strcmp()则返回正数。ASCII标准规定, 在字母表中, 如果第1个字符串在第2个字符串前面,strcmp()返回一个负数; 如果两个字符串相同, strcmp()返回0; 如果第1个字符串在第2个字符串后面, strcmp()返回正数。
一般而言, strcmp()会依次比较每个字符, 直到发现第 1 对不同的字符为止。 然后, 返回相应的值。strcmp()比较所有的字符, 不只是字母。
strcmp()函数比较的是字符串, 不是字符, 所以其参数应该是字符串(如"apples"和"A") , 而不是字符(如'A') 。 但是, char 类型实际上是整数类型, 所以可以使用关系运算符来比较字符。
程序在读到EOF字符(这种情况下s_gets()返回NULL) 、 用户输入quit或输入项达到LIM时退出。
strncmp()函数
strcmp()函数比较字符串中的字符, 直到发现不同的字符为止, 这一过程可能会持续到字符串的末尾。 而strncmp()函数在比较两个字符串时, 可以比较到字符不同的地方, 也可以只比较第3个参数指定的字符数。