ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

C语言:printf 与 scanf 详解:占位符、输出格式、返回值与赋值忽略符

C语言:printf 与 scanf 详解:占位符、输出格式、返回值与赋值忽略符

【C语言学习笔记】printf 与 scanf 详解:占位符、输出格式、返回值与赋值忽略符

学习主题:《从 C 语言到 AI 编程实践》
本篇知识点:printf 基本用法与不自动换行、占位符与参数个数对应、宽度 / 对齐 / 正负号 / 小数位 / 部分字符串、scanf 的读取原理与返回值、%c 与 %s 的空白处理、%s 越界、赋值忽略符

一、printf 基本用法

printf= print + f,其中的f代表 format(格式化),表示可以定制输出文本的格式。它把参数文本输出到屏幕。

#include<stdio.h>intmain(){printf("Hello World");return0;}

输出Hello World。连写两个:

printf("Hello World");printf("hehe");// 输出:Hello Worldhehe

printf 不会在行尾自动换行,运行结束后光标停在输出结束的位置,不自动换行。想让光标到下一行开头,要显式加\n。

printf("Hello World\n");printf("hehe");// 输出两行

如果漏掉#include <stdio.h>:

warning C4013: "printf" 未定义;假设外部返回 int error LNK2019: 无法解析的外部符号 printf,函数 main 中引用了该符号 fatal error LNK1120: 1 个无法解析的外部命令

这说明printf是库函数,必须包含<stdio.h>头文件。

二、占位符与参数个数

占位符就是这个位置可以用其他值代入。

printf("There are 3 apples\n");printf("There are %d apples\n",5);// There are 5 applesprintf("%s will come tonight\n","lisi");// lisi will come tonight

参数与占位符是一一对应的:有 n 个占位符,printf的参数就应该有 n + 1 个(格式串本身算一个)。个数不匹配时,会输出内存中的任意值:

printf("%s says it is %d o'clock\n","wangwu");// 少给了一个参数,打印出内存中的随机值// wangwu says it is -4 o'clock

打印地址用%p:

inta=10;printf("a的地址是: %p\n",&a);// 类似 0000005B81AFBF4

2.1 常用占位符

占位符对应类型
%cchar
%dint / signed int,十进制
%uunsigned int,十进制
%hd/%hushort / unsigned short
%ld/%lulong / unsigned long
%lld/%llulong long / unsigned long long
%ffloat(double 也可用%f,也可用%lf)
%Lflong double
%p指针(打印地址)
%s字符串
%x十六进制整数
%zusize_t

三、输出格式

3.1 限定宽度与对齐

printf("%d\n",123);//123printf("%5d\n",123);// 123 宽度至少 5 位,不足补空格printf("%5d\n",123456);//123456 超过 5 位就按实际位数输出

默认右对齐(前面补空格),在%后加-变成左对齐:

printf("----%5d----\n",123);//---- 123----printf("----%-5d----\n",123);//----123 ----

浮点同理:

printf("%f\n",123.45);//123.450000 默认 6 位小数printf("%12f\n",123.45);// 123.450000printf("%-12f====\n",123.45);//123.450000 ====

3.2 总是显示正负号

默认只给负数显示-,加+后正数也带正号:

printf("%d\n",12);//12printf("%+d\n",12);//+12printf("%+d\n",-12);//-12

3.3 限定小数位数

printf("%f\n",123.456);//123.456000printf("%.2f\n",123.456);//123.46printf("%6.2f\n",0.5);// 0.50 宽度 6,保留 2 位

宽度和小数位数都可以用*代替,由参数传入:

intm=6,n=2;printf("%*.*f\n",m,n,0.5);// 0.50

3.4 输出部分字符串

%.[m]s指定输出长度:

printf("%s\n","abcdefghi");//abcdefghiprintf("%.5s\n","abcdefghi");//abcde

四、scanf 基本用法

scanf读取键盘输入,原型在stdio.h。程序运行到它时会停下,等用户输入并回车,然后把输入存入变量。

intmain(){intscore=0;printf("请输入成绩:");scanf("%d",&score);printf("成绩是:%d\n",score);return0;}// 请输入成绩:88// 成绩是:88

数据流:键盘 →(标准输入 stdin)→ 程序变量 →(标准输出 stdout)→ 屏幕。

一次读多个值:

inti=0,j=0;floatx=0.0,y=0.0;scanf("%d%d%f%f",&i,&j,&x,&y);// 输入 1 -20 3.4 -4.0e3

输入多个值时,数据之间用一个或多个空格、甚至换行分开都不影响——scanf处理数值占位符时会自动过滤空白字符(空格、制表符、换行符)。

五、scanf 的读取原理

用户的输入先放进缓存,按下回车后,才按占位符对缓存进行解读。解读时从上一次解读遗留的第一个字符开始,直到读完缓存,或者遇到第一个不符合条件的字符为止。

intx;floaty;// 用户输入 " -13.45e12# 0"scanf("%d",&x);// 跳过空格读 -13,遇到 . 停止(. 不是整数的合法字符)printf("%d\n",x);// -13scanf("%f",&y);// 从上次停下的 .45e12# 0 继续printf("%f\n",y);// 449999994880.000000

理解"从上一次遗留处继续"这一点,后面很多输入问题就解释得通了。

六、scanf 的返回值

scanf的返回值是成功读取的变量个数;一个都没读到返回 0;在读任何数据之前遇到错误或文件结尾,返回常量EOF(-1)。

inta=0,b=0;floatf=0.0f;intr=scanf("%d %d %f",&a,&b,&f);printf("a=%d b=%d f=%f\n",a,b,f);printf("r = %d\n",r);
输入结果说明
10 20 4.5a=10 b=20 f=4.500000,r=3三个都读到
10 20 abca=10 b=20 f=0.000000,r=2第三个不是数字
10 #a=10 b=0 f=0.000000,r=1第二个不匹配
#a=0 b=0 f=0.000000,r=0一个都没读到
Ctrl+Z全 0,r=-1遇到文件结尾,返回 EOF

竞赛里的多组数据输入就靠返回值:

inta=0,b=0;// 只写一次 scanf:输入多组时只会读第一组// scanf("%d %d", &a, &b);while(scanf("%d %d",&a,&b)==2)// 读满两个才继续{printf("%d\n",a+b);}

也可以写成while (scanf("%d %d", &a, &b) != EOF)。

七、scanf 的占位符

占位符说明
%c字符
%d整数
%ffloat
%lfdouble
%Lflong double
%s字符串
%[]指定匹配的字符集,遇到不在集合中的字符就停止

printf 与 scanf 的%f差异:

  • printf:float用%f,double用%f或%lf都行;
  • scanf:float用%f,double必须用%lf。

7.1 %c 不跳过空白

除%c外,所有占位符都会自动忽略起首的空白字符。%c总是取当前第一个字符,哪怕它是空格。

charn=0;scanf("%c",&n);// 输入 " abc",输出 == ==,读到的是空格本身scanf(" %c",&n);// 输入 " abc",输出 ==a==,格式串里的空格表示跳过空白

要跳过空白,就在%c前加一个空格。

7.2 %s 与 %[]

%s遇到空白字符(空格、制表符、换行)就停止:

chararr[20];scanf("%s",arr);// 输入 abc def,只得到 abcprintf("%s\n",arr);// 注意:数组名本身就是地址,这里不用 &

想读带空格的字符串,用字符集:

scanf("%[a-z]",arr);// 输入 abcdefg1234 → abcdefg,遇到不在 a-z 的字符停止scanf("%[^\n]",arr);// 输入 abc def → abc def,读到除换行外的所有字符

八、%s 的越界与赋值忽略符

8.1 %s 不检查长度

%s读入字符数组时不检测是否超过数组长度,很容易写越界:

chararr[5];scanf("%s",arr);// 输入 abcdefgh(9 字节)→ 栈被破坏// Run-Time Check Failure #2 - Stack around the variable 'arr' was corrupted.

正确做法是指定最大长度:%[m]s。数组 5 个字节,最多读 4 个,留 1 个给\0:

chararr[5];// [][][][][\0]scanf("%4s",arr);// 输入 abcdefgh → abcd

同样的坑还有类型不匹配:

inta=0;floatf=0.0;scanf("%d",&a,&f);// 少写了一个占位符// C4477 "scanf": 格式字符串 "%d" 需要类型 "int *" 的参数,// 但可变参数 1 拥有了类型 "float *",运行时会破坏栈

占位符必须与变量类型、个数严格对应。

8.2 赋值忽略符 %*c

输入格式不固定时,比如分隔符可能是-也可能是/:

intyear=0,month=0,day=0;scanf("%d-%d-%d",&year,&month,&day);// 输入 10-20-30 → 10 20 30(正确)// 输入 10/20/30 → 10 0 0(分隔符不匹配,后面全部失败)

两种修法。第一种用%c把分隔符吃进变量:

charc1=0,c2=0;scanf("%d%c%d%c%d",&year,&c1,&month,&c2,&day);

更简洁的是赋值忽略符*:加在任何占位符的百分号后面,该占位符照样解析,但结果被丢弃。

scanf("%d%*c%d%*c%d",&year,&month,&day);// 输入 10-20-30 或 10/20/30,都输出 10 20 30

%*c就是"读一个字符但不要它",无论分隔符是什么都能跳过。

九、本篇踩坑清单

#坑现象正确做法
1以为 printf 会自动换行两次输出粘在一起结尾加\n
2漏写#include <stdio.h>C4013、LNK2019用库函数就包含头文件
3占位符比参数多打印内存随机值(如 -4)占位符与参数一一对应
4以为 scanf 能读完一整行只读到第一个空格用%[^\n]或fgets
5%c前不加空格读到前导空格写" %c"
6double用%f输入结果错乱scanf 里必须用%lf
7%s不限长度Run-Time Check Failure #2栈被破坏写%4s,留一位给\0
8scanf 占位符与变量类型不符C4477,运行时破坏栈类型、个数严格对应
9用scanf("%d-%d-%d")固定分隔符输入用/就失败用%*c吃掉分隔符
10多组输入只写一次 scanf只处理第一组while (scanf(...) == 2)

十、小结

  1. printf不自动换行,占位符个数 = 参数个数 − 1,少给参数会打印内存随机值;
  2. 输出格式可以组合:宽度%5d、左对齐%-5d、正负号%+d、小数位%.2f、部分字符串%.5s,宽度和精度还能用*从参数传入;
  3. scanf按缓存解读、从上次遗留处继续,返回值是成功读取的个数,多组输入靠它判断;
  4. %c不跳空白、%s遇空白即停且不检查长度——前者要加空格,后者要写%4s这类上限,分隔符不确定时用%*c忽略。

标签:C语言printfscanf占位符输入输出学习笔记

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进