C语言输入输出补充

一、再次补充–核心基础语法对照表

常用数据类型 关键字 scanf/printf 占位符 占用内存(典型值) 适用场景
整型 int %d 4 字节 年龄、数量、整数计算
单精度浮点型 float %f 4 字节 身高、体重、带小数的计算
双精度浮点型 double %lf 8 字节 高精度科学计算
字符型 char %c 1 字节 单个英文字母、符号
字符串(字符数组) char[] %s 取决于定义的数组大小 姓名、地址、连续文本

二、 scanf 键盘录入三大黄金法则(重点考点)

法则 1:除了 %s,其余核心变量必须加取地址符 &

  • 原理scanf 需要知道变量在内存中的具体地址,才能把键盘输入的值填进去。
  • 数组特例:字符串数组名(如 name)本身就代表了该数组的首地址,因此不要&
1
2
3
4
5
6
7
8
9
char name[20];
int age;
float height;

// ❌ 错误写法:name 加了 & 会引发隐患;age 和 height 漏了 & 会导致程序直接崩溃
scanf("%s %d %f", &name, age, height);

// 正确写法:
scanf("%s %d %f", name, &age, &height);

法则 2:多个数据连续输入时,推荐用“空格”分隔占位符

scanf 的双引号内,占位符之间推荐使用空格分隔。

1
scanf("%d %d", &a, &b); // 推荐:输入时可以用【空格】或【回车换行】来分隔 a 和 b

⚠️ 反面教材:

  • scanf("%d,%d", &a, &b); ➡️ 输入时必须强行打逗号(如 10,20),极其不灵活,且不能用回车换行。
  • scanf("%d123%d", &a, &b); ➡️ 错误写法,严格禁止夹带其他乱码字符。

补充%d 的“贪婪读取”特性

%d 在读取键盘输入时,是一个贪婪的提取器
  • 机制:它会从第一个数字字符开始,一直往后吞数字,直到遇见第一个非数字字符(比如空格、回车、字母或标点符号)才会依依不舍地停下来。

📌 以你的代码为例:

第一个 %d 开始发威:它看到 678123789 全是数字,于是一口气把这一整串数字全部吃掉,赋值给变量 a(此时 a = 678123789)。

  1. 匹配格式控制符 123:第一个 %d 吃完后,输入缓冲区里已经空空如也了。但 scanf 格式串里后面还死板地写着 123,它在缓冲区里找不到 123 来匹配,导致匹配失败。
  2. 变量 b 的下场:因为前面的格式匹配在 123 这里断掉了,后面的第二个 %d 根本没有机会执行。最终,变量 b 无法分到任何值(保持原有的垃圾值或初始值)。

经典对比:格式串带固定字符的正确输入姿势

为了让你更直观地理解 scanf 的匹配逻辑,我们来看下面这个对比表格:

代码写法 (scanf 格式串) 键盘输入内容 实际变量分配结果 幕后解析过程
scanf("%d123%d", &a, &b); 678 123 789 (带空格) a = 678
b = 789
%d 遇到空格停下,a 拿到 678
② 顺着往下走,刚好匹配掉输入里的 123
③ 遇到第二个空格跳过,第二个 %d 拿到 789
scanf("%d123%d", &a, &b); 678123789
(连着打)
a = 678123789
b = 无值 / 失败
%d 太贪婪,把数字全吞了,a 拿到整串;
② 格式串里的 123 在缓冲区里找不到对应的字符,
匹配宣告破裂b 颗粒无收。
scanf("%d, %d", &a, &b); 10, 20 a = 10
b = 20
%d 遇到逗号停下,a 拿到 10
scanf 成功消掉格式串里的 ,
③ 第二个 %d 拿到 20

💡 考点总结

  1. scanf 双引号里写了什么,键盘就必须死板地敲什么
  2. 遇到非数字,%d 才会踩刹车。空格、回车、逗号、字母,都是 %d 的刹车片。

三、 printf 精准输出与格式控制

1. 小数点位数控制(%.Nf

%f 默认打印 6 位小数。通过在 %f 之间加入 .N 可以指定保留 N 位小数(遵循四舍五入原则)。

  • %.1f ➡️ 保留 1 位小数
  • %.2f ➡️ 保留 2 位小数

2. 规范输出习惯

  • 换行符 \n:在输出字符串的末尾加上 \n 可以使控制台在打印完毕后自动换行,保持控制台整洁。

  • 题目格式严格匹配

    例:题目要求格式为:我的姓名为XXX 年龄为XXX

    1
    2
    3
    printf("请录入你的姓名年龄与身高\n");   // 此处换行了 不然控制台输出就会粘在一起!
    scanf("%s %d %f", &name , &age , &height);
    printf("收到!你的名字是%s年龄是%d身高是%.2f", name, age, height);

四、Visual Studio 绿线警告与报错解析(不是很重要,只要代码能跑就行!)

在 Visual Studio 中写 C 语言时,经常会遇到静态分析器画的绿色波浪线。这虽然不影响编译运行,但代表代码存在不规范或潜在的安全隐患

1. 表达式直接丢给 printf 导致的警告

  • 现象:在长方体计算中,直接写 printf("...%.2f", chang * kuan); 容易在 VS 中引发警告。
  • 原因float 类型在进行乘法运算时,C 语言内部会自动将结果提升为 double(双精度)进行计算,这与 %f / %.2f 预期的 float 类型产生了微妙的类型安全不匹配。
  • 最佳工程化解决方案业务计算与展现逻辑分离。先定义专门的变量存储结果,再把变量传给 printf
1
2
3
4
5
6
// ❌ 不推荐(容易弹警告)
printf("A面的面积为%.2f\n", chang * kuan);

// 推荐写法(干净规范,杜绝警告,且极其方便调试)
float areaA = chang * kuan;
printf("A面的面积为%.2f\n", areaA);

2. scanf 安全性报错(C4996 经典报错)

  • 现象:VS 报错 error C4996: 'scanf': This function or variable may be unsafe...

  • 原因:微软认为传统的 scanf 不会检查输入边界,容易导致内存溢出(比如你的 name 只有 20 字节,用户却输入了 100 字节,就会导致程序崩溃或被黑客利用)。

  • 通用的 3 种解决方法

    1. 方案一(最推荐):在源文件的第一行(必须是首行)加入宏定义:

      1
      #define _CRT_SECURE_NO_WARNINGS
    2. 方案二:使用微软特有的安全函数 scanf_s(例如 scanf_s("%d", &a);),但注意这种写法在标准 C 语言考试或 Linux 编译器下不通用,专升本考试中仍需写 scanf

示例代码

键盘录入3个小数 分别表示长方体的长宽高 分别求A面(长宽) B面(宽高) C面(长*高)的面积与体积 结果保留2位小数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#define _CRT_SECURE_NO_WARNINGS // 解决 VS 安全报错
#include <stdio.h>

int main()
{
// 1. 变量定义
float chang, kuan, gao;

// 2. 规范输入
printf("请分别输入这个长方体的长、宽、高(用空格或回车隔开):\n");
scanf("%f %f %f", &chang, &kuan, &gao);

// 3. 数据记录反馈
printf("已记录!长:%.2f,宽:%.2f,高:%.2f\n", chang, kuan, gao);

// 4. 业务计算(定义中间变量,消灭printf乘方运算导致的绿线警告)
float areaA = chang * kuan;
float areaB = gao * kuan;
float areaC = chang * gao;
float volume = chang * kuan * gao;

// 5. 格式化输出
printf("经计算得A面的面积为:%.2f\n", areaA);
printf("经计算得B面的面积为:%.2f\n", areaB);
printf("经计算得C面的面积为:%.2f\n", areaC);
printf("经计算得该长方体的体积为:%.2f\n", volume);

return 0; // 规范结尾
}