Appearance
数据类型和变量:认识 C 语言能处理的数据
预计学习时长:约 5 小时(本讲内容最多,建议分三次完成)· 运行环境:VS2026 / gcc / clang 均可
🎯 学习目标
学完本讲,你将能够:
- 说出 C 语言内置的字符型、整型、浮点型、布尔类型,以及各自的典型长度
- 用 sizeof 查询类型长度,用 limits.h / float.h 获取取值范围
- 正确创建、命名、初始化变量,区分全局变量与局部变量
- 正确使用算术、赋值、自增自减运算符,避开整数除法的坑
- 用 printf 按指定格式输出,用 scanf 安全地读取键盘输入
🔗 先修知识
- 第 2 讲:第一个程序:知道 #include、main、编译运行是怎么回事
- 还没装好编译器?先看附录 A:环境搭建指南
先修自检(三问)
- 一个 C 程序从哪一行开始执行?
printf是做什么的?需要哪个头文件?- 源代码写完后,要经过什么步骤才能运行?
答不上来先点开第 2 讲复习。
一、数据类型全家桶
C 语言提供了丰富的数据类型来描述生活中的数据。看一张购物小票:
| 信息 | 适合的类型 |
|---|---|
| 货号、数量 | 整数 |
| 品名、单位 | 字符串(字符组成) |
| 单价、小计 | 小数 |
所谓“类型”,就是相似数据的共同特征。编译器只有知道数据的类型,才知道该分配多少内存、怎么操作它。
1.1 字符型
c
char // 字符型,character,1 字节
[signed] char // 有符号字符型
unsigned char // 无符号字符型1.2 整型
c
short [int] // 短整型
int // 整型
long [int] // 长整型
long long [int] // 更长的整型(C99 引入)每种前面都可以加 signed(有符号,默认)或 unsigned(无符号)。比如 unsigned int、unsigned long long。
1.3 浮点型(小数)
c
float // 单精度
double // 双精度(默认选它)
long double // 更高精度1.4 布尔类型
C 语言原本用整数 0 表示假、非 0 表示真;C99 引入了专门的布尔类型 _Bool。包含头文件 <stdbool.h> 后,可以直接用 bool、true、false:
📄 bool_demo.c · ✅ 完整程序
c
#include <stdio.h>
#include <stdbool.h>
int main()
{
bool flag = true; // 等价于 _Bool flag = 1;
if (flag)
printf("我喜欢 C\n");
return 0;
}<stdbool.h> 里就是三个宏:bool 展开为 _Bool,true 为 1,false 为 0。
二、sizeof:查询类型占多大
每种数据都有长度。sizeof 是关键字也是运算符,用来计算类型或变量所占的字节数:
c
sizeof(类型)
sizeof 变量 // 操作数是变量(不是类型)时,括号可以省略sizeof 的返回值类型是 size_t(一种无符号整型别名),打印用 %zu。🟢
📄 sizeof_basic.c · ✅ 完整程序
c
#include <stdio.h>
int main()
{
int a = 10;
printf("%zu\n", sizeof(a)); // 4:变量
printf("%zu\n", sizeof a); // 4:变量可省略括号
printf("%zu\n", sizeof(int)); // 4:类型必须带括号
printf("%zu\n", sizeof(3 + 3.5)); // 8:表达式类型是 double
return 0;
}注意 sizeof(3 + 3.5) 里的表达式不会真的执行,编译器只看它的类型来确定大小。
2.1 各种类型的实际长度
📄 size.c · ✅ 完整程序
c
#include <stdio.h>
int main()
{
printf("char %zu\n", sizeof(char));
printf("bool %zu\n", sizeof(_Bool));
printf("short %zu\n", sizeof(short));
printf("int %zu\n", sizeof(int));
printf("long %zu\n", sizeof(long));
printf("long long %zu\n", sizeof(long long));
printf("float %zu\n", sizeof(float));
printf("double %zu\n", sizeof(double));
printf("long double %zu\n", sizeof(long double));
return 0;
}VS2026(X64)与 gcc(64 位)的典型结果:
| 类型 | VS2026 | gcc 64 位 |
|---|---|---|
| char / bool | 1 / 1 | 1 / 1 |
| short | 2 | 2 |
| int | 4 | 4 |
| long | 4 | 8 |
| long long | 8 | 8 |
| float / double | 4 / 8 | 4 / 8 |
| long double | 8 | 16(MinGW 上可能与 double 相同) |
两条要记住的规则 🟢:
sizeof(char)恒为 1(C11 §6.5.3.5);- 标准只保证
short ≤ int ≤ long ≤ long long且 short 至少 2 字节、int 至少 2 字节——具体字节数由平台决定,所以跨平台代码里不要写死“long 一定是 4”。
2.2 sizeof 里的表达式不计算
📄 sizeof_no_eval.c · ✅ 完整程序
c
#include <stdio.h>
int main()
{
short s = 12;
int b = 10;
printf("%zu\n", sizeof(s = b + 1)); // 2:结果类型是 short
printf("s = %d\n", s); // s 还是 12!
return 0;
}sizeof 在编译期就根据表达式类型算出了长度;赋值动作要到运行期才会发生,但那时 sizeof 早已处理完,所以赋值不会执行。
三、signed、unsigned 与取值范围
3.1 有符号与无符号
signed:带正负号,能表示负数。int默认就等同于signed int;unsigned:无符号,只能表示 0 和正数。同样字节下,最大值约增大一倍。
例如 16 位:signed short 范围是 -32768~32767;unsigned short 范围是 0~65535。
c
signed int a = -10; // 有符号
unsigned int b = 10; // 无符号;unsigned 后面的 int 可省略⚠️ char 与 int 不同:char 默认是否带符号由实现决定(gcc/MSVC 的 x86 平台是有符号,部分 ARM 平台是无符号)。需要确定时请明确写 signed char 或 unsigned char。🟡
3.2 取值范围:limits.h 和 float.h
与其背范围,不如直接用头文件里的常量,换平台也不会错:
| 常量 | 含义 |
|---|---|
SCHAR_MIN / SCHAR_MAX | signed char 的最小 / 最大值 |
SHRT_MIN / SHRT_MAX | short 的最小 / 最大值 |
INT_MIN / INT_MAX | int 的最小 / 最大值 |
LONG_MIN / LONG_MAX | long 的最小 / 最大值 |
LLONG_MIN / LLONG_MAX | long long 的最小 / 最大值 |
UCHAR_MAX / USHRT_MAX / UINT_MAX / ULONG_MAX / ULLONG_MAX | 对应无符号类型的最大值 |
整型极限在 <limits.h>,浮点极限在 <float.h>。
📘 提高(L2):INT_MIN 为什么写成 (-2147483647 - 1)
头文件里你会看到 #define INT_MIN (-2147483647 - 1) 而不是直接写 -2147483648。因为 2147483648 超出了 int 的最大值,在早期编译规则下这个整数字面量的类型处理会带来麻烦;拆成“-2147483647 再减 1”就始终在 int 范围内运算。
四、变量
4.1 创建与初始化
会变化的值叫变量,不变的值叫常量。创建变量的语法:
c
数据类型 变量名;
int age; // 整型变量
char ch; // 字符变量
double weight; // 浮点变量创建时就给值,叫初始化:
c
int age = 18;
char ch = 'w'; // 字符用单引号
double weight = 48.0;
unsigned int height = 100;⚠️ 局部变量一定要初始化
不初始化的局部变量,里面是内存中原来留下的“垃圾值”,结果不确定。养成声明时就赋初值的习惯(如 int sum = 0;)。
4.2 命名规则
- 只能由字母、数字、下划线
_组成; - 不能以数字开头;
- 区分大小写(
Age和age是两个变量); - 不能使用关键字(int、return 等)。
4.3 全局变量与局部变量
📄 global_local.c · ✅ 完整程序
c
#include <stdio.h>
int global = 2023; // 全局变量:定义在大括号外面
int main()
{
int local = 2018; // 局部变量:定义在大括号里面
printf("%d\n", local);
printf("%d\n", global);
return 0;
}| 类型 | 定义位置 | 作用范围 | 存放区域 |
|---|---|---|---|
| 全局变量 | 大括号外 | 整个工程 | 静态区 |
| 局部变量 | 大括号内 | 所在局部范围 | 栈区 |
同名时局部变量优先(局部会遮蔽全局):
📄 shadow.c · ✅ 完整程序
c
#include <stdio.h>
int n = 1000;
int main()
{
int n = 10;
printf("%d\n", n); // 输出 10
return 0;
}初学时记住内存三区:栈区放局部变量,静态区放全局变量,堆区用于动态内存(第 15 讲介绍)。
五、算术运算符
+、-、*、/、% 都是双目运算符(两个操作数)。
5.1 加减乘
📄 arith.c · ✅ 完整程序
c
#include <stdio.h>
int main()
{
int x = 4 + 22;
int y = 61 - 23;
int z = 5 * 5;
printf("%d %d %d\n", x, y, z); // 26 38 25
return 0;
}5.2 除法 /:整数除整数是整除
📄 int_div.c · ✅ 完整程序
c
#include <stdio.h>
int main()
{
float x = 6 / 4; // 6、4 都是 int,整除得 1,再转 float
int y = 6 / 4;
printf("%f\n", x); // 1.000000,不是 1.5
printf("%d\n", y); // 1
return 0;
}想得到小数,至少一个操作数得是浮点型:
📄 float_div.c · ✅ 完整程序
c
#include <stdio.h>
int main()
{
float x = 6.0f / 4; // 或 6 / 4.0
printf("%f\n", x); // 1.500000
return 0;
}⚠️ 典型逻辑错误:
c
int score = 5;
score = (score / 20) * 100; // 5/20 整除得 0,结果 0(本意是 25)改成 score / 20.0 让它做浮点除法,才能得到 25。
5.3 取模 %:只能用于整数
% 返回两整数相除的余数:
c
int x = 6 % 4; // 2负数取模,结果符号跟随第一个操作数(C99 起规定除法向 0 取整):🟢
📄 mod_neg.c · ✅ 完整程序
c
#include <stdio.h>
int main()
{
printf("%d\n", 11 % -5); // 1
printf("%d\n", -11 % -5); // -1
printf("%d\n", -11 % 5); // -1
return 0;
}六、赋值、自增自减与强制转换
6.1 赋值与连续赋值
c
int a = 100; // 初始化:创建时给值
a = 200; // 赋值:创建后再给值C 支持连续赋值,从右向左进行,但可读性差,建议拆开:
c
c = b = a + 3; // 能跑,不推荐
b = a + 3; c = b; // 推荐,调试时每步都看得清6.2 复合赋值
c
a += 3; // 等价 a = a + 3
a -= 2; // a = a - 2
a *= 5; // a = a * 5
a /= 3; // a = a / 3
a %= 2; // a = a % 2
// 还有 >>= <<= &= |= ^=([第 4 讲](/阶段1-从零开始/04-运算符详解)介绍)6.3 自增 ++ 与自减 --
前置:先加减,后使用
c
int a = 10;
int b = ++a; // a 先变 11,b 得 11后置:先使用,后加减
c
int a = 10;
int b = a++; // b 先得 10,a 再变 11-- 同理:
c
int a = 10;
int b = --a; // a=9, b=9(前置)
int c = 10;
int d = c--; // d=10, c=9(后置)6.4 单目 +、-
c
int a = +10; // 正号,可省略
int b = -a; // 负号,翻转正负:b = -106.5 强制类型转换
c
int a = 3.14; // double 转 int,编译器报警,a 得 3
int b = (int)3.14; // 显式强转,警告消失,只取整数部分“强扭的瓜不甜”——能不用就不用;需要时显式写出来,让人知道你是有意为之。
七、printf:格式化输出
7.1 基本用法
printf 把文本输出到屏幕,f 表示 format(格式化)。它不会自动换行,需要手动加 \n:
📄 hello.c · ✅ 完整程序
c
#include <stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}7.2 常用占位符
| 占位符 | 对应类型 | 占位符 | 对应类型 |
|---|---|---|---|
%d | int | %u | unsigned int |
%c | char | %o / %x | 八进制 / 十六进制整数 |
%f | double(float 自动提升) | %s | 字符串 |
%lf | printf 中等同 %f | %p | 指针地址 |
%zu | size_t | %% | 输出一个 % |
📄 placeholders.c · ✅ 完整程序
c
#include <stdio.h>
int main()
{
printf("There are %d apples\n", 3);
printf("%s 今年 %d 岁\n", "小明", 18);
return 0;
}📘 提高(L2):冷门占位符速查
- 浮点:
%a/%A十六进制浮点;%e/%E科学计数法;%g/%G短格式(自动选普通/科学计数法) - 长度修饰:
%hd(short)、%ld(long)、%lld(long long),对应无符号%hu/%lu/%llu;%Lf、%Le用于 long double %n:不输出字符,把“目前已输出的字符数”存入对应整型参数 占位符与参数必须一一对应,少了参数会读到不确定的值。
7.3 控制输出样式
📄 format.c · ✅ 完整程序
c
#include <stdio.h>
int main()
{
printf("[%5d]\n", 123); // 最小宽度 5,右对齐:[ 123]
printf("[%-5d]\n", 123); // 左对齐:[123 ]
printf("[%+d]\n", 12); // 正数也显示 +:[+12]
printf("[%.2f]\n", 3.14159); // 保留 2 位小数:[3.14]
printf("[%6.2f]\n", 3.14); // 宽 6、2 位小数:[ 3.14]
printf("[%.5s]\n", "hello world"); // 只取前 5 字符:[hello]
return 0;
}宽度和小数位也可以用 * 从参数传入:printf("%*.*f", 6, 2, 3.14);。
八、scanf:读取键盘输入
8.1 基本用法与 &
📄 scanf_basic.c · ✅ 完整程序
c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int score = 0;
printf("请输入成绩:");
scanf("%d", &score); // & 取地址:把输入放进 score
printf("成绩是:%d\n", score);
return 0;
}规则:除指针变量外,变量前必须加 &,因为 scanf 需要变量的地址来写入。
⚠️ VS 用户:error C4996: 'scanf'
VS2026 会对 scanf 报 C4996。解决办法:文件最顶部(所有 #include 之前)加 #define _CRT_SECURE_NO_WARNINGS。本讲完整程序都已加好,gcc、clang 无此问题。不建议改用 scanf_s——那是 MSVC 独有,无法跨平台。
8.2 一次读多个变量
数值占位符(%d、%f 等)会自动跳过空白(空格、Tab、换行),所以一行或分多行输入都可以:
🧩 示意片段
c
int i = 0, j = 0;
float x = 0, y = 0;
scanf("%d%d%f%f", &i, &j, &x, &y); // 输入:1 -20 3.4 -4.0e3scanf 从内部缓冲区按格式逐个解读,读到不符合该类型的字符就停下,下次调用从停下的位置继续。
8.3 返回值与 EOF
scanf 返回成功读取的变量个数:全部成功返回对应个数;匹配失败返回 0;在读到任何数据前遇到文件结尾,返回 EOF(即 -1)。
📄 scanf_return.c · ✅ 完整程序
c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int a = 0, b = 0;
float f = 0;
int r = scanf("%d %d %f", &a, &b, &f);
printf("r = %d, a = %d, b = %d, f = %g\n", r, a, b, f);
return 0;
}Windows 命令行下用 Ctrl+Z 再回车表示输入结束。
8.4 %c 不跳空白
%c 是唯一不跳过空白的占位符,会读入当前第一个字符(哪怕是空格、回车)。想让它跳过空白,在 %c 前加一个空格:
c
scanf(" %c", &ch); // 格式串里的空格 = 跳过 0 个或多个空白8.5 %s 与宽度限制
%s 从第一个非空白字符读到下一个空白为止,并自动在末尾加 \0,所以读不了带空格的字符串。scanf 不会检查数组长度,溢出会越界——必须用宽度限制:
📄 scanf_safe_string.c · ✅ 完整程序
c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
char name[11];
scanf("%10s", name); // 最多读 10 个字符,自动加 \0,绝不越界
printf("你好,%s\n", name);
return 0;
}8.6 %[] 字符集与赋值忽略符 *
%[] 限定只读取集合内的字符,如 %[0-9] 只收数字,遇到其他字符停止。
%* 表示“解读但不存储”,常用于兼容不同分隔符:
📄 date_input.c · ✅ 完整程序
c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int year = 0, month = 0, day = 0;
scanf("%d%*c%d%*c%d", &year, &month, &day);
printf("%d 年 %d 月 %d 日\n", year, month, day);
return 0;
}输入 2020-01-01 或 2020/01/01 都能正确解析——%*c 把分隔符读走但不保存。
⚠️ 常见坑与报错(本讲汇总)
| 你看到的现象 | 原因 | 修法 |
|---|---|---|
6 / 4 得 1 不是 1.5 | 两个 int 做整除 | 一侧改浮点:6.0 / 4 |
(score/20)*100 得 0 | 整除先丢了小数 | 写 score / 20.0 |
| 变量里出现奇怪数字 | 局部变量没初始化 | 声明时赋初值 |
| scanf 结果不对/程序崩溃 | 变量前漏了 & | scanf("%d", &x) |
| 读字符串时数组越界 | %s 没有宽度限制 | %10s 配合长度 11 的数组 |
%c 读进了回车/空格 | %c 不跳空白 | 写成 " %c" |
| gcc 警告 conversion from 'double' to 'int' | 浮点直接赋给整型 | 显式 (int) 或改用浮点变量 |
| VS 报 C4996: 'scanf' | VS 的安全警告 | 顶部加 #define _CRT_SECURE_NO_WARNINGS |
| printf 输出乱码或崩溃 | 占位符与参数不匹配 | 检查 %d/%f/%s 和参数一一对应 |
| 以为 long 一定是 4 字节 | 长度由平台决定 | 用 sizeof / limits.h |
🛠️ 动手练习
📊 本讲网页练习进度0 / 5(0%)
进度自动保存在本浏览器;编程题不计数,请在编辑器中完成。
练习 1 · ⭐ 基础 · 读输出
知识点:后置自增
📄 ex1.c · ✅ 完整程序
c
#include <stdio.h>
int main()
{
int a = 10, b = 20;
int c = a++ + b++;
printf("a=%d, b=%d, c=%d\n", a, b, c);
return 0;
}请写出 printf 的完整输出(逗号后有无空格都算对):
空①
练习 2 · ⭐ 基础 · 选择判断
知识点:类型长度规则
题干:下面哪些说法是正确的?(多选)
练习 3 · ⭐ 基础 · 读输出
知识点:整除与取模
题干:写出输出:
📄 ex3.c · ✅ 完整程序
c
#include <stdio.h>
int main()
{
int a = 17 / 5;
int b = 17 % 5;
double c = 17 / 5;
double d = 17.0 / 5;
printf("%d %d %.1f %.1f\n", a, b, c, d);
return 0;
}请写出四个结果(空格分隔):
空①
练习 4 · ⭐ 基础 · 找错改错
知识点:scanf 的 & 与字符串宽度
题干:下面代码有两处错误:
🧩 待改错片段
c
char name[10];
int age;
scanf("%s %d", name, age);请分别填出修正后的格式串(只需引号内部分)和第二个参数:
空①空②
练习 5 · ⭐ 基础 · 程序填空
知识点:复合赋值
题干:写出每一步执行后 a 的值:
🧩 待补全片段
c
int a = 10;
a += 5; // a = ______
a *= 2; // a = ______
a -= 8; // a = ______
a %= 7; // a = ______请按顺序填出四个值:
空①空②空③空④
练习 6 · ⭐⭐ 提高 · 写小程序
知识点:浮点输入输出
🛠 动手编程题 · 请在 VS2026(或你的编辑器)中完成
输入圆的半径,输出圆的面积,保留 2 位小数。公式 S = πr²,π 用常量 3.14159。
✅ 过关标准:输入 2 输出 12.57。
网页内无法练习写代码,亲手敲、亲手编译才能真正学会。下方按顺序展开 思路 → 步骤 → 答案。
💡 提示 1(思路)
π 在程序中固定不变,用 const 常量;半径和面积都用 double。
🧭 提示 2(步骤)
const double PI = 3.14159; double r = 0;scanf("%lf", &r); double s = PI * r * r;printf("%.2f\n", s);
🔑 参考答案与解释
c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
const double PI = 3.14159;
double r = 0;
scanf("%lf", &r);
double s = PI * r * r;
printf("%.2f\n", s);
return 0;
}为什么用 %lf 读 double、%f 打印:scanf 必须区分 float/double(%f/%lf);printf 中 float 会提升为 double,统一用 %f。
练习 7 · ⭐⭐ 提高 · 写小程序
知识点:浮点除法(避开整除坑)
🛠 动手编程题 · 请在 VS2026(或你的编辑器)中完成
输入华氏温度 F,输出摄氏温度 C。公式 C = 5 / 9 × (F − 32),保留 1 位小数。
✅ 过关标准:输入 100 输出 37.8;输入 32 输出 0.0。
网页内无法练习写代码,亲手敲、亲手编译才能真正学会。下方按顺序展开 思路 → 步骤 → 答案。
💡 提示 1(思路)
公式里的 5/9 若按整数写会整除成 0,必须让它成为浮点运算。
🧭 提示 2(步骤)
- 写成
5.0 / 9.0(或直接用 5.0/9) - double f;scanf("%lf", &f)
- double c = 5.0 / 9.0 * (f - 32.0)
🔑 参考答案与解释
c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
double f = 0;
scanf("%lf", &f);
double c = 5.0 / 9.0 * (f - 32.0);
printf("%.1f\n", c);
return 0;
}如果写成 5 / 9 会怎样:整除结果为 0,所有温度都输出 0.0——这是把数学公式翻译成 C 代码时最常见的错误。
练习 8 · ⭐⭐⭐ 挑战 · 写小程序
知识点:/ 与 % 的综合运用
🛠 动手编程题 · 请在 VS2026(或你的编辑器)中完成
输入一个总秒数(非负整数),把它换算成“X 小时 X 分 X 秒”。
✅ 过关标准:输入 3725 输出 1 小时 2 分 5 秒;输入 60 输出 0 小时 1 分 0 秒。
网页内无法练习写代码,亲手敲、亲手编译才能真正学会。下方按顺序展开 思路 → 步骤 → 答案。
💡 提示 1(思路)
“小时”是总数除 3600;拿走小时后,剩下的部分再处理分和秒。
🧭 提示 2(步骤)
- hours = total / 3600
- remaining = total % 3600
- minutes = remaining / 60;seconds = remaining % 60
🔑 参考答案与解释
c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int total = 0;
scanf("%d", &total);
int hours = total / 3600;
int remaining = total % 3600;
int minutes = remaining / 60;
int seconds = remaining % 60;
printf("%d 小时 %d 分 %d 秒\n", hours, minutes, seconds);
return 0;
}这是 / 和 % 最经典的搭配:/ 取出“高位单位”,% 取出“换算后的余数”,和第 6 讲逆序拆位是同一种思想。
📝 小结与自测
核心结论
- 内置类型:字符 char、整型 short/int/long/long long、浮点 float/double/long double、布尔 bool(C99)。
- sizeof 查字节数,返回 size_t 用 %zu;sizeof 中的表达式不执行;char 恒为 1,其余长度平台相关。
- unsigned 只表非负、最大值翻倍;char 的符号性由实现决定;范围常量在 limits.h / float.h。
- 整数除法是整除,想要小数必须有浮点操作数;% 只用于整数,负数取模符号跟随左操作数。
- printf/scanf 的占位符与参数必须一一对应;scanf 要加 &、字符串要限宽、
%c不跳空白。
自测清单
| 我能独立做到 | ✅ |
|---|---|
| 创建并初始化不同类型的变量 | |
| 用 sizeof 打印全部类型长度 | |
| 区分整数除法与浮点除法 | |
| 正确使用 ++/-- 和复合赋值 | |
| 用 printf/scanf 完成带格式的输入输出 | |
| 写出秒数换算程序 |
🔬 选学拓展(L3)
以下内容为选学
跳过不影响下一讲学习。
L3-1:整数溢出与“绕圈”现象
有符号整数运算溢出(如 INT_MAX + 1)是 🔴 未定义行为;而无符号整数溢出有良好定义:按 2ⁿ 取模,即“绕回”,例如 unsigned int 的 UINT_MAX + 1 得 0。做底层计数、哈希时常利用无符号绕回,但有符号溢出绝不能依赖。
L3-2:scanf 失败后输入还在缓冲区
scanf 匹配失败时,导致失败的字符不会被取走,下次 scanf 会再次撞上它。所以循环读输入时若不清理,会陷入死循环。稳健做法是检查 scanf 返回值,并用 while (getchar() != '\n') ; 清空行缓冲(完整封装在第 6 讲 L3 的健壮菜单中给出)。
L3-3:为什么 double 比 float 常用
float 只有约 7 位有效十进制数字,double 约 15~17 位;现代 CPU 处理 double 的速度并不比 float 慢,而精度高得多,所以 C 代码默认使用 double,只在内存紧张(大量数据、嵌入式)时才用 float。
预告:下一讲学习运算符详解:位运算与表达式求值。