数据类型与变量-Part3-输入输出格式化艺术 C语言输入输出格式化艺术系列导航✅ Part 1: C语言数据类型与变量基础篇✅ Part 2: C语言内存探秘进阶篇 Part 3: C语言输入输出格式化艺术 ← 你在这里上一篇我们深入了内存底层这篇我们来聊聊你和计算机对话的核心技能——printf和scanf。掌握格式化输出输入你就能写出像专业工程师一样的日志和交互界面。一、关于 Visual Studio 的强迫症警告如果你在 Windows 上用 Visual Studio 学习 C 语言一定遇到过这个烦人的红字“This function or variable may be unsafe. Consider using scanf_s instead…”为什么会有这个警告微软认为传统的 C 标准库函数如scanf、strcpy、sprintf存在安全隐患容易导致缓冲区溢出。VS 会强制报 C4996 警告建议你换成微软特有的安全版本如scanf_s。但scanf_s不是标准 C 语言函数在 GCC、Clang 等其他编译器上无法运行。为了保持代码的跨平台兼容性我们通常加一行宏定义来屏蔽警告#define_CRT_SECURE_NO_WARNINGS⚠️绝对致命的规则这行代码必须放在所有#include头文件之前放在后面无效双刃剑的真相为什么要加隐患在哪让我们专注于学习标准 C而不是微软的私有扩展scanf确实不安全——定义char name[10]用户输入 20 个字符程序就崩溃了阿文碎碎念初学时先用这个宏屏蔽警告但心里要时刻绷紧一根弦——永远不要相信用户的输入是规矩的。后期学了缓冲区限制记得把防御措施加上去。二、printf — 把数据送出去2.1 基本用法printf()的作用是将参数文本输出到屏幕。#includestdio.hintmain(){printf(Hello World);// 不自动换行printf(Hello World\n);// 加 \n 自动换行return0;}printf()不会在行尾自动添加换行符。想让光标移到下一行开头记得加\n。文本内部的换行也靠\nprintf(Hello\nWorld\n);// 两行输出// 等价写法printf(Hello\n);printf(World\n);2.2 占位符 — “这个位置留给别人”printf()可以在输出文本中指定占位符这个位置可以用其他值代入。printf(There are %d apples\n,3);// 输出: There are 3 apples占位符的第一个字符一律为%第二个字符表示类型%d表示整数%s表示字符串。printf(%s will come\n,特朗普);// 输出: 特朗普 will come2.3 格式化输出的完整公式printf的核心在于它的格式字符串完整写法拆解为五个部分%[标志][最小宽度][.精度][长度修饰符]类型说明符类型说明符 — 决定打印什么类型的数据这是占位符的灵魂必须和传入的变量类型严格匹配。类别占位符适用场景与说明⭐ 推荐指数基础整数%d,%iint最常用的有符号整数⭐⭐⭐⭐⭐%uunsigned int无符号整数⭐⭐⭐⭐%o八进制输出不带前缀 0⭐⭐⭐%x,%X十六进制输出嵌入式开发、颜色值⭐⭐⭐长度修饰整数%hd,%hushort短整型⭐⭐⭐%ld,%lulong长整型处理大数字、时间戳⭐⭐⭐⭐%lld,%llulong long超长整型⭐⭐⭐⭐浮点数%f,%Fdouble普通小数形式printf 中 float 会自动提升为 double⭐⭐⭐⭐⭐%.2f保留两位小数金钱计算必备⭐⭐⭐⭐⭐%e,%E科学计数法⭐⭐⭐%g,%G自动选最短格式去掉无用 0⭐⭐⭐⭐长双精度%Lf,%Lelong double注意是大写 L⭐⭐⭐字符与字符串%c单个字符⭐⭐⭐⭐⭐%s字符串⭐⭐⭐⭐⭐系统与指针%p打印指针地址调试神器⭐⭐⭐⭐%zusize_tsizeof的返回值⭐⭐⭐⭐%tdptrdiff_t指针差值⭐⭐⭐特殊功能%n将已输出字符数存入变量⭐⭐%%输出一个普通的百分号%⭐⭐⭐⚠️平台差异注意long double的占位符在 Windows (MSVC) 和 Linux 上有差异。MSVC 中long double往往是double的别名通常直接用%fLinux 下用%Lf。跨平台代码务必注意这一点。⚠️printf vs scanf 的关键区别在printf中float类型会自动提升为double所以打印float和double都用%f。但在scanf中没有这种自动提升读取float用%f读取double必须用%lf——这是很多新手踩的坑标志 — 决定对齐和样式写在%后面用来微调输出的外观标志作用示例-左对齐默认是右对齐%-10d强制显示符号正数也加%d→50用 0 填充前导空格%05d→00007空格正数前面留一个空格负数保持-% d→5或-5避坑指南%04d是打印编号的神器。想显示0007而不是7用它就对了最小宽度 — 决定最少占多少位置写在标志后面是一个非负整数。如果实际内容 最小宽度 → 左边补空格或0如果实际内容 最小宽度 →按实际长度完整打印不会截断%5d 打印 123 → 123左边补 2 个空格 %5d 打印 123456 → 123456超长不截断精度 (.precision) — 决定精确度写在宽度后面以点号.开头数据类型精度含义示例浮点数 (%f)小数点后保留几位默认6位%.2f→3.14字符串 (%s)最多打印几个字符%.3s→hel整数 (%d)最少显示几位不足补0%.5d→00123 宽度和精度经常结合使用%8.2f 总宽度至少 8 位其中小数占 2 位。长度修饰符 — 决定数据的具体长短修饰符适用类型配合占位符示例hshort%hd,%hul小写Llong%ld,%lull两个小写Llong long%lld,%lluL大写Llong double%Lf2.4 完整代码演示#includestdio.hintmain(){// 1. 补零与宽度控制常用于编号、时间printf(编号: %04d\n,7);// 输出: 编号: 0007// 2. 左对齐与宽度控制常用于打印表格printf(|%-10s|%10s|\n,姓名,分数);// 输出: |姓名 | 分数|printf(|%-10s|%10.1f|\n,张三,95.67);// 输出: |张三 | 95.7|// 3. 强制显示正负号printf(温度变化: %d 度\n,5);// 输出: 温度变化: 5 度printf(温度变化: %d 度\n,-3);// 输出: 温度变化: -3 度// 4. 字符串截取printf(缩写: %.3s\n,January);// 输出: 缩写: Jan// 5. 打印指针地址inta10;printf(变量a的地址: %p\n,a);return0;}三、scanf — 把数据读进来3.1 基本用法scanf用于从键盘获取数据到程序中的变量。#includestdio.hintmain(){intscore0;printf(请输入成绩:);scanf(%d,score);printf(成绩是%d\n,score);return0;}⚠️核心要点score前面的取地址符一定不能省略为什么printf是把变量里的数送出去屏幕所以传的是值scanf是把键盘的数读进来变量所以传的是地址——告诉计算机把数据存到这个变量的家里。3.2 一次读取多个变量scanf(%d%d%f%f,i,j,x,y);输入时可以用空格隔开一行或多行都可以scanf会自动跳过空格和换行符。⚠️避坑指南格式字符串中除了占位符和空白字符之外其他任何普通字符都是非输入控制符。scanf(%d,%d,%f,%f,i,j,x,y);// 用户输入必须严格匹配10,20,3.5,4.8// 输错任何一个标点后面的变量就无法正确读取3.3 scanf 的返回值scanf()的返回值是整数表示成功读取的变量个数返回值含义正数 N成功读取了 N 个变量0没有读取任何项或匹配失败EOF (-1)遇到文件结尾或读取错误#includestdio.hintmain(){inta0,b0;floatf0.0f;intrscanf(%d %d %f,a,b,f);printf(a%d b%d f%f\n,a,b,f);printf(r %d\n,r);return0;}测试场景输入返回值 r说明10 20 3.53三个全部读取成功10 20 CtrlZ2只读了两个第三个读取失败CtrlZ不输入任何数据-1 (EOF)直接遇到文件结尾3.4 常用占位符占位符读取类型说明%c字符%d整数%ffloat%lfdouble输入专用。printf输出时用%f因为 float 会自动提升为 double。scanf输入时必须用%lf来获取double的精确地址。%s字符串⚠️核心避坑 —%s的危险性charname[11];scanf(%s,name);上面代码是一个经典的缓冲区溢出漏洞。name数组长度 11最多存 10 个字符 \0但用户可以输入任意长度的字符串正确写法charname[11];scanf(%10s,name);// 限制最多读 10 个字符阿文碎碎念永远在%s前加一个数字限制留出\0的位置。这是防御性编程的基本素养。不加限制的%s就像是开着门欢迎黑客进来。3.5 严格按照格式化形式输入#includestdio.hintmain(){intyear0,month0,day0;scanf(%d-%d-%d,year,month,day);printf(%d %d %d\n,year,month,day);return0;}用户必须输入2025-01-01格式输入2025/01/01就会解析失败。inta;scanf(a%d,a);// 输入必须是a10不能直接输入 103.6 循环读入 — OJ 题常见场景scanf返回 EOF 表示读取结束所以! EOF就表示还有数据可读#includestdio.hintmain(){inta,b;while(scanf(%d%d,a,b)!EOF){printf(a%d,b%d\n,a,b);}printf(程序结束\n);return0;}输入输出测试输入输出1 2a1,b2CtrlZ(3次VS环境)程序结束小结这篇我们搞懂了输入输出的格式化艺术VS 安全警告#define _CRT_SECURE_NO_WARNINGS必须放最前面它是一把双刃剑printf 公式%[标志][宽度][.精度][长度修饰符]类型说明符——掌握它你就是排版大师printf vs scanf 的关键区别printf 中 float 自动提升为 double都用%fscanf 中 float 用%fdouble必须用%lfscanf 核心传地址不是传值——因为它是把数据读进来%s必须加数字限制scanf(%10s, name)而不是scanf(%s, name)循环读入while(scanf(...) ! EOF)是 OJ 题的标配系列导航✅ Part 1: C语言数据类型与变量基础篇✅ Part 2: C语言内存探秘进阶篇 Part 3: C语言输入输出格式化艺术 ← 你在这里三篇系列文章完结感谢阅读如果觉得有帮助欢迎点赞收藏转发 ❤️