
零基础学C语言最先碰到的不是指针也不是链表而是printf和scanf这两个函数。很多人学到这里会卡住printf的%到底写什么scanf为什么有时候要加有时候不加明明代码照着抄却出现中文乱码、输入跳过、程序闪退一堆问题。这篇内容就是专门讲这两个函数的适合刚接触C语言、还在跟着视频或教材一点点敲代码的零基础读者也适合在Dev-C、VS Code、Code::Blocks之间换环境时总被各种小问题打断的人。我不会把课本上那套格式化说明符表格原样搬过来而是按实际学习顺序拆先准备环境再跑通一个最小程序接着把printf和scanf的用法、格式、常见坑逐个说清最后用几个练习题说明输入输出到底怎么配。目标是让你看完以后自己能独立写出“输入两个数、输出计算结果”这类小程序并且报错时知道先去哪里看。1. 先搞清楚为什么C语言入门总绕不开printf和scanf1.1 printf和scanf在程序里到底做什么C语言程序本质上是对数据做处理。数据从哪里来要么在代码里写死要么从键盘读进来。处理完的结果怎么看要么存到文件要么打印到屏幕上。printf负责把结果按指定格式输出scanf负责按指定格式从键盘读入数据。这两个函数解决的就是“程序和外界的交互”问题。虽然一开始你可能觉得它们只是两个普通函数但后面的判断、循环、数组、字符串、指针题目几乎都要通过printf和scanf来展示结果。换句话说输入输出不熟练后面写再多逻辑也无法验证。你可能在一些课程视频里听到“格式化输入输出”这种说法。格式化是什么意思就是按照你指定的格式去读和写。printf里的%d表示“这里要放一个整数”scanf里的%d表示“从输入里读一个整数”。这就是格式化的含义。1.2 零基础学习路线里printf和scanf排在哪一步零基础学C语言常见的顺序是这样的先看懂一个最小程序的结构了解头文件、main函数、分号、大括号接着学变量和数据类型再学printf和scanf这时候才真正开始“能写能读”。之后才是if、for、while这些流程控制。很多新手容易犯一个错就是急着学指针、结构体结果连scanf都不熟练做题时数据读不进来或者读到一半程序就跳过输入了。我的建议是先花两到三天把所有基础输入输出场景都练习一遍整数、小数、单个字符、字符串、多组数据、带空格的输入。这些练熟了后面学数组、字符串、结构体才不会被输入输出卡住。1.3 网上的教程和练习题那么多怎么搭配着学搜“C语言零基础入门”会看到很多视频课和图文教程。比较多人推荐的是翁恺老师的C语言课程配套讲义和视频在公开学习平台都能找到。看视频时不要只看不写一定要跟着敲。配套的练习题可以在洛谷、PTA这些在线判题平台找入门题目做。在这些平台上做题时核心就是处理好输入格式。题目说“输入两个整数”你用scanf(%d %d, a, b)读就行题目说“第一行一个整数n”你就先scanf(%d, n)。这些能力说穿了还是printf和scanf的熟练度。我见过不少学习者视频看了几十集一上OJ就卡住。原因往往不是算法不会而是scanf格式和题目要求对不上。所以入门阶段输入输出格式的练习量一定要够。2. 环境准备好之前先别急着写代码2.1 Windows上选哪款编译环境更省事C语言需要一个编译器还需要一个写代码的地方。对零基础的人来说工具不是越多越好而是越少折腾越好。Windows平台下我比较推荐先装Dev-C。它的特点是体积小自带编译器安装完直接新建文件就能编译运行不需要额外配置环境变量。如果你觉得Dev-C界面有点旧也可以用Code::Blocks同样是开源免费的。VS Code不是不能用但对零基础用户来说有个门槛VS Code本身只是一个编辑器编译器要另外安装MinGW-w64还要配置tasks.json和launch.json。这个配置对还没有接触过“环境变量、PATH”这些概念的新手来说很容易劝退。等你用Dev-C写过几十道题再切换到VS Code也不迟。这里给一个通用判断标准如果你打开软件之后三分钟内不能从“新建文件”走到“编译运行”就说明这个环境对你的当前阶段来说太复杂先换个更简单。2.2 Linux和macOS上的打开方式如果你用的是Linux系统里通常自带gcc或者通过apt、dnf等包管理器安装一下就能用。写代码可以用gedit、VS Code、CLion也可以在终端里用vim或nano。入门阶段建议直接在终端里用gcc编译gcc main.c -o main ./mainmacOS类似可以先安装Xcode Command Line Tools然后同样用gcc或clang编译。不过在macOS上scanf在终端里的表现和Windows控制台有些差异比如换行处理上略有不同但入门题目基本不受影响。2.3 第一个“Hello World”程序怎么跑通不管你选哪个环境第一个程序的逻辑都一样。新建一个文件保存成main.c然后输入这段代码#include stdio.h int main() { printf(Hello, World!\n); return 0; }编译运行后屏幕上会输出一行Hello, World!这个程序解决了两件事第一验证你的编译环境是好的第二让你第一次感受printf输出。如果这一步看到中文乱码别急着处理先确认英文是否正常。英文正常说明程序没问题中文乱码是编码问题后面会专门讲。2.4 编译运行后常见的启动报错新手最常遇到的不是代码逻辑错而是编译环境问题找不到编译器安装时没有把编译器装全或者环境变量没配好。Dev-C一般不会出现。文件路径带有中文或特殊字符某些编译器对中文路径支持不好建议把源码文件放在C:\code\或D:\learn_c这类纯英文目录下。编译时提示undefined reference to main说明没有写main函数或者main拼写错误。运行窗口一闪而过程序结束了系统自动关闭窗口。可以在return 0前加一句getchar()或者用system(pause)但更推荐在IDE里直接点运行查看结果。排查顺序是先看软件下方面板有没有编译错误信息再看是不是代码里少了分号或大括号最后看文件位置和编译器配置。3. printf输出函数的使用与常见坑3.1 printf基本语法格式字符串和参数列表printf的第一个参数是格式字符串后面跟着要输出的数据。格式字符串里的普通字符会原样输出%开头的占位符会被后面的数据替换。int age 20; printf(我的年龄是%d岁\n, age);这里%d是占位符age是实际要输出的值。输出结果就是“我的年龄是20岁”。如果格式字符串里有%d但后面没有给对应参数程序输出的是不确定值有时甚至乱码。这种问题编译器不会报错只能自己养成“数量对应”的习惯。如果输出多个数int a 3; int b 5; printf(%d %d %d\n, a, b, a b);依次对应第一个%d放a第二个%d放b第三个%d放a b的结果。3.2 格式说明符什么时候用%d什么时候用%f这是新手第一个容易混淆的地方。C语言常见数据类型和printf格式符对应关系如下数据类型格式符示例int 整数%dprintf(%d, 100);long long 长整数%lldprintf(%lld, 10000000000LL);float 单精度小数%fprintf(%f, 3.14);double 双精度小数%fprintf(%f, 3.14);char 单个字符%cprintf(%c, A);字符串 char数组%sprintf(%s, hello);十六进制整数%xprintf(%x, 255);八进制整数%oprintf(%o, 8);double在printf里也用%f但在scanf里要写成%lf这是很多新手掉坑的地方。后面讲scanf时会再强调。如果你把整数用%f输出或者把小数用%d输出结果通常是乱数或0。编译器在部分情况下会提示format mismatch但也有很多编译器不警告所以看到输出怪异时先检查格式符和变量类型是否匹配。3.3 转义字符\n、\t、\到底怎么理解printf里常见反斜杠开头的字符它们不是普通字符而是转义字符。\n 换行输出后光标移到下一行。\t 制表符让输出对齐到下一个制表位。\ 输出一个反斜杠。 输出一个双引号。 输出一个单引号。\0 字符串结束标志这个在学字符串时会遇到。最容易出错的点是printf(a\nb)里的\n是在引号里面不是写成printf(a, \n)。刚起步时经常有人把\n放错位置编译直接报错。另一个容易忽略的\n不只是为了让输出“好看”它还会影响scanf的输入缓冲区。后面第4节会说明。3.4 printf中文乱码先分清是控制台编码还是编译器编码中文乱码是C语言入门的高频问题。编译环境把源文件保存成UTF-8编码而Windows控制台默认可能用GBK编码两边不一致输出的中文就变成乱码。处理办法有几种在Windows环境把源文件保存为GBK编码。Dev-C里可以通过“文件另存为”选择编码。在VS Code里输出乱码时可以在控制台执行chcp 65001切换到UTF-8代码页或者在文件保存时选UTF-8。在代码开头使用系统API控制控制台编码但这种方式依赖平台入门阶段不建议优先使用。最简单的操作先用英文输出测试程序本身如果英文正常说明问题就是编码不一致。然后按你的IDE和系统组合把源文件编码和控制台编码统一起来。注意做题和考试时特别是在OJ平台尽量使用英文输出或按题目要求的格式输出。中文内容容易因为编码问题导致非技术性失分。3.5 输出宽度、精度和对齐控制printf可以在占位符中间加数字控制最小宽度和精度。例如printf(%5d\n, 42); // 宽度5默认右对齐输出 42 printf(%-5d\n, 42); // 左对齐输出 42 printf(%.2f\n, 3.14159); // 保留两位小数 printf(%6.1f\n, 3.14159); // 宽度6保留一位小数控制宽度主要用于表格对齐比如输出成绩单时让数字上下对齐。精度控制则用来固定小数位数这在OJ题目里很常见要求输出保留两位小数就用%.2f。这些不是必须一开始就记住但要做到“看到题目要求保留几位小数时知道去printf里加.2”。4. scanf输入函数怎么用才不出错4.1 scanf基本语法把键盘输入按格式放进变量scanf的使用看起来和printf对称但多了一个重要符号int age; scanf(%d, age);%d告诉scanf“从输入里读一个整数”age表示“把这个整数放到age这个变量对应的内存地址中”。新手必须记住scanf里基本类型变量前要加。不加有些编译器只是警告但运行时会崩溃或出现无法预计的结果。多个变量可以写在一个scanf里int a, b; scanf(%d%d, a, b);输入时两个数之间用空格、换行或制表符分隔都可以。scanf遇到空白字符会跳过继续找下一个数字。4.2 为什么变量前要加什么时候不加是取地址运算符简单理解就是“找到变量的门牌号”。scanf要把键盘读到的数据写进变量内存里所以需要知道变量的地址于是要加。但也有不加的情况处理字符串时char数组名本身就是地址不需要再加。比如char name[50]; scanf(%s, name); // 这里不用写成name刚入门时你可以先记住一个简化规则int、char、float、double这些基本变量用scanf时都要加数组名作为字符串输入时不用加。等学到指针之后再回头看就会明白底层原因。4.3 scanf的匹配规则输入格式到底有多严格scanf的格式字符串里空白的处理方式很关键。如果写成scanf(%d %d, a, b)输入时两个整数之间可以用空格、回车或Tab隔开scanf会自动跳过空白。但如果你在格式字符串里写了非空白字符比如scanf(%d,%d, a, b)那么输入时必须也带逗号写成11,22。如果写成11 22scanf读到整数11后期望看到逗号但实际看到的是空格匹配失败b不会被读取。因此做题时首先要看题目怎么描述输入格式。题目说“两个整数之间用空格分隔”代码里写scanf(%d%d, a, b)或scanf(%d %d, a, b)都行。题目说“每个数字一行”也一样用%d读取因为scanf会自动跳过换行符。但如果题目明确写了“输入格式为x,y”那你就要按逗号匹配。这类情况在有些题目里会出现所以读题比写码更重要。4.4 缓冲区残留为什么scanf会跳过输入或读到旧数据这是C语言新手最头疼的问题。看这个例子int num; char ch; scanf(%d, num); scanf(%c, ch);你输入10然后按回车第一次scanf读走了数字10但回车产生的换行符\n还留在缓冲区。第二次scanf(%c, ch)不会让你输入而是直接把缓冲区里的\n读走了。于是ch就变成了换行符代码看起来像“跳过输入”。解决办法是在两个scanf之间清掉缓冲区残留scanf(%d, num); getchar(); // 吃掉换行符 scanf(%c, ch);或者读字符时在格式字符串前加一个空格scanf( %c, ch);格式字符串开头的空格会让scanf先跳过所有空白字符再读下一个非空白字符。另一个相关问题是反复检查scanf的返回值。scanf的返回值是成功读入的变量个数如果读不到返回值会小于预期。用这个可以判断输入是否结束。int x; while (scanf(%d, x) 1) { // 成功读取一个整数可以在这里处理 }多组输入题经常用这种写法。4.5 printf和scanf在格式上最容易配错的地方我总结三个最常见的格式配错第一double类型在printf里写%f在scanf里写%lf。printf(%f, d)没问题scanf(%f, d)读double可能出错。正确是scanf(%lf, d)。第二scanf指定了错误的分隔符。比如题目输入是空格分隔你却写scanf(%d, %d, a, b)那么输入11 22时b读不到。第三printf的换行和OJ输出格式不一致。OJ一般对输出结尾空格和换行的要求比较宽容但如果你多输出一个空格容易出现Presentation Error。所以注意题目要求“每个数之间用空格隔开行末无空格”这类描述。5. 从printf和scanf出发理解一个完整C程序的骨架5.1 最小但完整的程序结构一个能处理输入输出的程序至少要包含头文件、main函数、变量声明、输入、处理、输出几个部分。#include stdio.h int main() { int a, b, sum; scanf(%d%d, a, b); sum a b; printf(%d\n, sum); return 0; }这段代码覆盖了入门阶段最常见的流程。其中return 0表示程序正常结束有些教材写void main但正规的C标准是main返回int。作为零基础直接按int main写就好。5.2 变量声明、输入输出是怎么配合的在C语言中变量必须先声明再使用。声明就是告诉编译器“我要用一块内存来存整数”。比如int a;这一步做完后a的值是不确定的所以不要在没有赋值前直接printf输出a。输入的赋值由scanf完成。scanf相当于“从键盘取数并塞进变量”。这个操作和赋值运算符有点像但scanf是动态的程序运行到那里时才真正拿到用户输入的值。这里有一个细节在本地练习时输入前最好给用户一个提示。但OJ上不需要因为OJ会按标准输入自动提供数据如果你写printf(请输入两个数\n)反而可能影响OJ判定因为输出里多了无关内容。所以本地练习可以加提示OJ做题不要加。5.3 结合常见练习题拆解输入输出怎么落地搜“C语言练习题”会看到很常见的题目类型比如“输入一个正整数n求1到n之和”“输入一个日期判断是这一年的第几天”“字符串逆序”等等。这些题目的共同点是输入格式固定输出格式固定。拿“字符串逆序”这题来说如果题目要求输入一个字符串输出它的逆序代码可以这样写#include stdio.h #include string.h int main() { char s[100]; scanf(%s, s); int len strlen(s); for (int i len - 1; i 0; i--) { printf(%c, s[i]); } printf(\n); return 0; }如果题目里的字符串可能包含空格scanf(%s, s)就只读到空格前的一部分。这时要考虑用gets或fgets。不同平台对gets的支持情况不一样更稳妥的是用fgets。这个差异说明遇到题目时要搞清楚输入里有没有空格以及题目允许数组多长。再比如“输入一个日期计算它是一年中的第几天”这类题目通常先读三个整数year month day再用月份数组和循环累加。核心输入就一句话scanf(%d%d%d, year, month, day)。你会发现真正决定你能否轻松读题的还是scanf格式。5.4 在OJ上刷题输入输出经常遇到的三类问题第一类多组输入不知道怎么写。看到题目说“输入包含多组测试数据以EOF结束”就用while (scanf(%d, n) ! EOF)。EOF表示输入结束在Windows下可以通过CtrlZ再回车触发在Linux下是CtrlD。第二类输出格式不对。比如要求“每行输出一个结果”你少写了\n或者要求“两个数之间有一个空格”你写成了两个空格。这属于输出格式错误。第三类数组越界。题目明确n最大是100你数组开成a[10]输入一大直接越界。这不是printf和scanf的问题但往往体现在scanf写入数据时崩溃。建议数组多开一点比如题目说100长度你就开105。6. 学习过程中最值得留意的几个边界和排查顺序6.1 报错时先按这个顺序排查很多人遇到编译报错、运行卡住第一反应是怀疑编译器坏了。其实大部分问题出在代码本身。我一般按下面顺序排查看编译错误信息。C语言编译器会告诉你第几行出错比如expected ; before return意思是你漏了分号。看语法符号。中英文括号混用、分号写成中文全角这些都是零基础常见问题。看变量类型和格式符是否匹配。int配%dfloat配%fdouble在scanf里配%lf。看scanf是否漏了。漏了可能不报错但运行结果奇怪可能闪退。看输入格式是否符合scanf的格式字符串。如果scanf(%d,%d)你输入时有没有带逗号。看缓冲区是否有残留。连续scanf %c导致跳过输入时用getchar()处理。如果是运行结果不对但没报错重点检查逻辑和变量赋值不一定再盯着语法。6.2 程序卡在输入、跳过输入、无限循环是怎么回事表现一程序运行后什么都不显示一直等待输入。这是正常现象因为scanf在等你键盘输入。如果你已经在控制台输入了数据按了回车程序还是很怪再看格式是否匹配。表现二程序没有等待输入就直接结束或者读到的值不对。这通常是缓冲区残留。比如上一个scanf读取了整数剩余换行符被下一个%c读走。处理方式前面讲过用getchar()或scanf( %c)。表现三用while (scanf(%d, n) ! EOF)时程序读完数据后出现死循环。通常是因为输入里有非数字字符scanf无法读取返回0但字符还留在缓冲区导致循环不断失败。解决办法是当scanf返回值不是1时主动跳过非法字符。表现四printf输出了但内容不对比如输出0。这可能是因为变量没有正确初始化或者scanf没读到值。可以在scanf前后加printf调试看变量值有没有变化。6.3 路径、权限、资源占用这些非代码问题也要会看入门阶段很少遇到资源和权限问题但有两个场景值得知道。第一个场景文件操作。学会printf和scanf之后很多教程会引入fprintf和fscanf或者用freopen重定向输入输出。用freopen时文件路径写错会导致程序找不到文件。在Windows下路径里的反斜杠要转义比如写成test.txt相对路径最省事。如果文件打不开先确认文件是否存在、程序工作目录是否正确。第二个场景在Linux里用gcc编译多文件项目时如果忘记链接数学库会出现undefined reference to sqrt等错误。这时要在编译命令里加-lm。代码本身没有错但编译命令不完整。这类问题说明排查问题不能只盯代码也要看编译命令和环境。6.4 从printf和scanf往后学习重心应该怎么转移等你把printf和scanf练熟能独立完成“输入-处理-输出”的循环之后就可以往下学了。接下来通常会遇到分支和循环if、for、while配合printf输出判断结果。数组与字符串批量存储数据用printf遍历输出。函数把逻辑封装成子函数用printf做调试输出。指针理解scanf为什么需要理解数组名为什么不用。内存和文件操作涉及malloc、fopen与printf/scanf同族的sprintf、fprintf等函数。常用算法冒泡排序、选择排序、递归这些题目的输入输出核心仍然是scanf和printf。很多你搜到的热门话题比如“C语言指针”“C语言内存管理”“C语言文件读写操作”“C语言回调函数详解”都是在这个基础之后才容易理解。如果你现在还听不懂不必着急说明你还没到那个阶段。每个人的节奏不同。有人一两天就能把输入输出玩熟有人要一周。关键是每一种输入输出场景都亲手敲一遍而不是只看别人的代码。写得多了你会发现所谓“C语言从入门到放弃”其实很多人就卡在第一周而第一周里最常见的问题又集中在printf和scanf这些格式细节上。我个人更建议把上面这些代码示例自己新建一个工程一条一条地敲故意制造几个错误比如漏分号、漏、格式符写成%d输出浮点数看看编译器和运行结果会有什么样的反应。这种“故意踩坑”的过程比背十遍语法都管用。等你把错误都见过了再去做洛谷、PTA上的入门题就会顺手很多。