printf和scanf的作用是程序和外设进行交互的接口,
printf可以将程序输出到屏幕上或打印机上;
scanf可以将键盘的字符输入到程序中;
但是printf和scanf并不是让程序和外设直接交互的,因为直接交互的话,一是外设读写速度慢,而是频繁读写外设易造成损伤,
因此在程序和外设之间,c语言在内存中增加了输入缓冲区和输出缓冲区,用于提高程序和外设读写的响应速度,详情见下文。
printf
printf的作用是在程序中输出指定内容到设备上:
printf("hello world!\n");
格式控制符
命令 | 功能 |
---|---|
%d | 十进制有符号整数 |
%u | 十进制无符号整数 |
%f | 浮点数 |
%s | 字符串 |
%c | 单个字符 |
%p | 指针的值 |
%e | 指数形式的浮点数 |
%x, %X | 无符号以十六进制表示的整数 |
%o | 无符号以八进制表示的整数 |
%g | 把输出的值按照%e或者%f类型中输出长度较小的方式输出 |
%p | 输出地址符 |
%lu | 32位无符号整数 |
%llu | 64位无符号整数 |
转义字符
命令 | 功能 |
---|---|
\n | 换行 |
\f | 清屏并换页 |
\r | 回车 |
\t | Tab符 |
\xhh | 表示一个ASCII码用16进表示 |
scanf
# include <stdio.h>
int scanf(const char *format, ...);
scanf("%d%d", &i, &j);
scanf的作用是输入指定内容到变量中;
格式化控制符的作用是将键盘的内容格式化后保存到变量中;
需要注意的是,scanf的变量参数是指针格式,普通变量一定要取址。
输入数据时,遇到三种情况则默认输入结束。第一,遇到空格,回车,tab键。第二,规定的宽度结束时。第三,非法输入。
格式化控制符
命令 | 功能 |
---|---|
%c | 把输入解释成一个字符,如果前边加一个空格,即" %c",那么的scanf会忽略输入设备前边的空白项,直到遇到第一个字符,并只读区一个字符。 |
%d | 把输入解释成一个有符号十进制整数 |
%e,%f,%g,%a | 把输入解释成一个浮点数(%a是C99的标准) |
%E,%F,%G,%A | 把输入解释成一个浮点数(%A是C99的标准) |
%i | 把输入解释成一个有符号十进制整数 |
%o | 把输入解释成一个有符号的八进制整数 |
%p | 把输入解释成一个指针(一个地址) |
%s | 把输入解释成一个字符串:输入的内容以第一个非空白字符作为开始,并且包含直到下一个空白字符的全部字符 |
%u | 把输入解释成一个无符号十进制整数 |
%x,%X | 把输入解释称一个有符号十六进制整数 |
缓冲区
缓冲区是c语言为加速程序和外设的读写速度,而维护的一块内存区域,
程序运行时,CPU从内存中读取程序数据,当程序需要对外设进行读写时,内存就需要和外设进行读写,而外设的读写速度很慢,因此c语言在内存中开辟了缓冲区,使大部分和外设交互的操作都在内存中进行。
printf
并不会逐个字符显示在屏幕上,而是先存在缓冲区,在特定的时机,将缓冲区一起显示在屏幕上:
1.遇到\n
2.程序结束 #include <unistd.h> sleep(3);
3.遇到输入语句
4.当缓冲区满4k时
5.fflush(stdout) 手动刷新
缓存区机制可以提高数据的读写速度,并且可以让低速的输入输出设备与高速的CPU之间协调工作。
scanf
并不会从键盘逐个字符读入程序,而是先存在缓冲区,在特定的时机,将缓冲区一起输入到程序:
1.当需要读取的是整型或者浮点型时,而缓冲区中的数据是字符或符号时,因此读取会失败,数据会残留在输入缓冲区中,就会影响接下来的所有数据的读取;
解决方法:借助scanf返回值判断是否接受成功,如果接受过程有失败情况,可以先清理输入缓冲区,在重新循环接收数据,直到完全接收成功为止;
2.fgets可以指定读取size-1个字符,如果有多余的会残留在输入缓冲区中,影响下一次输入;
解决方法:scanf(“%*[^\n]”); 表示从缓冲区中接受任意字符并丢弃,只要不是\n就一直进行,遇到\n才停下
3.当先输入整型或者浮点型数据,再紧接着输入字符、字符时,前一次残留的’\n’或空格,会影响字符和字符串的输入;
解决方法:%c前面加空格 ,scanf(" %c",&ch);
printf、scanf和缓冲区
使用回车键,回车符号\n也会记录在缓冲区。
例如:
#include<stdio.h>
int main()
{
char a='d';
char b='d';
printf("hello\n");
scanf("%c",&a); //第一次输入,获取到输入e
scanf("%c",&b); //第二次输入,获取的是缓冲区留下的\n,就是手动按下回车产生的\n
if(a=='\n'){printf("yyy");}
else if(a=='e'){printf("nnn");}
printf("%c\t",a);
if(b=='\n'){printf("uuu");}
printf("%c",b);
return 0;
}
这个程序本来应该有两次输入,但是此时输入一次程序就继续走完了:
hello
e
nnne uuu
这说明printf缓冲区和scanf缓冲区是两个独立的缓冲区,
scanf会从缓冲区读入数据,但\n留在了缓冲区,
printf会向缓冲区写入数据,且\n会留在缓冲区作为换行符。
cin会从缓冲区读入数据,直到空白,但会忽略开头的空白,且\n会留在缓冲区;
cout会向缓冲区写入数据,且\n会留在缓冲区作为换行符。
所以输入输出都会将\n留在缓冲区,只是读入时,遇到\n代表换行,
\n是一种控制符,代表换行;
在输入时,scanf将换行符\n作为空白符对待,cin将换行符\n作为空白符对待,getline将\n作为行信息获取一行;
在输出时,\n就是作为换行符,指导设备换行显示信息。
解决方案:
在%c前边加个空格,这种格式可以忽略前边的空白字符(空格、tab):
#include<stdio.h>
int main()
{
char a=' ';
printf("hello\n");
scanf("%c",&a);
scanf(" %c",&a);
if(a==' ') printf("a=%c\n",a);
else printf("a=%c\n",a);
return 0;
}
hello
e
r
a=r