stdio printf 和 scanf


  C 语言 stdio 标准库中定义了一些重要的 io 函数,而使用最广泛的就是 printfscanf

printf

  printf 主要功能是将文字输出到标准输出设备(如控制台)以及文字格式化。

声明

int printf(const char *format, ...)

通过声明我们可以看出,printf 接受的是 C 风格字符串(以空字符 \0 为结束的 char 数组),及能转换到 C 风格字符串的其他变量,现代编译器中,会自动将合适的用引号引起来的字符串(字符串字面值)转换为 C 风格字符串,也可以使用两个双引号表示空字符串。

format 标签可以对附加的参数列表中的变量进行替换,并按需格式化。

format 标签的格式是 %[flags][width][.precision][length]specifier

标志

flags 描述
- 在给定的字段宽度内左对齐,默认是右对齐(参见 width 子说明符)。
+ 强制在结果之前显示 + 或 -,即正数前面会显示 + 号。默认情况下,只有负数前面会显示一个 - 号。
空格 如果没有写入任何符号,则在该值前面插入一个空格。
# 自动添加进制表示,与 o、x 或 X 说明符一起使用时,非零值前面会分别显示 0、0x 或 0X。与 e、E 和 f 一起使用时,会强制输出包含一个小数点,即使后边没有数字时也会显示小数点。默认情况下,如果后边没有数字时候,不会显示显示小数点。与 g 或 G 一起使用时,结果与使用 e 或 E 时相同,但是尾部的零不会被移除。
0 在指定填充 padding(空白)的数字左边放置零(0),而不是空格(参见 width 子说明符)。

宽度

width 描述
(number) 要输出的字符的最小数目。如果输出的值短于该数,结果会用空格填充(或者用0)。如果输出的值长于该数,结果不会被截断。
* 宽度在 format 字符串中未指定,但是会作为附加整数值参数放置于要被格式化的参数之前。

精度

.precision 描述
.number 对于整型说明符(d、i、o、u、x、X),precision 指定了要写入的数字的最小位数。如果写入的值短于该数,结果会用零在头部来填充。如果写入的值长于该数,结果会被截断。精度为 0 意味着不输出任何字符。对于 e、E 和 f 是小数点后输出的小数位数。对于 g 和 G 是最大有效位数。对于 s 是要输出的最大字符数。默认情况下,所有字符都会被输出,直到遇到末尾的空字符。对于 c 没有任何影响。
.* 精度在 format 字符串中未指定,但是会作为附加整数值参数放置于要被格式化的参数之前。

长度

length 描述
h 参数被解释为短整型或无符号短整型(仅适用于整数说明符:i、d、o、u、x 和 X)。
l 参数被解释为长整型或无符号长整型,适用于整数说明符(i、d、o、u、x 和 X)及说明符 c(表示一个宽字符)和 s(表示宽字符字符串)。
L 参数被解释为长双精度型(仅适用于浮点数说明符:e、E、f、g 和 G)。

类型

PC 用户拖拽示例部分文字的光标可以看到被遮住的”结果”。

说明符 输入 输出 描述 示例 结果
d/i int 10进制有符号整数 i是老式写法 printf("%i",123); 123
o unsigned int 无符号8进制整数 无前缀0 printf("0%o",123); 0173
u unsigned int 无符号10进制整数 printf("%u",123); 123
x/X unsigned int 无符号16进制整数 x输出小写,X输出大写,无前缀0x) printf("0x%x 0x%X",123,123); 0x7b 0x7B
f/lf float(double) 指定精度浮点数 单精度浮点数用f,双精度浮点数用lf(printf可混用,但scanf不能混用) printf("%.6f %.7f",0.00001234,0.00001234); 0.000012 0.0000123
F float(double) 与f相同 infinity 和 nan 输出为大写形式。 printf("%f %F %f %F\n",INFINITY,INFINITY,NAN,NAN); inf INF nan NAN
e/E float(double) 科学计数法eg:1e2 使用指数(Exponent)表示浮点数,输出的E的大小写取决于格式化使用的 printf("%e %E",0.000000123,0.000000123); 1.230000e-07 1.230000E-07
g/G float(double) 最短字符输出 根据数值的长度,选择以最短的方式输出,%f或%e/%F或%E printf("%g %g",0.000000123,0.123); 1.23e-07 0.123
c char 字符型 把输入的数字按照ASCII码相应转换为对应的字符 printf("%c\n",64) A
s char* 字符串 输出字符串中的字符直至字符串中的空字符 printf("%s","测试test"); 测试test
S wchar_t* 宽字符串 输出字符串中的字符直至字符串中的空字符(宽字符串以两个空字符’\0‘结尾) setlocale(LC_ALL,"zh_CN.UTF-8"); wchar_t wtest[]=L"测试Test"; printf("%S\n",wtest); 测试test
p void* 16进制指针 小写 printf("%010p","lvlv"); 0x004007e6
n int* %n对应的参数是一个指向signed int的指针,在此之前输出的字符数将存储到指针所指的位置 int num=0; printf("lvlv%n",&num); printf("num:%d",num); lvlvnum:4
% 输出 % 本身 printf("%%"); %
m 不确定 打印errno对应的出错内容 printf("%m\n");
a/A float(double) 十六进制p计数法浮点数 a为小写,A为大写 printf("%a %A",15.15,15.15); 0x1.e4ccccccccccdp+3 0X1.E4CCCCCCCCCCDP+3
* 读取一个空数据  

scanf

  scanf 用法与 printf 类似,不过 scanf 使用的是变量的地址,所以对于常规变量需要先使用取地址运算符,对于指针则可以直接使用。

声明

int scanf(const char * restrict format,...);

format 标签的格式是 %[width][length]specifier,同 printf。

其中有一个坑是,很多文章都宣称 scanf 在读取数据前会先刷新缓冲区,但实际上并不是这样。

只有这些操作会刷新缓冲区:

  1. main 结束时隐式调用 exit(0)
  2. 遇到 \n \r
  3. fflush 手动刷新
  4. 缓冲区满时自动刷新

话不多说上代码:


#include <cstdio>
 
int main(void){
	using namespace std;
	char a,b,c;
	printf("enter three chars ");
	scanf("%c,%c,%c",&a,&b,&c);
	printf("%c,%c,%c",a,b,c);
	printf("\nenter three chars ");
	fflush(stdin);//强制清空缓冲区
    /*
    int d;
    d = getchar();
    */
	scanf("%c%c%c",&a,&b,&c);
	printf("%c,%c,%c",a,b,c);
	return 0;
}

结果:


enter three chars 1,2,3
1,2,3
enter three chars 123
1,2,3

如果不用 fflush,结果如下:


enter three chars 1,2,3//此时按下enter
1,2,3
enter three chars 123//此时按下enter

,1,2

注意区别:不用 fflush 刷新后,换行会替代原来 1 的位置,最后结果中 3 被挤掉。

现在我们把 fflush 注释掉,使用 getchar 函数获得这个字符,并且存入 d 变量中,我们会发现 d 的值是 10(\n,换行)。

所以我们得到结论,虽然按 enter 键后会发送 \n 帮助我们完成输入并清空缓冲区,但是,\n 本身却又被存进了缓冲区中。

虽然书上大多使用 getchar 来获得这种字符,得到正确结果,但是 fflush 从设计角度看更易于理解。