跳到主要内容
🔍
1.2.2已发布beginner · 约 4 课时 · P0

数据类型与常量变量

掌握基本类型大小与范围、signed/unsigned 语义、const/volatile/restrict 与变量存储位置,理解 sizeof 与 limits.h

  • 来源
  • 补充
  • 纠错

标记说明:【来源】来自上传资料 · 【补充】课程新编 · 【纠错】按勘误表修正 · 【更新】过时内容已现代化 · 【待确认】无法可靠还原

完成标准(本章)

  • 📖 已阅读:滚动 ≥ 80% 且有效阅读 ≥ 180
  • ✏️ 已练习:小练习正确率 ≥ 60%
  • 📝 已通过测验:分数 ≥ 60
  • 🛠️ 已掌握还需完成实践任务
学习状态:未开始

1.2.2 数据类型与常量变量

本章来源:基本类型分类与大小、signed/unsigned、变量声明与初始化、作用域与生命周期来自《第一阶段讲义》13.x/14.x 节【来源】,经重新组织表述;三条【纠错】已落地:sizeof(void)=1 是 GCC 扩展(err-7.3-9)、浮点常量默认 double(err-7.3-10)、volatile≠多任务同步方案(err-7.1-30);全部示例与练习为新编【补充】并本机实测(gcc 15.2.0 / MinGW-w64 x86_64,约 10% 新编)。平台适用性 universal。

① 学习目标

  1. 说出 char/short/int/long/long long/float/double 的最小范围保证与本机实测大小;
  2. sizeoflimits.h/float.h 实测任意类型的字节数与边界值,不背"固定表";
  3. 说清 signed/unsigned 的语义差别,并知道有符号溢出是未定义行为(UB);
  4. 说出整数字面量与浮点字面量的默认类型(3.14 是 double、3.14f 才是 float);
  5. 区分 const / volatile / restrict 的语义与典型场景,并说出 volatile 的一个常见误区;
  6. 说清局部/全局/static 变量的作用域、生命周期与存储位置差异。

② 前置知识

  • 必选:1.2.1(C 程序结构与标准演进——本章假设能独立编译运行单文件程序)。

③ 核心概念【来源】

概念说明
基本整型char(恒 1 字节)、shortintlonglong long。标准只保证最小范围(int ≥16 位、long ≥32 位、long long ≥64 位)与"short ≤ int ≤ long ≤ long long"的相对关系;本机实测 1/2/4/4/8 字节
sizeof 运算符编译期求类型/表达式的字节数;sizeof(char) 恒为 1;结果是平台相关的(本机 MinGW long=4,Linux x86_64 long=8)
limits.h / float.h各类型边界值的标准来源(INT_MAXUINT_MAXFLT_MINDBL_MAX 等),跨平台代码一律查它而不是背数字
signed / unsigned默认整型有符号;unsigned 范围 0~2ⁿ-1。unsigned 溢出是定义的(wrap,模 2ⁿ);有符号溢出是未定义行为(UB),不能当 wrap 规律
_Bool / bool_Bool 只存 0/1(本机 1 字节);#include <stdbool.h> 后用 bool/true/false
浮点类型float(约 6-7 位有效数字)/ double(约 15-16 位)/ long double;浮点比较用容差不用 ==
整数字面量默认类型十进制/八进制/十六进制常量按 int → long → long long 第一个"放得下"的类型;后缀 u/U(unsigned)、l/L(long)、ll/LL(long long)
浮点字面量默认类型**【纠错】**无后缀浮点常量默认是 double(3.14 是 double;3.14f 才是 float)——见 err-7.3-10
const编译期"只读"限定:修饰变量表示初始化后不可再赋值(编译器强制);函数参数 const T * 承诺不修改指向内容
volatile每次访问都强制从内存读写(编译器不得把访问优化掉/缓存进寄存器);用于硬件寄存器、信号/中断与主流程共享的变量【纠错】不保证原子性与内存序,不是多线程同步方案——见 err-7.1-30
restrict指针限定符:向编译器承诺"该指针是所指向对象的唯一访问途径",允许更激进的优化(memcpy 的参数即用 restrict)
作用域与生命周期作用域=名字可见范围(块/文件);生命周期=对象存活期。局部变量(栈,auto):块内;全局变量:整个程序;函数内 static:生命周期=整个程序、作用域不变;文件内 static 函数/变量:只本文件可见
sizeof(void)**【纠错】**GCC 扩展(结果 1),C 标准不允许对 void 类型求 sizeof——见 err-7.3-9

④ 通俗解释【补充】

  • 类型是"箱子尺寸"char 是 1 格小箱、int 是 4 格中箱。放得下的数相安无事,放不下就是溢出——unsigned 溢出是"转圈回到 0"(标准规定),signed 溢出是"箱子炸了,后果自负"(未定义行为);
  • sizeof 是尺子:别背尺寸表,编译期用尺子量一次,比任何"常见值"都可靠;
  • const 是"请勿改动"标签:贴了标签的箱子,编译器替你盯着,谁写谁报错;
  • volatile 是"别信缓存"标签:默认情况下编译器可能"上次看过就不再看";贴了 volatile 的箱子,每次都要亲自去仓库看最新值——但它管不了"两个人同时改"(那是锁/原子操作的活);
  • 函数内 static 是"只进一次门的账本":第一次进来时记账本,之后每次进来翻同一本——账本(生命周期)活到程序结束,但只有这个函数(作用域)能翻它。

⑤ 示例代码

代码示例与验证记录

  • examples/ex1-type-sizes.c基本类型 sizeof 与 limits.h/float.h 边界实测✓ 已实测(gcc 15.2.0 / MinGW-w64 x86_64 / Windows 11, 2026-08-15
    编译:gcc ex1-type-sizes.c -o ex1 -std=c11 -Wall -Wextra -Wpedantic
    适用环境:LinuxWindows(MinGW)
    展开预期输出(实测)
    sizeof(char)        = 1
    sizeof(short)       = 2
    sizeof(int)         = 4
    sizeof(long)        = 4
    sizeof(long long)   = 8
    sizeof(float)       = 4
    sizeof(double)      = 8
    sizeof(long double) = 16
    sizeof(void*)       = 8
    sizeof(_Bool)       = 1
    char 范围: -128 ~ 127
    short 范围: -32768 ~ 32767
    int 范围: -2147483648 ~ 2147483647
    long long 范围: -9223372036854775808 ~ 9223372036854775807
    unsigned int 最大值: 4294967295
    float 范围: 1.17549e-38 ~ 3.40282e+38
    double 范围: 2.22507e-308 ~ 1.79769e+308
    

    差异说明:本输出为 gcc 15.2.0 / MinGW-w64 x86_64 实测(0 警告)。注意 long=4 字节(Windows LLP64 数据模型): Linux x86_64 为 8 字节(LP64)——sizeof 结果随平台变化,这就是"量而不背"的原因

    完整源码见 /code 代码示例页

  • examples/ex2-const-volatile.cconst 只读保护 / volatile 指针访问 / 函数内 static 计数✓ 已实测(gcc 15.2.0 / MinGW-w64 x86_64 / Windows 11, 2026-08-15
    编译:gcc ex2-const-volatile.c -o ex2 -std=c11 -Wall -Wextra -Wpedantic
    适用环境:LinuxWindows(MinGW)
    展开预期输出(实测)
    N = 10
    两次 volatile 访问后 reg = 2
    第1次 counter() = 1
    第2次 counter() = 2
    第3次 counter() = 3
    

    差异说明:本机实测(0 警告)。const 赋值错误已用注释演示(取消注释会编译失败:assignment of read-only variable); static 计数 1/2/3 证明值在调用间保留且只初始化一次

    完整源码见 /code 代码示例页

  • examples/ex3-sizeof-void-diag.csizeof(void) 的 GCC 扩展警告(diagnostic:预期警告)诊断示例(预期报错/警告)✓ 已实测(gcc 15.2.0 / MinGW-w64 x86_64 / Windows 11, 2026-08-15
    编译:gcc ex3-sizeof-void-diag.c -o ex3 -std=c11 -Wall -Wextra -Wpedantic(预期警告,exit 0)
    适用环境:LinuxWindows(MinGW)
    展开预期输出(实测)
    ex3-sizeof-void-diag.c:4:28: warning: invalid application of 'sizeof' to a void type [-Wpointer-arith]
        4 |     printf("%zu\n", sizeof(void));   /* GCC 扩展;C 标准不允许对 void 求 sizeof */
          |                            ^~~~
    运行输出:1
    

    差异说明:gcc 15.2.0 实测:-std=c11 -Wall -Wextra -Wpedantic 下给出上述警告(-Wpointer-arith)且仍能编译、结果为 1—— 这正是 err-7.3-9 说的"GCC 扩展";C 标准不允许对 void 求 sizeof,严格工程(-Werror)会编译失败。 language 用 text 使 verify:examples 不把本示例当作必须编译通过的示例

    完整源码见 /code 代码示例页

⑥ 编译与运行方法

gcc ex1-type-sizes.c -o ex1 -std=c11 -Wall -Wextra -Wpedantic
ex1.exe        (Linux: ./ex1)
  • ex1 打印全部类型 sizeof 与 limits.h/float.h 边界,本机实测输出见示例卡(注意本机 long=4 字节:MinGW 的 LLP64 数据模型;Linux x86_64 为 8 字节);
  • ex2 演示 const 只读保护、volatile 的指针访问与函数内 static 计数(真实输出见示例卡);
  • ex3 是 diagnostic 示例(sizeof(void) 的 GCC 扩展警告),预期出现警告,不作为普通运行示例。

⑦ 常见错误

症状原因解决
【纠错】看到资料写 sizeof(void)=1那是 GCC 扩展,不是 C 标准标准写法 sizeof(char)/sizeof(表达式);见 err-7.3-9(ex3 实测警告)
【纠错】以为浮点常量"没有默认类型"无后缀浮点常量默认就是 double3.14 是 double,3.14f 才是 float;见 err-7.3-10
【纠错】用 volatile 做多线程共享变量的同步volatile 不保证原子性与内存序多线程用互斥锁/原子操作;见 err-7.1-30
int 溢出得到"负数的相反数",当成 wrap 规律有符号溢出是 UB,编译器优化后表现可能"反常"需要 wrap 就用 unsigned;有符号运算避免溢出
%d 打印 long/unsigned/size_t 结果错乱格式说明符与实参类型不匹配(UB)long→%ld、unsigned→%u、size_t→%zu
32 位机器上写死的 4 字节假设在 64 位/嵌入式上失效sizeof 与边界随平台变化一律 sizeof/limits.h 实测;移植代码避免硬编码

⑧ 小练习

小练习

学习自测:提交后才显示答案与解析(前端判分,不作为正式考试)
  • ex-1-2-2-1.C 标准保证字节数恒为 1 的类型是?(单选)

    ◌ 未作答
  • ex-1-2-2-2.int 变量自增越过 INT_MAX 后,结果是?(单选)

    ◌ 未作答
  • ex-1-2-2-3.代码 `unsigned int u = 10; int i = -1;` 中 `i < u` 的结果是?(单选)

    ◌ 未作答

⑨ 章节测验

章节测验

5 题题库 · 随机抽 5 题 · 及格线 60 分 · 前端判分(学习自测)
开始测验 →

⑩ 实战任务

实践任务

sizeof 全类型实测表 + volatile 寄存器访问小实验

参照 ex1 写出自己的"类型实测表"程序(打印全部类型 sizeof 与 int/unsigned/float/double 边界值), 并参照 ex2 写一个 volatile 变量经指针连续两次自增的小程序;把输出记录进笔记, 并能说出 volatile 的三个使用场景与一个误区。

输入与输出

无外部输入。交付物:① 自写的 sizeof 实测程序与完整输出记录;② volatile 指针访问程序(输出 2); ③ 笔记小结(volatile 三场景 + 一误区)。

功能要求

  • 自写 sizeof 程序:打印 char/short/int/long/long long/float/double/long double/指针/_Bool 的 sizeof
  • 用 limits.h 打印 INT_MAX/UINT_MAX、float.h 打印 FLT_MAX/DBL_MAX
  • 自写 volatile 实验:volatile int 经 volatile 指针连续两次 +1,输出 2
  • 笔记写出 volatile 三个场景(硬件寄存器/信号处理共享变量/setjmp-longjmp)与一个误区(多线程同步)

限制条件

  • 编译命令 -std=c11 -Wall -Wextra -Wpedantic,正常示例 0 警告
  • 不运行任何未定义行为代码

验收步骤(自检清单 0/3

验收标准

  • sizeof 程序覆盖全部要求类型且输出与 limits.h/float.h 一致(验收步骤 1)
  • volatile 程序输出为 2 且 0 警告(验收步骤 2)
  • 小结三场景与误区表述正确(误区必须提到"不保证原子性/内存序")(验收步骤 3)

常见失败原因

  • sizeof 程序漏了 long 或指针(平台差异最明显的两个)
  • 背字节数而不是看本机输出
  • 把 volatile 写成多线程同步手段
  • 有符号/无符号混用比较出现反直觉结果还不知道原因

可选扩展

  • 对比 Linux x86_64 的 sizeof(long)(应为 8),理解 LP64 与 LLP64
  • 用 gcc -O2 与 -O0 分别编译 volatile 实验对比反汇编(了解即可,GDB 章再深入)

完成必要清单后才能计入"已完成实践"(学习状态自动推导,不提供一键完成)

⑪ 面试问题

面试问题

  • sizeof(int) 为什么不能背成固定 4 字节?高频C/C++ · easy

    要点:sizeof 结果由实现/平台(数据模型、ABI)决定:标准只保证最小范围与相对关系;本机 MinGW 的 long 是 4 字节、Linux x86_64 是 8 字节,嵌入式可能不同。

    C 标准对整型只规定最小范围(如 int 至少 16 位)与"short ≤ int ≤ long ≤ long long"的相对关系, 具体字节数由实现的数据模型决定:Windows/MinGW 用 LLP64(long=4、指针=8),Linux x86_64 用 LP64(long=8)。 所以跨平台代码一律用 sizeof 实测 + limits.h 边界值(INT_MAX 等),不硬编码、不背表。 唯一恒定的只有 sizeof(char)=1。

    追问:
    • 追问:sizeof 是运行时算的吗?(不,编译期运算符)
    评分要点:
    • 平台相关而非语言规定
    • sizeof/limits.h 的用法
    • char 恒 1
  • const 和 volatile 分别解决什么问题?可以同时修饰一个变量吗?高频C/C++ · medium

    要点:const 表达"只读"(编译器强制不可再赋值);volatile 表达"每次访问都真实读写内存"(防优化)。可以同时用:const volatile 典型是"只读的硬件状态寄存器"。

    const:语义是"初始化后不再修改",编译器可据此优化与静态检查赋值错误(const T * 承诺不修改指向对象)。 volatile:语义是"每次访问必须从内存读写",编译器不得把访问缓存/合并/删除——用于硬件寄存器、 信号/中断与主流程共享的变量。两者正交:const volatile 表示"这个对象外部会变(volatile), 但程序不许写它(const)",如只读状态寄存器。误区:volatile 不是多线程同步手段(不保证原子性/内存序)。

    追问:
    • 追问:volatile 能防止多线程数据竞争吗?(不能,需要锁/原子操作)
    评分要点:
    • const 语义与优化
    • volatile 语义与场景
    • const volatile 组合
    • volatile 误区
  • int 和 unsigned int 一起比较,为什么结果常常"反直觉"?高频C/C++ · medium

    要点:常规算术转换会把 int 提升为 unsigned int:-1 变成 4294967295,与 10 比较为假。

    混合算术/比较时按"常规算术转换"把低等级转为高等级:有符号 int 与无符号 unsigned int 同宽时, 有符号操作数被转换为无符号——int 的 -1 变为 4294967295(模 2³²),于是 i < u 为假。 这是 C 语言设计的历史包袱,编写时要显式处理:比较前统一类型,或避免有符号/无符号混用; 编译器 -Wall -Wextra(-Wsign-compare)会给出警告。

    追问:
    • 追问:怎么消除这类隐患?(统一类型/显式转换/用 size_t 与带符号循环变量时小心)
    评分要点:
    • 常规算术转换规则
    • 具体例子 -1 vs 10
    • 规避方法

⑫ 延伸阅读

  • 《C 程序设计语言》(K&R,第 2 章:类型、运算符与表达式——只引书名);
  • C11 标准草案 N1570 §6.2.5(类型)、§6.7.3(限定符)——标准条文为权威口径;
  • 本地手册:man limits.h 或查看编译器的 limits.h 头文件(路径用 gcc -E -P 展开查看);
  • 下一章预告:1.2.3 运算符与优先级——把类型"算"起来,并避开求值顺序陷阱。

迁移训练(migration training)

把本章技能迁移到 Linux(x86_64)嵌入式(ARM 32 位)

本机(MinGW x86_64)Linux x86_64嵌入式 ARM 32 位
sizeof(long)4(LLP64)8(LP64)4
sizeof(void*)884
sizeof(long double)16168 或 16(实现不同)
char 字节数111(恒为 1)
边界值来源limits.hlimits.hlimits.h(交叉编译器自带)

不变的:类型分类、signed/unsigned 语义、const/volatile/restrict 语义、sizeof(char)=1、limits.h 用法;要改的:具体字节数与边界值——所以"量"(sizeof)和"查"(limits.h),不"背"。

内容来源映射

内容部分资料位置标记说明
基本类型分类与大小范围、signed/unsigned、变量声明与初始化、作用域与生命周期第一阶段讲义13.x/14.x 节来源正文在原资料基础上重新组织表述,未大段复制原文
sizeof(void) 为 1 的表述第一阶段讲义13.x 节(PAGE 189)纠错见勘误 err-7.3-9:GCC 扩展,C 标准不允许对 void 求 sizeof
浮点常量默认类型表述第一阶段讲义14.x 节(PAGE 204)纠错见勘误 err-7.3-10:无后缀浮点常量默认 double
volatile 用于多任务共享变量的表述讲义16PAGE 27纠错见勘误 err-7.1-30:volatile 不保证原子性与内存序
全部示例代码、sizeof/limits.h 实验、通俗解释(箱子/标签类比)、练习/测验/任务/面试题补充原资料示例多为截图无法还原;本章示例全部重写并本机实测(gcc 15.2.0 / MinGW-w64 x86_64,约 10% 新编)

本章勘误与更新记录(【纠错】/【更新】)

  • err-7.3-9 · 表述修正 · 出处 PAGE 189

    原文:资料 sizeof 表把 sizeof(void) 直接写为 1

    正确:sizeof(void) 为 1 是 GCC 扩展:C 标准不允许对 void 类型求 sizeof(void 是"不完整类型"),标准写法用 sizeof(char)(恒为 1)或 sizeof(表达式)

    原因:第一步报告 7.3 第 9 条:把 GCC 扩展当成语言规则会误导读者

  • err-7.3-10 · 表述修正 · 出处 PAGE 204

    原文:资料对浮点常量"默认类型"表述自相矛盾(一处说无默认类型、一处说 double)

    正确:无后缀浮点常量默认是 double:3.14 是 double,3.14f 才是 float;这是明确的语言规则,不存在"无默认类型"

    原因:第一步报告 7.3 第 10 条:资料自相矛盾,需按标准统一表述

  • err-7.1-30 · 技术错误 · 出处 PAGE 27

    原文:资料把 volatile 列为多任务共享变量的解决方案

    正确:volatile 只保证每次访问都从内存读写(编译器不优化掉该访问),不保证原子性也不提供内存序;多任务共享变量需要互斥锁或原子操作(1.3.11 展开)

    原因:第一步报告 7.1 第 30 条:volatile 用于多线程同步是常见严重误区