
很多初学 C 语言的朋友打开书本或者视频课程后最先接触的往往就是printf(Hello, World!);和scanf()。这两个函数看起来很简单但它们在 C 语言学习中承担着非常关键的任务一个是把程序结果“说”出来一个是把用户的输入“接”进来。如果你完全没有编程基础或者以前学过一点但总是卡在“能看懂代码、自己写不出代码”的困境里那这篇教程就是为你准备的。本文会用尽量通俗的语言结合完整可运行的代码示例把 C 语言中printf和scanf的用法、原理、常见坑和实战套路一次性讲清楚。学完之后你不仅能自己写出简单的交互程序还能对 C 语言程序的整体运行流程有一个清晰的认识。1. 为什么入门 C 语言要先掌握 printf 和 scanf1.1 什么是 C 语言C 语言是一门非常经典的“中级语言”它既具备高级语言的表达能力又保留了可以直接操作内存、硬件的能力。正因为如此C 语言在操作系统、嵌入式开发、驱动开发、游戏引擎、数据库底层等场景中依然占有不可替代的位置。很多大学的计算机专业第一门编程课就是 C 语言原因也很简单C 语言语法相对简洁没有太多花哨的语法糖C 语言贴近底层能让你理解内存、指针、数据类型的本质学会 C 语言之后再学 C、Java、Python、Go 等语言都会轻松不少很多经典的数据结构、算法教材都用 C 语言描述。当然C 语言也有它的学习曲线。比如指针、内存管理、指针和数组的关系这些概念对零基础来说并不容易。但好消息是这些难点不会在一开始就拦住你。入门阶段我们可以借助printf和scanf先把“输入、处理、输出”这条主链路跑通。1.2 scanf 和 printf 在 C 语言中的角色C 语言程序本质上是在处理数据。一个程序通常可以拆成三步获取输入对数据进行加工处理输出结果。如果你没有学习图形界面编程那么最常用的输入输出方式就是控制台命令行窗口的文本输入和输出。scanf负责从键盘读取数据printf负责把数据打印到屏幕上。用一张简单的图表示这个流程键盘输入 - scanf 函数 - 内存变量 - 程序处理 - printf 函数 - 屏幕输出几乎所有入门 C 语言的小程序都离不开这两个函数。比如#include stdio.h int main() { int a, b; printf(请输入两个整数); scanf(%d %d, a, b); printf(它们的和是%d\n, a b); return 0; }这段代码虽然短但它包含了完整的“输入、处理、输出”结构。掌握printf和scanf就能写出很多有实际意义的小工具比如计算器、成绩等级判断、温度单位转换等等。2. 环境准备零基础该选哪款 C 语言开发工具2.1 常用开发环境对比很多初学者面临的第一个问题不是语法而是“我该在哪里写代码、怎么运行”。这里整理几款常见的 C 语言开发环境。开发环境适合人群特点Visual StudioWindows零基础入门、Windows 用户功能强大调试方便按 F5 就能运行但安装包较大有些快捷键需要适应Dev-C零基础入门轻量下载很快内置 GCC 编译器适合初学练习Code::Blocks零基础入门开源免费跨平台界面比较传统使用简单CLion进阶开发者JetBrains 出品需要付费订阅界面漂亮有学生免费授权但对新手来说配置稍微复杂Vim / GCCLinux学习型选手最贴近 Linux 开发环境但编辑器操作难度高不建议零基础一上来就尝试如果你是 Windows 用户我的建议是要么选 Visual Studio要么选 Dev-C。Visual Studio 的调试功能特别好可以在代码某一行打断点看到变量的实时变化这对理解scanf和变量的存储过程非常有帮助。Dev-C 胜在轻量不需要等待太久的安装过程适合快速写小例子。2.2 写第一个 C 程序框架不管用哪款 IDE新建 C 语言源文件后通常都会得到这样一个基本框架#include stdio.h int main() { // 你的代码写在这里 return 0; }逐行拆解#include stdio.h预处理命令表示引入标准输入输出头文件。stdio是Standard Input Output的缩写。因为printf和scanf的声明都在这份头文件里所以必须写上这一行。int main()程序的主函数整个程序从main函数开始执行。int表示这个函数返回一个整数。return 0;返回 0 表示程序正常结束。系统会根据这个返回值判断程序是否成功运行。2.3 编译运行过程说明C 语言是一门编译型语言它不像 Python 那样直接解释执行。一个.c文件要经过以下过程才能变成可执行程序.c 源文件 - 预处理 - 编译 - 汇编 - 链接 - .exe 可执行文件在 IDE 中你只需要点击“编译运行”按钮IDE 会在后台自动完成这些步骤。如果你用的是 Linux 下的 gcc 编译器命令是gcc test.c -o test ./test第一行命令把test.c编译成可执行文件test第二行命令运行它。初学者可以先不理解链接、汇编这些细节但要知道“写代码 - 编译 - 运行”是一个完整的过程报错往往就发生在编译阶段。3. printf 函数详解让程序开口说话3.1 最简单用法原样输出printf最基本的功能是原样输出一段字符串内容。格式如下printf(要输出的内容);例如#include stdio.h int main() { printf(Hello, World!); printf(C language is fun.); return 0; }运行结果Hello, World!C language is fun.注意两个printf之间并不会自动换行。如果你希望输出后换行需要在字符串中使用转义字符\n。\n是换行符Newline的缩写它并不会真的显示成“反斜杠n”而是让后续内容从下一行开始。修改上面的代码printf(Hello, World!\n); printf(C language is fun.\n);运行结果Hello, World! C language is fun.3.2 格式化输出%d、%f、%c、%s如果只想输出固定的文字那程序的意义不大。更常见的情况是你要输出某个变量的值。printf通过“格式控制符”来指定变量的类型和输出格式。先看一个例子#include stdio.h int main() { int age 20; float height 1.75; double pi 3.1415926535; char initial Z; char name[] Alice; printf(姓名%s\n, name); printf(年龄%d\n, age); printf(身高%.2f\n, height); printf(圆周率%lf\n, pi); printf(首字母%c\n, initial); return 0; }运行结果姓名Alice 年龄20 身高1.75 圆周率3.141593 首字母Z这些常用的格式控制符需要记牢格式控制符含义示例变量类型%d输出有符号十进制整数int%f输出浮点数默认保留 6 位小数float、double%lf输出双精度浮点数double%c输出单个字符char%s输出字符串char[]或char *%x输出十六进制整数int%o输出八进制整数int%%输出一个百分号无这里有两个细节需要特别说明。第一printf中%f和%lf其实都可以用来输出double类型的数据因为float在传入printf时会被自动转换为double。但在scanf中%f和%lf是严格区分的用错了就会导致数据读取异常。这一点我们后面讲scanf时会详细展开。第二格式控制符中间可以插入一些修饰符。比如%.2f表示浮点数保留两位小数%5d表示整数至少占 5 个字符宽度如果不足左边补空格。再看几个例子#include stdio.h int main() { int num 42; double price 3.14159; printf(数字%5d\n, num); // 宽度为5右对齐 printf(数字%-5d末尾\n, num); // 宽度为5左对齐 printf(价格%.2f\n, price); // 保留两位小数 printf(价格%8.3f\n, price); // 宽度8保留3位小数 return 0; }运行结果数字 42 数字42 末尾 价格3.14 价格 3.142这种格式控制在实际项目中经常用于打印表格数据、日志对齐等场景掌握之后输出效果会专业很多。3.3 转义字符printf中还有一类常见的字符叫转义字符。它们以\开头表示一些特殊功能。转义字符含义\n换行\t水平制表符Tab常用于对齐\\输出反斜杠本身\输出双引号\输出单引号\r回车例如要输出一行带引号和反斜杠的内容可以这样写printf(她说\你好\\n); printf(路径C:\\Program Files\\\n);运行结果她说你好 路径C:\Program Files\如果不使用\而直接写双引号编译器会认为字符串提前结束从而产生语法错误。3.4 printf 的常见误区第一个误区是忘记写#include stdio.h。如果代码里用了printf却没引入头文件编译器会报隐式声明错误。虽然某些旧编译器可能只给警告但建议始终写全。第二个误区是格式控制符和变量类型不匹配。比如用%d输出float类型变量结果可能是错误的。遇到输出乱码或明显不对的结果时可以先检查格式符和变量类型是否匹配。第三个误区是字符串中直接用中文双引号。C 语言中所有符号都必须是英文半角符号中文全角引号会导致编译失败。4. scanf 函数详解让程序学会接收信息4.1 基本用法scanf是输入函数它从键盘读取数据并按照你指定的格式存入对应变量。基本写法scanf(格式控制符, 变量地址);比如下面这段程序#include stdio.h int main() { int age; printf(请输入你的年龄); scanf(%d, age); printf(你输入的年龄是%d\n, age); return 0; }运行时程序会在“请输入你的年龄”处等待用户输入。用户输入一个整数并按下回车这个值就被存进了变量age然后被printf输出。4.2 为什么要写 符号很多初学者都会困惑为什么printf(%d, age)里不用写而scanf(%d, age)里必须写这里涉及到 C 语言一个很重要的概念函数传参和变量地址。简单理解printf只需要读取age的值所以直接把age传进去即可scanf要做的是把键盘读取到的数据写入age这个变量的内存空间中函数内部必须知道变量的内存地址才能修改它是取地址运算符age就是age在内存中的地址。你可以把变量看作一个盒子盒子里放着值。scanf需要把新的值放进盒子就必须知道盒子的位置。printf只需要看看盒子里放了什么所以不需要地址。初学者比较容易踩的坑是忘记写。如果scanf(%d, age)漏写了程序很可能在运行时崩溃或者读入一个无意义的值。编译器有时只给警告不会直接报错所以一定要仔细检查。对于字符串数组来说情况有点特殊char name[50]; scanf(%s, name);这里不需要写因为数组名name本身代表数组首元素的地址。如果你写name编译器会警告虽然某些编译器能运行但类型可能不完全匹配。建议记住规则普通变量需要字符数组名不需要。4.3 一次输入多个值scanf可以一次读取多个数据格式控制符之间用空格、Tab 或换行分隔都可以。例如#include stdio.h int main() { int a, b, c; printf(请输入三个整数); scanf(%d %d %d, a, b, c); printf(它们的和%d\n, a b c); return 0; }运行时用户既可以输入10 20 30也可以分多行输入10 20 30scanf会跳过空白字符空格、Tab、换行依次匹配三个整数。如果需求是读取小数要注意float和double的区别#include stdio.h int main() { float f; double d; printf(请输入一个 float 和一个 double); scanf(%f %lf, f, d); printf(float: %f, double: %f\n, f, d); return 0; }float对应%fdouble对应%lf。这里和printf不太一样printf输出时%f和%lf基本通用但scanf中如果float用了%lf或double用了%f数据都可能读错而且很难排查。4.4 scanf 的缓冲区陷阱scanf最经典的一个坑出现在混合读取数字和字符时。看下面这个例子#include stdio.h int main() { int age; char gender; printf(请输入年龄); scanf(%d, age); printf(请输入性别M/F); scanf(%c, gender); printf(年龄%d性别%c\n, age, gender); return 0; }当你输入20并按下回车后程序似乎没有等待第二次输入就直接结束了而且性别输出的内容很奇怪。这是因为scanf(%d)只读取了数字20而输入缓冲区中还残留了一个换行符\n。紧接着scanf(%c)读到的是这个换行符直接把它当作字符存进了gender于是第二次输入被“跳过”了。解决办法有几种。第一种在%c前面加一个空格让scanf跳过空白字符scanf( %c, gender);第二种在读取字符前用getchar()把缓冲区里的换行符吃掉getchar(); scanf(%c, gender);第三种使用while (getchar() ! \n);清空缓冲区。这种方法在需要清掉大量残留字符时更可靠但对初学者来说比较容易记混。建议刚开始时统一采用第一种方法在读取单个字符的格式串前面加一个空格。这个空格的含义是“跳过所有空白字符”包括空格、Tab 和换行。scanf( %c, gender);这个技巧看起来不起眼但在实际写作业、做项目时经常用到值得专门记下来。5. 综合实战做一个简单的个人信息录入程序5.1 需求分析综合上面介绍的printf和scanf我们来做一个完整的入门小项目个人信息录入程序。需求如下用户依次输入姓名、年龄、身高、性别年龄是整数身高是浮点数性别是单个字符 M 或 F程序在最后把用户输入的内容整理成一份清晰的个人信息表打印出来。功能虽然简单但包含的知识点非常密集字符串输入、整数输入、浮点数输入、字符输入、缓冲区处理、格式化输出。5.2 完整代码文件路径main.c#include stdio.h int main() { char name[50]; int age; float height; char gender; printf(请输入姓名); scanf(%s, name); printf(请输入年龄); scanf(%d, age); printf(请输入身高米); scanf(%f, height); printf(请输入性别M/F); scanf( %c, gender); printf(\n 个人信息 \n); printf(姓名%s\n, name); printf(年龄%d 岁\n, age); printf(身高%.2f 米\n, height); printf(性别%c\n, gender); printf(\n); return 0; }5.3 运行与结果分析在 IDE 中编译运行输入示例请输入姓名ZhangSan 请输入年龄21 请输入身高米1.78 请输入性别M/FM输出 个人信息 姓名ZhangSan 年龄21 岁 身高1.78 米 性别M 程序按预期工作。这里有几个细节需要注意char name[50]定义了一个字符数组最多可以存放 49 个字符。scanf(%s, name)不允许输入带空格的字符串比如Zhang San会被拆成Zhang和San两个部分程序只读取Zhang。如果希望读取带空格的字符串通常需要使用gets或fgets这属于后续进阶内容。身高输出时用了%.2f所以即使输入1.8输出也会显示为1.78或1.80这样两位小数。性别的scanf前面有空格这是为了避免读取残留的换行符。5.4 代码剖析这段代码的流程可以概括为提示输入姓名 - scanf(%s, name) 提示输入年龄 - scanf(%d, age) 提示输入身高 - scanf(%f, height) 提示输入性别 - scanf( %c, gender) 格式化输出个人信息其中scanf的格式控制符和变量写入方式如下表输入项格式控制符针对变量是否使用 姓名%schar name[50]否年龄%dint age是身高%ffloat height是性别%cchar gender是如果你运行后显示中文乱码不要慌下一节会专门讲乱码的解决方法。6. 附printf 输出中文乱码怎么解决6.1 乱码产生原因很多初学者在 Windows 上使用 Visual Studio 或 Dev-C 时会遇到printf输出的中文变成一堆乱码的情况。根本原因是源文件编码和运行控制台编码不一致。在 Windows 中控制台默认使用的代码页通常是 GBK即代码页 936而现代许多编辑器默认把源文件保存为 UTF-8 编码。编译时编译器读取 UTF-8 源文件把中文字符串的 UTF-8 字节序列写进程序运行时控制台却用 GBK 去解码这些字节于是显示出来的就是乱码。简单来说就是“写入时的编码”和“读取时的编码”对不上。6.2 解决方案根据你使用的工具不同有以下几种常见解决办法。方法一把源文件保存为 GBK/ANSI 编码。在 Visual Studio 中选择“文件 - 另存为”在“保存”按钮附近找到编码下拉框选择“ANSI”或“简体中文GB2312”即可。Dev-C 新版本也支持在编辑器中切换编码。这种方法可以保证源码和 Windows 控制台编码一致一般输出中文不会乱码。方法二在程序开头切换控制台代码页为 UTF-8。在main函数开头加一句#include stdlib.h int main() { system(chcp 65001); // 其他代码 }system(chcp 65001)会调用 Windows 命令把控制台代码页设置为 UTF-8。这样 UTF-8 编码的源文件在控制台输出时就能正常显示中文。缺点是system函数有一定安全隐患不过在学习阶段问题不大考试和比赛环境中一般不推荐使用。方法三直接用英文输出。如果你只是练习语法暂时不改编码也可以。等后续学习了文件系统、编码相关的内容再回过头来解决中文乱码心里会更清楚。需要说明的是在 Linux 和 macOS 的终端中默认编码通常是 UTF-8所以同样的程序基本不会出现中文乱码。乱码问题主要集中在 Windows 环境。7. 常见问题与排查思路零基础阶段最容易遇到下面这些问题整理成一张表格方便查阅。问题现象常见原因解决思路编译报错stdio.h: No such file or directory头文件路径配置错误或项目类型选错检查编译器配置确认项目是 C 项目而不是空项目编译报错undefined reference to main项目中没有main函数或main拼写错误检查函数名是否为main并确认函数体完整忘记写导致程序崩溃或数据异常scanf需要变量地址检查scanf中普通变量前是否写了第二次scanf被跳过缓冲区残留换行符在%c前加空格或使用getchar()清空输入20后输出结果多了一个换行或奇怪字符同样是缓冲区问题用scanf( %c, gender)处理字符输入printf输出乱码源文件编码和控制台代码页不一致保存源文件为 ANSI或者在程序中执行system(chcp 65001)用%d输出小数得到 0 或错误值格式控制符和变量类型不匹配检查%d、%f、%lf是否和变量类型对应输入字符串时无法输入空格scanf(%s)遇到空格会停止读取改用fgets或gets注意安全性scanf(%lf, d)写成%f读入数据异常float和double在scanf中格式不同double必须用%lf如果程序编译通过但运行结果不符合预期推荐按照以下顺序排查检查代码中是否有编译警告。警告也值得处理比如scanf的返回值未使用这类警告可能暗示潜在问题检查变量类型和格式控制符是否一一对应检查所有普通变量的scanf是否都有检查缓冲区是否残留换行符在关键位置临时用printf打印变量值确认输入是否真正成功。第 5 条是调试中的常用手段。比如你怀疑scanf没有正确读取年龄可以在scanf后面加一句printf(debug age%d\n, age);先确认数据是否到了变量里再往后排查。这种“中间打印”的调试方法在后续学习循环、函数、指针时也非常实用。8. 最佳实践与学习建议8.1 编码风格建议很多初学者觉得代码只要能跑起来就行其实从第一天开始就应该注意编码风格。C 语言代码风格规范不仅让自己看得懂也方便以后做课程设计、团队项目时和其他人协作。几个基本要求每个main函数内部的语句使用缩进一般缩进 4 个空格变量命名要有意义。比如age、height、gender而不是a、b、c常量名建议用大写比如#define PI 3.14159双目运算符两侧加空格比如a b而不是ab大括号换行风格保持统一要么都换行要么都不换行。8.2 输入输出安全scanf虽然方便但在生产环境中并不够安全。比如你没有限制输入内容用户输入了非数字字符scanf会读取失败并留下混乱的输入状态。安全一点的替代方案是使用fgets和sscanf组合。初学者暂时不必深入但要有这个意识C 语言不会替你检查输入合法性写代码时要自己考虑异常情况。8.3 后续学习路线学完printf和scanf之后你已经掌握了 C 语言“输入 - 处理 - 输出”的基本链路。接下来建议按这样的顺序继续学习变量与数据类型int、float、double、char的取值范围和存储区别运算符与表达式算术运算符、关系运算符、逻辑运算符、赋值运算符分支结构if、else、switch循环结构while、do-while、for以及break和continue的用法数组一维数组、二维数组、字符串数组函数自定义函数、参数传递、返回值、局部变量和全局变量指针地址、指针变量、指针与数组、指针与函数结构体自定义数据类型组织多条信息文件操作fopen、fprintf、fscanf等。在学习过程中尽量不要只看不写。每学一个新的语法点就把它搭配printf和scanf写成一个能运行的小程序。比如学到循环时可以写一个从 1 加到 100 的程序学到数组时可以写一个统计成绩的平均分程序。看得懂和写得出来之间有一段很长的距离这段距离只能靠敲代码来缩短。另外很多初学者会去看视频教程比如翁恺老师的《C语言程序设计》等经典课程。看视频的同时一定要跟着动手。哪怕只是简单地把老师的代码自己重新打一遍也会比单纯看一遍视频有效得多。最后再强调一点C 语言的很多语法细节比如%d、%f、、转义字符在刚接触时记不住是非常正常的。不要试图一次性背完多用、多查、多报错、多改正才是更可靠的学习方式。建议把本文中的代码亲手敲一遍再按照自己的需求修改数据比如把个人信息录入程序改成宠物信息录入、图书信息录入这样举一反三收获会更大。希望这篇教程能帮你迈出 C 语言学习的第一步。下一篇可以继续深入讲解变量、数据类型和运算符有需要的读者可以继续保持关注。