输出函数 puts()
头文件
stdio.h
函数原型
int puts(const char *string);
功能
向控制台输出一串字符串
即送一字符串到stdout中
参数
输入:字符串指针
输入可以是字符串数组,也可以是字符串常量
思考:
puts()函数的输入是一个字符串指针。当使用一个字符串数组名作为输入时,数组名此时表示一个指向数组的指针值,这符合puts()的输入要求,并能够正确输出。但是,当输入为一个字符串常量时,函数仍然能够正常的输出字符串。
可以作如下猜测:
1.字符串常量的取值实际上是一个地址值,即指针,这样才能够满足puts()函数对输入参数的要求,而这个指针应该指向了字符串常量实际所在的地址。
2.程序运行时,字符串常量将占用内存空间,这样才能保证puts()函数能通过指针找到要输出的数据。
既然字符串常量占用内存,那么应该就能够得到它的地址并输出。
输出:成功标志
函数的返回值为一个整形数据,标志函数执行是否成功。若字符串成功输出,则返回值为非负数(通常为0);若字符串输出失败,则返回值为EOF(通常为-1)。
特性
puts()和printf()都能够用来输出字符串,但是两者有些许不同。puts()在输出字符串后,会自动换行,而printf不会自动换行。两者有如下关系:
puts(s)↔printf(“%s\n”,s);
puts()函数仍然以’\0’来确定字符串的结尾。
- puts()函数只能够输出字符串,参数只能是字符指针。输入若为其他类型数据或指针,编译器报错,无法进行数据类型转换。
实验
这里使用的实验平台为VS2013,不排除在其他平台上会有出现不同实验结果的可能
字符串常量占用内存空间
前面说到,字符串常量在内存中将占用空间,并且能够取得它的地址并输出。下面用实验进行简单的证明。
#include <stdio.h>
int main()
{
char *p;
p = "Hello World!";
printf("%p\n%s\n", "Hello World!", "Hello World!");
printf("%p\n%s\n", p, p);
return 0;
}
输出如下
012A5858
Hello World!
012A5858
Hello World!
请按任意键继续. . .
可见字符串本身的取值就是一个指针,而字符串的数据则保存在指针所指向的内存空间。
另外可以发现,对于完全相同的字符串常量,在内存中只会记录一次,而不会重复占用空间。
puts()与printf()
二者最直观的区别就是,是否会自动换行。
#include <stdio.h>
int main()
{
puts("Hello.");
printf("Hello.\n" );
printf("Hello.");
return 0;
}
输出如下
Hello.
Hello.
Hello.请按任意键继续. . .
前两种输出方式的输出结果完全相同,第三种输出,printf中没有写入换行符,最后输出并没有换行。
可以看出,puts函数在输出时,会自动换行,而printf不会。
字符串长度由’\0’确定
puts输出字符串,从输入指针所指向的字符开始,到遇到的第一个’\0’字符结束。
#include <stdio.h>
int main()
{
char c[10] = "abcde\0fgh";
int i;
puts(c);
for (i = 0; i < 10; i++)
{
putchar(c[i]);
}
putchar('\n');
return 0;
}
输出如下
abcde
abcde fgh
请按任意键继续. . .
可以看到,虽然’\0’以后的字符确实在数组中存在,但是puts并没有对这些字符进行输出。
因此,puts函数输出字符串是以’\0’作为结束标记的。
另外可以看到,在对字符串数组初始化时,数组并没有在’\0’字符处就停止了拷贝,而是将字符串所有的字符全部拷贝进了数组。因此可以了解到,并不是所有对字符串的操作都是以’\0’字符作为结束标记的。
对此思考了一个问题,利用字符串对字符串数组进行初始化时,字符串是以怎样的数据类型进行解析的。
#include <stdio.h>
int main()
{
char a[10] = "abcde";
char *p = a;
char b[10] = p; //e1
char c= "abcde"; //e2
char d[10] = a; //e3
char e[10] = (const char[10])a; //e4
return 0;
}
编译出错,错误提示如下
e1:error C2440: “初始化”: 无法从“char *”转换为“char [10]”
e2:error C2440: “初始化”: 无法从“const char [6]”转换为“char”
e3:error C2075: “d”: 数组初始化需要大括号
e4:error C2440: “类型转换”: 无法从“char [10]”转换为“const char [10]”
从第一个错误信息可以了解到,字符指针不能用来初始化字符数组,也就是说字符串的数据类型并不是字符指针。
那字符串的数据类型到底是什么呢。于是看第二个错误信息。在这里故意将字符串赋值给一个字符变量,这自然是非法的,从而可以让编译器告诉我们,我们在将什么数据类型赋值给字符变量。
从第二个错误信息中,我们就可以了解到,编译器将字符串解析为了字符数组常量
,也证实了之前字符串不是指针的推断(指针与数组并不是同一种类型,它们在行为上有些类似,但是实质并不相同)。这也解释了为什么在初始化时,程序能够知道字符串的长度,因为数组类型不同于单纯的指针,它是有长度信息的。
既然初始化用的是常量字符数组,那字符数组变量能用于初始化吗。于是有了错误3和错误4。
错误3告诉我们,字符数组变量不能为字符数组初始化。
错误4告诉我们,尝试将字符数组变量类型转换为字符数组常量是非法的行为。
从上面的分析可以知道,在目前为止本人所知道的数据类型中,不考虑常规的大括号初始化方式,能直接为字符数组初始化的数据类型,就只有字符串,即字符数组常量。