printf()函数与scanf()函数
文章目录
格式化标准输出函数 ——printf()
概念:屏幕是系统的标准输出设备,从屏幕中输出数据被称为标准输出
相关函数:
printf():
格式化标准输出函数
函数原型:
#include <stdio.h> int printf(const char *format,...);//...表示可变参数,即表示可以只有format这一个参数,也可以表示后面还有其它参数。
函数说明:
printf系列函数能够对这种不同类型的参数进行格式化编排和输出。format参数控制输出的格式。
例如:
printf("%d\n",100); //默认以十进制int类型识别匹配并打印,输出一个整数 printf("%c\n",'A'); //输出字符 printf("%s\n",100); //输出字符串 printf("%x\n",100); //输出十六进制整数 printf("%f\n",3.14); //输出浮点数 printf("%lf\n",3.14); //输出double类型浮点数 printf("%p\n","abcd"); //输出地址 printf("%d,%d\n",100); //输出两个整数 printf("%d,%s\n","abcd");//输出字符串
类似%d、%f被称为格式控制符,一个格式控制符对应一个相应格式的数据。
注意
函数中的参数数目和类型必须与format字符串里的转换控制符匹配。
图中输出的A对应的ASCII为65,97作为ASCII时对应的字符为a。
前两句是因为输出格式问题导致输出结果与我们想要的不一致,但这都只是字符与ASCII码之间的转换;
第三个输出的97是正确的,至于为什么会输出@字符,我现在也还没弄清楚つ﹏⊂,求解。
第四个输出为字符串地址的十进制输出,一般都是直接用%p格式输出地址。
第五个输出不懂つ﹏⊂,格式是不对的,参数是一个地址,格式为字符串,为什么会什么都不输出,因为不匹配吗?求解。
最后一条输出字符串的格式是正确的
其它用于输出的函数
-
puts()
-
fputs()
-
sprintf()
返回值
printf()的返回值是所打印的字符的数目,如果有输出错误,那么printf()会返回一个负数。
这个返回值是它打印输出用途的附带功能,通常很少用到。使用返回值的一个可能原因是要检查输出错误。在向文件中输出而非向屏幕上输出的时候,这是很常见的。
从上图可以看出,包括空格、换行符都会被算入到num中。
相关题目
这是我以前做过的一道笔试题,具体题目已经找不到了,只能凭印象写了一下,不是原题,主要就是利用了printf函数的输出。记得好像没这么简单,感兴趣的朋友可以去找一下。
主要需要了解的知识点就是程序的执行顺序和printf的输出。
参数执行顺序
printf函数参数执行的问题:
VC6.0、VS code、VS2019 从右到左
linux,dev-c++ 从左到右
(这个是我网上找的,我在自己电脑的VS code、VS 2019和Ubuntu上验证,参数执行顺序都是从右到左(つ﹏⊂),感兴趣的朋友自己验证一下。
从图中可以看出,是先执行第三个表达式,再到第二个、第一个表达式。
只是将char类型换成了int类型,不懂为什么会出现一个13,在Linux上验证也是这个答案,求解。
打印较长字符串
-
使用多个printf()语句;
-
使用反斜线符号(/)和回车键的组合来结束一行。这就使得屏幕上的文本另起一行,并且在字符串中不会有换行符。不过下一行代码必须从最左边开始。
-
采用字符串连接的方法。如果一个用双引号引起来的字符串后面跟着另一个双引号引起来的字符串,且两者间只有空白字符,那么计算机将会把它们连成一个字符串。
比如:
printf("hello " "world!%d",num); printf("hello world!%d",num); //这两句是一样的
格式控制符:
- 十进制整数:%d
- 无符号十进制整数:%u
- 无符号八进制整数:%o
- 无符号十六进制整数:%x
- 字符:%c
- 字符串:%s
- 单精度浮点数:%f
- 双精度浮点数:%lf
- 长双精度浮点数:%Lf
- 地址:%p
- 以指数形式表示浮点数:%e
输出缓冲区
printf()函数什么时候真正把输出传输给屏幕?
先来了解一下缓冲区:
printf函数使用时会根据系统平台的不同具有缓冲区机制:
- 无缓存:只要输出一个字符,屏幕设备就显示一个
- 行缓存:遇到’\n’换行从缓冲区刷出,显示到屏幕上
- 全缓存:只有遇到对应的刷新操作或清缓冲区操作才显示在屏幕上,如fflush()函数。
首先,printf()语句将输出传递给缓冲区(buffer)的中介存储区域。缓冲区中的内容再不断地被传递给屏幕。
标准C规定在以下几种情况下将缓冲区内容传给屏幕:
缓冲区满的时候;
遇到换行符的时候;
需要输入的时候。
将缓冲区内容传给屏幕或文件称为刷新缓冲区。
例如前面两个printf()语句还未填满缓冲区,但后面有一条scanf语句,就会迫使内容传送给屏幕。
测试代码:
因为在自己电脑上的两个C编译软件上测试都没办法体现出这一点,只能选择在Linux上进行测试,通过gdb单步调试,得到下面的结果。
从上图可以看到,单步运行14、15行代码时,都没有输出内容,运行16行时遇到换行符,将缓冲区中的三块字符串一起输出;运行17、18行时没反应,到了19行遇到scanf函数需要输入时,将缓冲区中的内容输出来。
printf()的标志
标志 | 意义 |
---|---|
- | 数据左对齐:也就是说,会把数据打印在字段的左侧开始处;%-20s |
+ | 有符号的值若为正,则显示带加号的符号; 若为负,则带减号的符号; %+6.2f |
(空格) | 有符号的值若为正,则显示时带前导空格(但是不显示符号); 若为负,则带减号符号。+标志会覆盖空格标志; % 6.2f |
# | 使用转换说明的可选形式。若为%o格式,则以0开始; 若为%x和%X格式,则以0x或0X开始。 对于所有的浮点形式,#保证了即使不跟任何数字,也打印一个小数点字符。 对于%g和%G格式,它防止尾随零被删除。 %#o、%#8.0f、%+#10.3E |
0 | 对于所有的数字格式,用前导零而不是用空格填充字段宽度。 如果出现-标志或指定了精度(对于整数)则忽略该标准。 %010d、%08.3f |
实例
转义字符
转义字符 | 释义 |
---|---|
? | 在书写连续多个问号时使用,防止他们被解析成三字母词 |
\’ | 用于表示字符常量 ‘ |
\’’ | 用于表示一个字符串内部的双引号 |
\\ | 用于表示一个反斜杠,防止它被解释为一个转义序列符 |
\a | 警告字符,蜂鸣 |
\b | 退格符 |
\f | 进纸符 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ddd | ddd表示1~3个八进制的数字,如:\130;用来表示ASCII表中任意字符 |
\xdd | dd表示2个十六进制数字, 如: \x30;用来表示ASCII表中任意字符 |
其中,使用八进制\ddd的方式是比较通用的,而十六进制\xdd的方法在Windows系统可能不太通用,需要的时候建议用八进制\ddd。
例如:
相关题目
如图中要求求出绝对地址的长度,得到的结果为14而不是18;因为这地址中存在3个转义字符,其中\t是比较容易发现的,剩下一个\32比较容易忽略,因为8已经超出了八进制的规定,所以不能列入转义字符\32或\032中。
这也是为什么在编程中输入地址时需要双斜杠的原因。
类型修饰符
修饰符 | 意义 |
---|---|
标志 | 五种标志(-、+、空格、#和0),可以使用0个或多个标志 |
数字 | 字段宽度的最小值。如果字段不能容纳要打印的数或字符串,系统就会使用更宽的字段。例:%4d |
.数字 | 精度。对于%e、%E和%f转换,是要将在小数点的右边打印的数字的位数 对于%g、%G转换,是有效数字的最大位数。 对于%s转换,是将打印的字符的最大数目。 对于整数转换,是将要打印的数字的最小位数。 |
h | 和整数转换说明符一起使用,表示一个short int或unsigned short int类型数值–%hu、%hx |
hh | 和整数转换说明符一起使用,表示一个signed char或unsigned char类型数值 |
j | 和整数转换说明符一起使用,表示一个intmax_t或uintmax_t类型数值 |
l | 和整数转换说明符一起使用,表示一个long int或unsigned long int类型数值 |
ll | 和整数转换说明符一起使用,表示一个long long int或unsigned long long int类型数值 |
L | 和浮点转换说明符一起使用,表示一个long double值 |
t | 和整数说明符一起使用,表示一个ptrdiff_t(与两个指针之间的差相对应的类型) |
z | 和整数转换说明符一起使用,表示一个size_t值(sizeof返回的类型) |
浮点型参数的转换
有用于打印浮点类型double和long double的转换说明符,但没有用于float的说明符。原因是在K&R C中float值在被用于表达式中或被用于参数之前,会被自动转换为double类型。因此无需专门的转换说明符来显示float类型。
实例
格式化标准输入函数 ——scanf()
概念:键盘是系统的标准输入设备,从键盘中输入数据被称为标准输入
缓冲区:和printf类似,scanf具有输入缓冲区的机制;缓冲区中存储的是字符而不是像内存一样存储二进制。
标准IO:stdio-标准输入 stdout-标准输出 stderr-标准出错输出
相关函数:
scanf():
函数原型
#include <stdio.h> int scanf(const char *format,...);
函数说明
scanf系列函数的工作方式与printf系列函数相似,作用是从一个文件流里读取数据,并把数据放到以指针参数形式传递过来的地址处的变量中。它们也使用一个格式字符串来控制输入数据的转换,它所使用的许多转换控制符都与printf系列函数一样。
scanf函数,scan format的简写,作用是将读入的值保存到对应的变量中,这些变量必须是正确的,并且必须精确匹配格式字符串。否则,内存数据就可能遭到破坏,从而使程序崩溃。scanf("%d", &a); // 从键盘输入一个整型,放入指定的内存地址 &a 中
scanf("%f", &f); // 从键盘输入一个浮点数,放入指定的内存地址 &f 中
scanf("%d%f", &a, &f); // 从键盘依次输入一个整型和一个浮点型数据,用空白符隔开
char c;
char s[10];
scanf("%c", &c); // 从键盘输入一个字符,放入指定的内存地址 &c中
scanf("%s", s ); // 从键盘输入一个单词,放入指定的数组 s 中(注意不是&s,数组名称就是数组的地址)
gets()
fgets()
fgetc()
getchar()
scanf()开始读取输入以后,会在遇到的第一个空白字符空格(blank)、制表符(tab)或换行符(newline)处停止读取。
转换控制符
转换控制符 | 说明 |
---|---|
%d | 读取一个十进制整数 |
%o,%x | 读取一个八进制或十六进制整数 |
%c | 读取一个字符,它不会跳过起始的空白字符 |
%s | 读取一个字符串,它会跳过起始的空白字符 |
%f、%e、%g | 读取一个浮点数 |
%[] | 读取一个字符集合 |
%% | 读取一个%字符 |
%hd | 读入一个短整数 |
%ld | 读入一个长整数 |
%lg | 读入一个双精度浮点数 |
注意
- 使用%s控制符来扫描字符串时要小心,它会跳过起始的空白字符,在字符串里出现的第一个空白字符处停下来,所以最好用它来读取单词而不是一般意义上的字符串。此外,如果没有使用字段宽度限定符,它能够读取的字符串的长度是没有限制的,所以接收字符串必须有足够的空间来容纳输入流中可能的最长字符串。较好的选择是使用一个字段限定符,或者结合使用fgets和sscanf从输入读入一行数据,再对它进行扫描。这样可以避免可能被恶意用户利用的缓冲区溢出。
scanf()的转换修饰符
修饰符 | 意义 |
---|---|
* | 滞后赋值 %*d |
digit(s) | 最大字段宽度;在达到最大字段宽度或者遇到第一个空白字符时停止对输入项的读取 %10s |
hh | 把整数读成signed char或unsigned char |
ll | 把整数读作long long或unsigned long long |
h,l或L | “%hd”和“%hi”指示该值将会存储在一个short int中。 |
scanf()函数所用的转换说明符与prinf()所用的几乎一样。主要区别在于printf()把%f、%e、%E、%g、%G同时用于float和double,而scanf()只把它们用于float,而用于double时要加l修饰。
特殊之处
除**%c以外的说明符会自动跳过输入项之前的空格**,然后会一直读取字符,直到遇到空白字符或遇到一个不符合正在读取的类型的字符。所以scanf(“%d%d”,&n,&m)与scanf(“%d %d”,&n,&m)的行为结果是相同的。
但对于%c来说,向格式字符串中添加一个空格将导致一些区别。
如果在%c之前有一个空格,那么scanf()会跳到第一个非空白字符处。
也就是说scanf("%c",&ch)读取在输入中遇到的第一个空格(可能是空白字符),而scanf(" %c",&ch)则读取遇到的第一个非空白字符。
实例
如图所示,格式字符串中%c前面没加空格时,输入空格+1;输出为空格;
格式字符串中%c前面加空格时,输入空格+1;输出为1.
返回值
scanf()函数返回成功读入的项目个数。如果没有读取任何项目(当它期待一个数字而你却键入一个非数字字符串),返回0.当它检测到“文件结尾”,返回EOF(-1).
可变宽度输出字段
printf()和scanf()的修饰符(可变宽度输出字段)
在printf中,假定你不想事先指定字段宽度,而是希望由程序来指定该值,那么你可以在字段宽度部分使用*代替数字达到目的,但你必须使用一个参数来告诉函数字段宽度应该是多少。
上图所示,输入6,输出256占了6个字符宽度(右对齐)。
再输入3,输出3.142占了6个字符宽度(右对齐),其中显示3位小数部分。
在scanf()中*提供截然不同的服务。当把它放在%和说明符字母之间时,它使函数跳过相应的输入项目。如果程序需要读取一个文件中某个特定的列(该文件的数据以统一的列排列),那么该功能很有用。
如图所示,输入12、23、34,输出时跳过了12、23,显示34.
这笔记中大部分题目都来自比特鹏哥的视频,B站有一套免费的C语言课程,讲得非常详细,就是时间有点长。
我目前还在学习C语言阶段,所以理解上可能还不太准确,这些笔记部分是按我自己的想法写的,可能有点变扭,望理解。
参考文章: https://blog.csdn.net/luoting2017/article/details/88941166
>>C语言的杂碎知识点
>>Markdown学习笔记
>>数据在内存中的存储
>>printf源码