本篇文章给大家谈谈printf函数和scanf函数,C语言printf函数和scanf函数详解,以及请举例说明printf()函数和scanf()函数的区别?的知识点,同时本文还将给你拓展C语言pri
本篇文章给大家谈谈printf函数和scanf函数,C语言printf函数和scanf函数详解,以及请举例说明printf()函数和scanf()函数的区别?的知识点,同时本文还将给你拓展C 语言printf函数遇到的奇怪问题 ?、C/C++scanf函数的使用(负载printf函数格式说明)、C中printf和scanf函数的定义/主体、C语言fprintf()函数和fscanf()函数的具体使用等相关知识,希望对各位有所帮助,不要忘了收藏本站喔。
本文目录一览:- printf函数和scanf函数,C语言printf函数和scanf函数详解(请举例说明printf()函数和scanf()函数的区别?)
- C 语言printf函数遇到的奇怪问题 ?
- C/C++scanf函数的使用(负载printf函数格式说明)
- C中printf和scanf函数的定义/主体
- C语言fprintf()函数和fscanf()函数的具体使用
printf函数和scanf函数,C语言printf函数和scanf函数详解(请举例说明printf()函数和scanf()函数的区别?)
对于 printf 函数,相信大家并不陌生。之所以称它为格式化输出函数,关键就是该函数可以按用户指定的格式,把指定的数据显示到显示器屏幕上。该函数原型的一般格式如下:int printf(const char * format,... );
很显然,与其他库函数不同的是,printf 函数是一个“可变参数函数”(即函数参数的个数是可变的)。确切地说,是其输出参数的个数是可变的,且每一个输出参数的输出格式都有对应的格式说明符与之对应,从格式串的左端第 1 个格式说明符对应第 1 个输出参数,第 2 个格式说明符对应第 2 个输出参数,第 3 个格式说明符对应第 3 个输出参数,以此类推。其中,格式说明符的一般形式如下(方括号 [] 中的项为可选项):
%[flags][width][.prec][length] type_char
/*用中文标识如下:*/
%[标志符][宽度][精度][长度]类型符
1) 类型符(type_char)
它用以表示输出数据的类型,如表 1 所示。符 号 | 类 型 | 说 明 | 示 例 | 结 果 |
---|---|---|---|---|
% | 无 | 输出字符“%”本身 | pnntf("%%"); | % |
d、i | int | 以整型输出 | printf("%i,%d", 100,100); | 100,100 |
u | unsigned int | 以无符号整型输出 | printf( "%u,%u",100u,100 | |
o | unsigned int | 以八进制无符号整S输出 | printf( "%o”,100); | 144 |
x | unsigned int | 以十六进制小写输出 | printf("%x",11); | b |
X | unsigned int | 以十六制大写输出 | printf("%X",11); | B |
除表 1 所示的类型符之外,还有一个比较特殊与另类的类型符“%n”,当在格式化字符串中碰到“%n”时,在“%n”之前输出的字符个数会保存到下一个参数里。例如,下面的示例代码演示了如何获取在两个格式化的数字之间空间的偏量:
int main(void) { int pos=0; int x = 123; int y = 456; printf("%d%n%d\n",x,&pos,y); printf("pos=%d\n",pos); return 0; }很显然,上面代码中的 pos 将输出 3,即“123”的长度,运行结果为:
123456
pos=3
这里需要特别注意,“%n”返回的是应该被输出的字符数目,而不是实际输出的字符数目。当把一个字符串格式化输出到一个定长缓冲区内时,输出字符串可能被截短。不考虑截短的影响,“%n”格式表示如果不被截短的偏量值(输出字符数目)。看下面的示例代码:
int main(void) { char buf[20]; int pos=0; int x = 0; snprintf(buf,sizeof(buf),"%.100d%n",&pos); printf("pos=%d\n",pos); return 0; }很显然,上面的代码会输出 100,而不是 20。
由此可见,相对于“%d”“%x”“%s”等,“%n”的显著不同之处就是“%n”会改变变量的值,这也就是格式化字符串攻击的爆破点,如下面的示例代码所示:
char daddr[16]; int main(void) { char buf[100]; int x=1; memset(daddr,'/0',16); printf("前X: %d/%#x (%p)\n",&x); strncpy(daddr,"AAAAAAA%n",9); snprintf(buf,daddr); buf[sizeof(buf) - 1] = 0; printf("后X: %d/%#x (%p)\n",&x); return 0; }在上面的代码中,x 将被从 1 修改成 7,其运行结果为:
前X: 1/0x1 (0061FEA8)
后X: 7/0x7 (0061FEA8)
之所以会出现这样的结果,是因为程序在调用 snprintf 函数之前,首先调用了 printf 函数,而这时 printf 函数的 &x 参数在 main 函数的堆栈内存中留下了 &x 的内存残像。当调用 snprintf 时,系统本来只给 snprintf 准备了 3 个参数,但是由于格式化字符串攻击原因,使得 snprinf 认为应该有 4 个参数传给它,这样 snprintf 就私自把 &x 的内存残像作为第 4 个参数读走,而 snprintf 所谓的第 4 个参数对应的就是“%n”,于是 snprintf 就成功修改了变量 x 的值。这也就是最常见的使用 Linux 函数调用时的内存残像来实现格式化字符串攻击的方法之一,所以在使用的时候一定要注意。
2) 标志符(flags)
它用于规定输出格式,如表 2 所示。符号 | 说 明 |
---|---|
(空白) | 右对齐,左边填充 0 和空格 |
(空格) | 输出值为正时加上空格,为负时加上负号 |
- | 输出结果为左对齐(默认为右对齐),边填空格(如果存在表格最后一行介绍的0,那么将忽略0) |
+ | 在数字前增加符号“+”(正号)或“-”(负号) |
# | 类塑符是o、x、X吋,增加前缀0、0x、0X;类型符是e、E、f、F、g、G时,一定要使用小数点;类型符是g、G时,尾部的 0 保留 |
0 | 参数的前面用0填充,直到占满指定列宽为止(如果同时存在“-”,将被“-”覆盖,导致 0 被忽略 |
3) 宽度(width)
它用于控制显示数值的宽度,如表 3 所示。符号 | 说 明 |
---|---|
n | 至少输出 n 个字符(n 是一个正整数)。如果输出少于 n 个字符,则用空格填满余下的位置(如果标识符为“-”,则在右侧填,否则在左端填) |
0n | 至少输出 n 个字符(n 是一个正整数)。如果输出值少于 n 个字符,则在左侧填满 0 |
* | 输出字符个数由下一个输出参数指定(其必须为一个整形量) |
4) 精度(.prec)
它用于控制显示数值的精度。如果输出的是数字,则表示小数的位数;如果输出的是字符,则表示输出字符的个数;若实际位数大于所定义的精度数,则截去超过的部分。如表 4 所示。符号 | 说 明 |
---|---|
无 | 系统默认精度 |
.0 | 对于 d、i、o、u、x、X等整形类型符,采用系统默认精度;对于f、F、e、E等浮点类型符,不输出小数部分 |
.n |
1) 对于d、i、o、u、x、X类型符,至少输出 n 位数字,且:
3) 对于 g 和 G 类型符,最多输出 n 位有效数字 4) 对于 s 类型符,如果对应的输出串的长度不超过 n 个字符,则将其原样输出,否则输出其头 n 个宁符 |
* | 输出精度由下一个输出参数指定(其必须为一个整型量) |
5) 长度(length)
它用于控制显示数值的长度,如表 5 所示。符号 | 说 明 |
---|---|
hh | 与d、i 一起使用,表示一个signed char 类型的值;与o、u、x、X—起使用,表示一个unsigned char 类型的值;与 n 一起使用,表示相应的变元是指向 signed char 型变量的指针(c99 ) |
h | 与d、i、o、u、x、X 或 n 一起使用,表示一个short int 或 unsigned short int 类型的值 |
l | 与d、i、o、u、x、X 或 n 一起使用,表示一个 long int 或者 unsigned long int 类型的值 |
ll | 与 d、i、o、u、x、X 或 n —起使用,表示相应的变元是 long long int 或 unsigned long long int 类型的值(c99 ) |
j | 与 d、i、o、u、x、X 或 n —起使用,表示匹配的变元是 intmax_t 或 uintmax_t 类型,这些类型在“stdint. h”中声明(c99 ) |
z | 与 d、i、o、u、x、X 或 n —起使用,表示匹配的变元是指向 size_t 类型对象的指针,该类型在“stddef. h”中声明(c99 ) |
t | 与d、i、o、u、x、X 或 n —起使用,表示匹配的变元是指向 ptrdiff_t 类型对象的指针,该类型在“stddef. h”中声明(c99 ) |
L | 和a、A、e、E、f、F、g、G—起使用,表示一个long double类型的值 |
最后,在使用 printf 函数时还必须注意,尽量不要在 printf 语句中改变输出变量的值,因为可能会造成输出结果的不确定性。如下面的示例代码所示:
int k=8; printf("%d,%d\n",k,++k);对于上面的代码,表面上看起来输出的结果应该是“8,9”。但实际情况并非如此,在调用printf函数时,其参数是从右至左进行处理的,即将先进行 ++k 运算,所以最后的结果是“9,9”。由此可见,千万不要在 printf 语句中试图改变输出变量的值,如果确实需要改变,可以按照下面的示例代码形式来处理:
printf("%d\n",k); printf("%d\n",++k);这样处理之后,其结果就是我们所需要的“8,9”了。
除此之外,每一个输出参数的输出格式都必须有对应的格式说明符与之一一对应,并且类型必须匹配。若二者不能够一一对应匹配,则不能够正确输出,而且编译时可能不会报错。同时,若格式说明符个数少于输出项个数,则多余的输出项将不予输出;若格式说明符个数多于输出项个数,则可能会输出一些毫无意义的数字乱码。
scanf 函数
相对于 printf 函数,scanf 函数就简单得多。scanf 函数的功能与 printf 函数正好相反,执行格式化输入功能。即 scanf 函数从格式串的最左端开始,每遇到一个字符便将其与下一个输入字符进行“匹配”,如果二者匹配(相同)则继续,否则结束对后面输入的处理。而每遇到一个格式说明符,便按该格式说明符所描述的格式对其后的输入值进行转换,然后将其存于与其对应的输入地址中。以此类推,直到格式串结束为止。该函数原型的一般格式如下:int scanf (const char *format,...);从函数原型可以看出,同 printf 函数相似,scanf 函数也是一个“可变参数函数”。同时,scanf 函数的第一个参数 format 也必须是一个格式化串。除此格式化串之外,scanf 函数还可以有若干个输入地址,且对于每一个输入地址,在格式串中都必须有一个格式说明符与之一一对应。即从格式串的左端第 1 个格式说明符对应第 1 个输入地址,第 2 个格式说明符对应第 2 个输入地址,第 3 个格式说明符对应第 3 个输入地址,以此类推。
也就是说,除第 1 个格式化串参数之外,其他参数的个数是可变的,且每一个输入地址必须指向一个合法的存储空间,以便能正确地接受相应的输入值。每个输入值的转换格式都由格式说明符决定。格式说明符的一般形式如下(方括号 [] 中的项为可选项):
%[*][width][length] type_char
/*用中文标识如下:*/
%[*][宽度][长度]类型符
下面先来看一段示例代码:
int main(void) { char c[5]; int i=0; printf("输入数据(hello):\n"); for(i = 0;i < 5; ++i) { scanf("%c",&c[i]); } printf("输出数据:\n"); printf("%s\n",c); return 0; }对于上面这段示例代码,我们希望在“c[5]”字符数组中能够存储“hello”字符串,并在最后输出到屏幕上。从表面上看,这段程序没有任何问题,但实际情况并非如此。当我们依次输入“h(回车)”“e(回车)”,然后再输入“l”时,问题发生了。此时,程序不仅中断输入操作,而且会打印出字符数组 c 中的内容,其运行结果为:
输入数据(hello):
h
e
l
输出数据:
h
e
l
很显然,字符数组“c[5]”是完全能够存储“hello”字符串的,但为什么输入到“l”就结束了呢?
其实原因很简单,在我们输入“h”和第一个回车后,“h”和这个回车符“\n”都保留在缓冲区中。第 1 个 scanf 读取了“h”,但是输入缓冲区里面还留有一个“\n”,于是第 2 个 scanf 读取这个“\n”,然后输入“e”和第 2 个回车符“\n”。同理,第 3 个 scanf 读取了“e”,第 4 个 scanf 读取了第 2 个回车符“\n”,第 5 个 scanf读取了“l”。因此,程序并没有提前结束,而是完整地循环了5次scanf语句,只不过有两次scanf都读取到回车符“\n”而已。
由此可见,在使用 scanf 函数时,如果不及时刷新输入缓冲区,有时会出现莫名其妙的错误。对于这类问题,其实解决办法有许多,比如可以使用“fflush(stdin);”语句来刷新输入缓冲区。但不得不说明的是,fflush 函数在可移植性上并不是很好。当然,也可以通过自己编写代码来解决,如下面的示例代码所示:
#include <stdio.h> void flush() { char c; while ((c=getchar()) != '\n'&&c!=EOF); } int main(void) { char c[5]; int i=0; printf("输入数据(hello):\n"); for(i = 0; i < 5; ++i) { scanf("%c",&c[i]); flush(); } printf("输出数据:\n"); printf("%s\n",c); return 0; }这样,就从根本上解决了输入缓冲区问题,其运行结果为:
输入数据(hello):
h
e
l
l
o
输出数据:
hello
除此之外,还应该注意 scanf 中的空白符(这里所指的空白符包括空格、制表符、换行符、回车符和换页符)带来的问题,如下面的代码所示:
int main(void) { int a=0; printf("输入数据:\n"); /*请注意,这里多了一个回车符\n*/ scanf("%d\n",&a); printf("输出数据:\n",a); printf("%d\n",a); return 0; }在上面的代码中,因为在“scanf("%d\n",&a);”语句中多加了一个回车符“\n”,导致的结果就是要输入两个数,程序才会正常结束,而不是我们所期望的一个数。运行结果为:
输入数据:
22
11
输出数据:
22
原因就是在用空白符结尾时,scanf 会跳过空白符去读下一个字符,所以必须再输入一个数。因此在编写程序时一定要多注意这类手误导致的错误。
C 语言printf函数遇到的奇怪问题 ?
今在STM32串口打印调试信息时遇到一奇怪问题:
描述如下:
单片机要用printf打印消息,需重定向,在STM32中实现如:
int fputc(int ch, FILE *f) { while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {} USART_SendData(USART1, (u8)ch); return ch; }
我利用定时一秒打印一心跳消息时,如下:
while (1) { Timer_Loop_Process(); if(TIMER_SET == bSystem1Sec) { printf(" Tick \n"); } }
但当 printf(" Tick \n");打印字符不同时(比如又多加了个空格)有时会两秒才显示打印一次。
经过测试发现,当打印的字符不超过7个,如printf("1234567");可以很正常的一秒打印一次,当超过了7个,如printf("12345678");则就变成2秒打印一次。
不知这个问题原因是什么,我还没有对printf内部实现机制有过深入研究。
希望有遇到过类似问题的给点提示或答案 ,谢谢!
C/C++scanf函数的使用(负载printf函数格式说明)
1、函数格式:
scanf(格式控制字符串,变量地址列表)
函数功能:
通过标准输入设备(键盘、写字板等),按照格式控制字符串中的格式要求为变量地址列表中的变量输入数据。
假设变量a和b的值任意输入,如何将a和b的值进行交换后输出?
语句编写:
#include<stdio.h>
int main()
{
int a,b,t;
scanf("%d%d",&a,&b);
t=a;
a=b;
b=t;
printf("a=%d,b=%d\n",a,b);
return 0;
}
运行结果:
注意说明
(1)格式控制字资串中多个格式转换说明符之间没有逗号,输入数据时,通常使用空格键或者回车键来分隔数据;格式转换说明符之间有逗号,输入数据时,一定要用逗号来分隔数据。
(2)格式转换说明符个数和类型必须与变量地址列表 --对应。
(3)*(抑制字符):表示本输入项只是读入,但不赋给相应变量。
格式控制字符串:
格式转换说明符 | 功能描述 |
---|---|
%d | 输入一个十进制整数 |
%f | 输入一个单精度实数 |
%lf | 输入一个双精度实数 |
%c | 输入一个字符 |
%s | 输入一个字符串 |
%o | 输入一个八进制整数 |
%x | 输入一个十六进制整数 |
%* | 表示本输入项只是读入,但不赋给相应变量 |
变量地址列表串:
变量地址列表由项目组成,两个输入项目之间用逗号分割,输入项目一般由地址符&和变量名组成,即:&变量名。
1、printf函数
函数格式
printf(格式控制字符串,输出列表)
函数功能:
讲输出列表中的各个表达式的值按照格式控制字符串中对应的格式输出到标准输出设备(显示屏)
格式控制字符串:
格式转换说明符 | 功能描述 |
---|---|
%d | 输出一个十进制整数 |
%f | 输出一个单精度实数 |
%lf | 输出一个单精度实数 |
%e或%E | 按指数格式输出一个实数 |
%c | 输出一个字符 |
%s | 输出一个字符串 |
%o | 输出一个八进制整数 |
%x | 输出一个十六进制整数 |
输出列表:
输出列表有输出列表组成,两个输出项之间用逗号分隔,输出项可以是一般的表达式,也可以是简单变量,即:变量名或者表达式。
主意说明:
(1)格式转换说明符个数和类型必须与输出列表--对应。
(2)格式控制字符串中可以有转义字符和普通字符。转义字符根据具体作用实现操作,普通字符原样输出。
(3) 修饰符m(正整数):指定输出项所占的宽度,当指定宽度小于实际宽度时按实际宽度输出,当指定宽度大于实际宽度时在前面用空格补足。
(4)修饰符.n(正整数):指定输出的实型数据的小数位数(四舍五入),系统默认小数位数为6。
(5)修饰符0(数字):指定数字前的空格用0填补。
(6)修饰符-;指定输出项的对齐方式,表示左对齐。
C中printf和scanf函数的定义/主体
解决方法
您可以在标准库中找到它,它随您使用的任何编译器一起提供..
C语言fprintf()函数和fscanf()函数的具体使用
fprintf()函数
在C语言中常用使用的打印函数是printf()函数,这个多用于给控制台打印数据,如果需要将内容直接打印到某个文件中的时候,可以使用fprintf()函数。它有两个参数,第一个参数为待写入的文件指针,第二个参数为待写入的内容。下面通过一个简单的例子来演示它的用法。
int main() { FILE *in,*out; int ret; char ch; /* 打开文件 */ in = fopen("123.txt","a+"); if(in == NULL) puts("打开失败!"); else puts("打开成功!"); fprintf(in," hello world"); /* 关闭文件 */ ret = fclose(in); if(ret == 0) puts("关闭成功!"); else puts("关闭失败!"); system("pause"); return 0; }
首先以追加的方式打开一个文本文件123.txt,然后在使用fprintf()函数向文件里面追加字符串。最后关闭打开的文件。
程序执行完成之后,打开123.txt文件,可以看到程序已经在原来的字符串后面追加了一个字符串。fprintf()函数中第一个参数的文件,不仅仅指的是文档,也可以是显示器。在C语言中键盘和显示器也可以是文件。
比如在上面的例子中,可以将fprintf()的第一个参数修改为stdout,这样字符串就会打印在终端上。
fscanf()函数
scanf()函数用于从键盘读取数据,fscanf()函数则用来从文件中读取数据。比如可以将上面的例子修改为,使用fscanf()函数从文件中读取数据。
int main() { FILE *in,*out; int ret; char ch; char str1[30],str2[30],str3[30]; /* 打开文件 */ in = fopen("123.txt","r"); if(in == NULL) puts("打开失败!"); else puts("打开成功!"); fscanf(in,"%s %s %s",str1,str2,str3); puts(str1); puts(str2); puts(str3); /* 关闭文件 */ ret = fclose(in); if(ret == 0) puts("关闭成功!"); else puts("关闭失败!"); system("pause"); return 0; }
打开123.txt文件,然后使用fscanf()函数读取文件中的字符串,然后再将读取的字符串打印出来。这里fscanf()中有三个参数,第一个参数是要读取的文件指针,第二个参数是要读取数据的格式,第三个参数是读取的数据存放位置。由于123.txt文件中总共有3组字符串,所以第二个参数里面写了三次,同样第三个参数存储字符串的数组也写了三个。因为fscanf()函数在读取字符串的时候是以空格结束的,如果只使用一个%s的话,只能读出来第一个字符串。
fscanf()函数也可以从键盘读取数据,将fscanf()函数的第一个参数改为标准输入stdin,就可以从键盘读取数据了。
运行程序后,通过键盘输入三个字符串,以空格隔开,按下回车键之后,就会打印出通过fscanf()函数从标准输入设备,也就是从键盘上读取的三个字符串了
到此这篇关于C语言fprintf()函数和fscanf()函数的具体使用的文章就介绍到这了,更多相关C语言fprintf()函数和fscanf()函数 内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
- C语言中fgets和fscanf区别详解
- C语言中的fscanf()函数与vfscanf()函数使用
- C语言实现文件读写
- 详解C语言fscanf函数读取文件教程及源码
关于printf函数和scanf函数,C语言printf函数和scanf函数详解和请举例说明printf()函数和scanf()函数的区别?的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于C 语言printf函数遇到的奇怪问题 ?、C/C++scanf函数的使用(负载printf函数格式说明)、C中printf和scanf函数的定义/主体、C语言fprintf()函数和fscanf()函数的具体使用等相关知识的信息别忘了在本站进行查找喔。
本文标签: