☰
C语言入门:printf和scanf的格式化输入输出核心详解
2026/10/20 5:07:31 网站建设 项目流程

很多同学在第一次上C语言课、打开编译器时,会先写一个“Hello World”。那一刻屏幕上跳出熟悉的英文,会觉得编程也没那么难。但真正让大量零基础学习者停下来的,往往不是#include,也不是main函数,而是两个看起来特别像“老朋友”的函数:printf和scanf。

我的判断是:printf和scanf能不能用对、用熟,决定了 C 语言入门的第一道分水岭能不能顺利跨过去。原因不是它们语法有多难,而是它们第一次让你真正面对“数据在程序里怎么表示、怎么进、怎么出”的问题。很多教程把它们当成一个查表工具,只给一张格式说明符表就完事,却没有讲清楚背后的格式化规则和输入缓冲机制。于是初学者到后面学指针、学字符串、学文件读写时,还会反复栽在“输入输出”这件事上。

如果你正处在 C 语言零基础入门阶段,或者已经写过几个小程序、但运行时总出莫名其妙的问题,这篇文章会帮你一次性理清printf和scanf的核心用法、常见坑,以及一套能照着做的学习路径。文章会从环境准备讲起,然后分别拆解这两个函数,最后用一个经典练习题把输入输出串起来。建议先收藏,再跟着动手实践。

1. 为什么 printf 和 scanf 是零基础入门的第一个分水岭

很多 C 语言入门教程会把printf放在第一节课,让学生“先用起来”。这个方向没有错,但也带来一个副作用:大量初学者以为printf只是打印文字、scanf只是读取输入,直到写复杂程序时才意识到,这两个函数背后牵涉到类型匹配、地址传递、缓冲机制,甚至字符编码。

如果你跟着翁恺老师的视频课或者其他经典 C 语言课程学习,你会发现printf和scanf一定出现在很靠前的位置。为什么?因为它们承担着程序与用户交互的最基础功能。程序不输出,你无法判断运行结果;程序不输入,你无法编写交互式程序。可以说,这两个函数是后续所有算法题、项目实战的基础设施。

从实际学习效果看,我见过太多同学在学指针时突然懵掉,回头一看,问题出在scanf的取地址符&上。也有人在学文件读写时反复出错,本质上还是不理解“格式化字符串”是怎么把不同类型的数据拼接到一起的。所以,现在花时间把printf和scanf搞清楚,不是在背两个函数,而是在为你之后学数组、结构体、指针、文件操作扫清最重要的前置障碍。

2. 认识 printf 和 scanf:格式化输入输出的核心概念

2.1 标准输入输出头文件 stdio.h

printf和scanf都是标准输入输出函数,声明在头文件stdio.h中。所以代码开头必须写:

#include <stdio.h>

只有包含这个头文件,编译器才知道printf和scanf的函数原型,否则会报“隐式声明”或“未定义”的错误。

2.2 printf 和 scanf 分别解决什么问题

  • printf的作用:按指定格式把数据输出到标准输出设备(默认是屏幕)。
  • scanf的作用:按指定格式从标准输入设备(默认是键盘)读取数据,并存储到变量内存中。

这两个函数都是“带格式”的,也就是说,你必须告诉它们:用整数方式解释?还是浮点方式解释?是输出一个字符还是一串字符?这就是“格式控制字符串”要做的事。

2.3 用类比理解格式化思想

你可以把printf想象成一家打印店,你拿着“格式单据”进去,单据上写好了模板,比如“今天是 %d 号”,再给店员一个具体的数值,店员就会把数值填入模板,输出“今天是 15 号”。

scanf则像一个“收件登记员”。你告诉它:“我要收一个整数、一个浮点数。”它就从键盘输入流里按顺序找出符合格式的字符,转换成对应类型,放到你指定的格子里。注意这里的关键词是“你指定的格子”,也就是变量地址。

2.4 最大的一个易混点:printf 用“值”,scanf 用“地址”

这可能是零基础阶段最重要的一个区别:

  • printf("年龄:%d", age);传入的是变量age的值。
  • scanf("%d", &age);传入的是age的地址,所以要写&age。

很多初学者在这里栽跟头。如果scanf里漏写&,程序并不会直接报错,而是会在运行时访问一个错误的内存地址,轻则变量没有被赋值,重则程序崩溃。这个问题在后面学指针时会得到更本质的理解,但在入门阶段,只需要记住:scanf要把输入的数据“写进”变量,所以必须给它变量的地址。

2.5 与其他语言的对比

Python的print和input使用起来非常灵活,几乎不用关心类型;Java的Scanner也封装了各种nextInt、nextLine。而 C 语言要求你必须精确控制类型和格式,这种“不友好”恰恰是它的优点:它逼着你理解数据在内存中是怎么表示的。带着这个视角去学习 C 语言,你会发现它不是在给你设障碍,而是在帮你建立对计算机底层更清晰的认识。

3. 环境准备:C 语言开发工具怎么选

在开始写printf和scanf之前,你需要一个能编译运行 C 程序的环境。很多搜索热词里出现“win10 安装 c 语言环境”“c 语言一般用什么编程软件”,说明环境搭建确实是新手第一个痛点。

3.1 Windows 用户的常见选择

工具特点适合人群
Dev-C++安装简单,内置编译器,零配置纯零基础,想快速跑通第一个程序
Code::Blocks开源免费,跨平台,配置较少入门到进阶过渡阶段
Visual Studio功能强大,Debug 体验好主要在 Windows 下开发的学生、开发者
VSCode + MinGW轻量,可定制,需要手动配置愿意花时间配置工具链的同学,也是实际工作中更常见的组合

对于零基础来说,第一目标不是用上最专业的工具,而是“能跑起来”。所以可以根据自己的耐心程度选择。如果你想减少配置成本,Dev-C++或在线编译器都足够起步;如果你打算长期写,建议一开始就用VSCode + MinGW或者Visual Studio,Debug 能力对后面学习帮助很大。

3.2 验证本地编译环境

如果你选择了本地编译器组合,可以打开命令行工具,输入以下命令确认编译器可用的版本:

gcc --version

如果能看到版本信息输出,说明 GCC 编译器已经安装成功。如果你的环境中没有gcc命令,大概率是MinGW或Visual Studio的编译工具没有加入系统PATH,检查一下安装目录下的bin文件夹是否包含gcc.exe。

3.3 第一个 C 语言程序

用任意编辑器新建一个hello.c文件,写入以下代码:

// 文件路径:hello.c #include <stdio.h> int main() { printf("Hello, World!\n"); return 0; }

然后在命令行进入该文件所在目录,执行编译和运行:

gcc hello.c -o hello ./hello

如果一切正常,终端会输出:

Hello, World!

这个程序虽然简单,但已经包含了一个 C 程序的基本骨架:头文件、main函数、语句、返回值。你已经在屏幕上看到printf的第一次输出了。

4. printf 函数详解:格式说明符、转义字符与常见坑

4.1 printf 的基本语法

printf的基本形式是:

printf("格式控制字符串", 参数列表);

格式控制字符串由两部分组成:

  • 普通字符:原样输出。
  • 格式说明符:以%开头,用于指定参数的类型和输出格式。

例如:

printf("年龄:%d 岁\n", age);

这里"年龄:%d 岁\n"是格式控制字符串,%d告诉编译器后面有一个int类型的参数要替换到这里,\n是换行符。

4.2 常用格式说明符对照表

格式说明符对应类型说明
%dint以十进制整数形式输出
%ffloat以小数形式输出单精度浮点数
%lfdouble以小数形式输出双精度浮点数
%cchar输出单个字符
%schar[]输出字符串
%xint以十六进制形式输出整数
%oint以八进制形式输出整数
%p指针输出指针地址
%%无输出一个百分号 %

这里特别提醒一个容易混淆的点:在printf中,float会被自动转换为double再传入,所以有时用%f输出double也不会出错;但在scanf中,读取double必须写%lf,不能用%f。这个细节到第 5 节还会强调。

4.3 宽度、精度和对齐控制

除了指定类型,printf还能控制输出的宽度和精度,这在写表格、对齐数据时非常有用:

printf("%5d\n", 42); // 宽度为5,右对齐,输出 " 42" printf("%-5d\n", 42); // 宽度为5,左对齐,输出 "42 " printf("%05d\n", 42); // 宽度为5,前面补0,输出 "00042" printf("%.2f\n", 3.14159); // 保留两位小数,输出 "3.14"

这些格式说明符在后续处理成绩表、日志输出、数据报表时会经常用到。

4.4 转义字符:输出“看不见”的字符

\n是换行符,也是最常用的转义字符。下面是几个常见的转义字符:

转义字符含义
\n换行
\t水平制表符(Tab)
\\输出一个反斜杠
"输出一个双引号
\0空字符,字符串结束标志

4.5 printf 常见坑

第一个坑是忘记换行。如果不写\n,连续多个printf的结果会挤在同一行,控制台看起来很难受。第二个坑是格式说明符和参数类型不匹配。比如用%d输出double,行为是未定义的,可能输出一个奇怪的大数,也可能输出 0,具体结果取决于编译器实现和内存布局。第三个坑是中文乱码,这在 Windows 控制台上尤其常见。解决方案在下面的示例和常见问题章节会专门展开。

4.6 printf 完整示例

下面这段代码集中演示了多种格式说明符和转义字符的用法:

// 文件路径:printf_demo.c #include <stdio.h> int main() { int age = 20; double height = 1.75; char name[] = "CSDN"; printf("姓名:%s\n", name); printf("年龄:%d 岁\n", age); printf("身高:%.2f 米\n", height); printf("编号:%05d\n", 42); printf("百分号:%%\n"); printf("引号:\"Hello\"\n"); return 0; }

这段代码的运行结果如下:

姓名:CSDN 年龄:20 岁 身高:1.75 米 编号:00042 百分号:% 引号:"Hello"

如果你在 Windows 下运行这段代码,发现中文输出是乱码,先不要慌。这个问题的典型原因是源码文件编码和控制台编码不一致。一种快速处理方式是让源码保存为“ANSI”(在 Windows 下通常是 GBK)编码,或者在编译器里调整源文件编码为“系统默认”。不同的编辑器操作位置不同,但思路是让“源码里的中文字节”和“控制台解码的字符集”对齐。

5. scanf 函数详解:地址符、输入缓冲与经典误区

5.1 scanf 的基本语法

scanf的基本形式是:

scanf("格式控制字符串", 变量地址列表);

它从标准输入读取数据,按照格式控制字符串的说明进行解析,并把结果写入对应地址。和printf不同,这里传的是地址,不是值。普通变量需要用&取地址。

5.2 为什么 scanf 必须加 & 地址符

可以这样理解:printf只需要读取变量的值,所以把值复制一份传进去就行;但scanf需要把键盘输入的数据“写”到变量所在的内存单元里,因此必须告诉函数这个变量在内存中的位置。如果不加&,scanf会把变量当前的值当成地址去写,轻则数据丢失,重则程序崩溃。

int age; scanf("%d", age); // 错误:缺少 &,程序可能在运行时崩溃 scanf("%d", &age); // 正确

这里也有一个例外:当读取的目标是数组名时,数组名本身就是首地址,不需要再加&。例如:

char name[20]; scanf("%s", name); // 正确,name 本身就是地址

这个解释在入门阶段可能有点绕,但你只需要先记住规则:scanf中普通变量前要加&,数组名不用加。等到学完指针,你自然就会理解其中的原理。

5.3 读取多个变量的输入格式

scanf可以一次读取多个变量,格式字符串中的空格、换行、Tab 都表示“跳过空白字符”:

int a, b; scanf("%d %d", &a, &b);

用户在键盘输入时,可以用空格分隔,也可以回车分隔:

10 20

回车结束后,a被赋值为 10,b被赋值为 20。

5.4 输入缓冲里的经典坑:读取字符会“吃掉”换行符

这是很多初学者最容易碰到的问题。请看下面这段代码:

#include <stdio.h> int main() { int n; char ch; printf("请输入一个整数:"); scanf("%d", &n); printf("请输入一个字符:"); scanf("%c", &ch); // 这里可能不会等待你输入 printf("n=%d, ch=%c\n", n, ch); return 0; }

运行后,输入完整数、按下回车,程序可能直接结束,根本没有等待你输入字符。原因是:你输入整数后按下的回车键\n还留在输入缓冲区里,紧接着的scanf("%c", &ch)直接从缓冲区里读取,于是ch接收到的就是换行符,而不是你之后想输入的字符。

解决办法是在%c前面加一个空格,让scanf先跳过空白字符:

scanf(" %c", &ch); // 注意%c前面的空格

这也算是入门阶段第一个真正需要理解“缓冲区”概念的场景。

5.5 用返回值判断输入是否成功

scanf的返回值是成功读取并赋值的参数个数。如果用户输入的内容和格式不匹配,返回值会小于预期值,甚至为 0。我们可以利用这个特点做简单的输入校验:

int n; if (scanf("%d", &n) == 1) { printf("读取成功,n=%d\n", n); } else { printf("输入不合法\n"); }

这个写法在后续刷题、写命令行工具时非常实用,能避免很多由非法输入导致的不可控行为。

5.6 scanf 完整示例

// 文件路径:scanf_demo.c #include <stdio.h> int main() { int a; char ch; printf("请输入一个整数:"); scanf("%d", &a); printf("你输入的整数是:%d\n", a); printf("请输入一个字符:"); scanf(" %c", &ch); // 注意%c前面的空格 printf("你输入的字符是:%c\n", ch); return 0; }

编译运行:

gcc scanf_demo.c -o scanf_demo ./scanf_demo

输入:

100 A

预期输出:

请输入一个整数:100 你输入的整数是:100 请输入一个字符:A 你输入的字符是:A

6. 综合实战:用 printf 和 scanf 完成“日期计算”小程序

理解了printf和scanf的基础用法后,我们用一道非常经典的 C 语言练习题来巩固实战能力。题目是:输入一个日期的年、月、日,计算并输出这一天是该年的第几天。

这道题在搜索热词中反复出现,说明它是很多课程和刷题平台的常客。它既用到了scanf读取多个变量,也用到了printf输出计算结果,还涉及数组、循环、函数和判断闰年,非常适合用来检验入门阶段的综合能力。

// 文件路径:day_of_year.c #include <stdio.h> int isLeap(int year) { return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); } int main() { int year, month, day; int days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int total = 0; printf("请输入年月日(格式:2024 3 15):"); if (scanf("%d %d %d", &year, &month, &day) != 3) { printf("输入格式不正确\n"); return 1; } if (month < 1 || month > 12 || day < 1 || day > 31) { printf("日期输入不合法\n"); return 1; } if (isLeap(year)) { days[1] = 29; } for (int i = 0; i < month - 1; i++) { total += days[i]; } total += day; printf("%d年%d月%d日是这一年的第%d天\n", year, month, day, total); return 0; }

关键逻辑拆解:

  • days数组保存平年每个月的天数,下标 0 对应 1 月。
  • isLeap函数判断闰年。如果是闰年,把 2 月天数改为 29。
  • scanf("%d %d %d", &year, &month, &day)一次读取三个整数,并且检查返回值是否为 3,确保三个数据都被成功读取。
  • 循环把month之前各月的天数累加,再加上当前月的day,得到该年的第几天。

这道题的难点不是最后的累加,而是对“闰年”这个细节的处理。很多初学者忘记判断闰年,导致 2 月的天数计算错误。这里顺便再说一句:如果以后遇到“每月天数”相关的题目,先考虑用一个数组来表示每个月,而不是写 12 个if,代码会更清晰。

7. 运行结果与效果验证

7.1 编译和运行

在命令行进入day_of_year.c所在目录,执行:

gcc day_of_year.c -o day_of_year ./day_of_year

7.2 预期输出

输入:

2024 3 15

输出:

2024年3月15日是这一年的第75天

为什么是 75?因为 2024 年是闰年,所以 2 月有 29 天。前两个月天数为 31 + 29 = 60,再加上 15,结果就是 75。

再测试一个平年日期:

2023 3 15

输出:

2023年3月15日是这一年的第74天

这是因为 2023 年不是闰年,2 月只有 28 天,前两个月天数为 31 + 28 = 59,加 15,结果是 74。

7.3 如何判断运行成功

  • 如果两个测试用例的输出都符合手算结果,说明程序逻辑正确。
  • 如果第一次运行就出现乱码,先检查控制台编码。
  • 如果输入后没有任何输出,检查是否卡在scanf等待用户输入,或者编译时是否有报错。
  • 如果程序崩溃,先把焦点放在scanf是否漏写&上,尤其是日期三个变量读入的部分。

8. 常见问题与排查思路

下面整理了一些新手在printf和scanf上最常遇到的问题,以及对应的排查方式和解决方案。

问题现象可能原因排查方式解决方案
printf 输出中文乱码源码文件编码与控制台编码不一致查看源码文件编码设置,查看控制台代码页Windows 下把源码改为 ANSI/GBK 编码,或执行chcp 65001切到 UTF-8
scanf 读取字符时程序不等待输入缓冲区中残留换行符被%c读到在读取字符前打印缓冲区状态或加调试输出在%c前加空格,写成scanf(" %c", &ch)
scanf 后变量没有被赋值变量前漏写&地址符检查函数调用中是否有&普通变量补写&,数组名不需要
编译报错implicit declaration没有包含stdio.h头文件检查代码第一行添加#include <stdio.h>
用 %d 输出 float/double 得到奇怪结果格式说明符和参数类型不匹配对照代码中的类型和格式符使用匹配的格式说明符,double 输出用%f或%lf
输入多个数据时程序行为异常格式字符串中分隔符与输入不一致检查scanf格式字符串中的空格和普通字符使用统一的分隔方式,推荐scanf("%d %d", &a, &b)
程序上屏闪退编译选项或运行方式问题在命令行中手动运行,观察输出Windows 下可在代码末尾加getchar()或使用命令行运行

其中“scanf 读取字符”和“printf 中文乱码”是所有初学者都会遇到的典型场景,强烈建议在执行本章示例时亲自复现一遍,再对照解决方案处理。亲手踩一次坑,比背十遍规则都管用。

9. 最佳实践与工程建议

9.1 保持格式说明符与变量类型匹配

类型匹配是printf和scanf的红线。尤其是double类型,在scanf中必须使用%lf,这一点需要刻意记住。在printf中,由于 C 语言的默认参数提升规则,float会被提升为double,所以有时用%f输出double并不会出错,但为了规范和可读性,建议始终使用与类型严格匹配的格式符。

9.2 给用户明确输入提示

无论是写课程作业还是命令行工具,输入前一定要用printf打印提示语。比如“请输入一个整数:”。否则用户面对一个黑框,完全不知道程序在等待什么。这个小习惯对你的代码可读性影响很大。

9.3 检查 scanf 的返回值

不要假设用户一定会按照格式输入。使用if (scanf(...) == 期望个数)来判断读取是否成功,是一种低成本、高收益的防御式编程方式。在很多在线练习平台的题目中,输入格式都比较规范,但在真实项目中,非法输入必须被拦截。

9.4 不要使用 gets 函数

在 C11 标准中,gets已经被移除,因为它无法限制输入长度,容易造成缓冲区溢出。企业级开发中,不要使用gets,改用fgets。对于字符串输入,scanf也建议指定宽度上限,避免溢出。不过宽度限制的写法在不同平台有细节差异,入门阶段先用%s理解字符串输入的基本方式即可,后续再深入学习字符串安全处理。

9.5 重视编码问题

中文乱码不是 C 语言本身的问题,而是“源码编码”和“控制台解码”不一致造成的。如果你是 Windows 用户,可以优先把源码保存为 ANSI 编码并在控制台使用默认代码页,这样中文输出最省心;如果你在跨平台项目中工作,通常使用 UTF-8,并配置控制台支持 UTF-8 输出。建议在项目开始时固定编码规范,避免项目后期出现大量乱码文件。

9.6 多写多练,把输入输出融入刷题环境

C 语言入门阶段最好的练习方式,是在洛谷、PTA、力扣等平台做题。这些平台上的简单题通常都要求你使用scanf读取数据、使用printf输出结果。刚开始可能觉得格式要求很严格,比如多一个空格都不行,但正是在这种“严格校验”下,你才会真正理解输入输出的细节。多练几道题后,printf和scanf就会从“新知识”变成“肌肉记忆”。

10. 总结:接下来该学什么

到这里,printf和scanf的核心内容已经梳理完了。这篇文章真正想讲的,不只是两个函数的语法,而是一套“程序如何与用户交互”的思维方式。你知道了printf用值、scanf用地址,知道了格式说明符要和类型匹配,知道了输入缓冲可能带来意料之外的行为。这些概念会在后续学习指针、数组、字符串和文件操作时反复出现。

下一步,建议你先按顺序做三件事:

第一,把文中的三个示例动手敲一遍,不要复制粘贴,亲手编译运行,感受一下从输入到输出的完整过程。

第二,修改示例中的格式符,故意制造错误,观察编译器报错和运行结果。比如把%d换成%f,看看输出会发生什么。这种“故意破坏”式练习,能帮助你更快记住规则。

第三,找几道入门级的输入输出题目练手,比如洛谷或 PTA 上的顺序结构练习题。当你能够熟练地用scanf读取多个数据、用printf格式化输出结果时,就可以进入下一阶段:数组、字符串和指针。

如果本文对你有帮助,建议收藏备用。遇到输入输出相关的问题,随时回来对照排查。C 语言入门不需要天赋,只需要把基础概念逐个击破,而printf和scanf,就是你打下的第一块地基。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询