变量、基本类型与关键字:C 程序如何给内存命名
C 语言里的变量不是一个抽象盒子。 它是某段存储的名字。 类型告诉编译器这段存储占多大、如何解释、能参与哪些运算。 关键字则是语言保留的词,用来表达控制流、类型、存储期和限定规则。 入门阶段如果把变量只理解成“放值的格子”,后面遇到指针、结构体和内存布局就会断层。
变量声明
声明把名字引入程序,并说明它的类型。
int age = 18;
double price = 99.5;
char grade = 'A';
这三行分别声明了三个对象:
age是int。price是double。grade是char。
变量名是源码层面的名字。 运行时真正存在的是对象存储。
声明和初始化不同
声明可以不初始化。 但未初始化的自动变量值不确定,读取它会导致错误。
int x; // 声明,未初始化
int y = 0; // 声明并初始化
初学时建议局部变量总是初始化。 这不是保守风格,而是避免未定义行为的基本习惯。
基本整数类型
C 提供多种整数类型。 它们的大小和范围与平台有关,但有基本顺序约束。
| 类型 | 常见用途 |
|---|---|
char |
字符或小整数 |
short |
较小整数 |
int |
默认整数 |
long |
较大整数 |
long long |
至少 64 位整数 |
不要假设 int 永远 32 位。
需要固定宽度时使用 <stdint.h>。
#include <stdint.h>
int32_t count = 0;
uint64_t total = 0;
固定宽度类型让文件格式、网络协议和跨平台数据更可靠。
有符号和无符号
signed 表示有符号。
unsigned 表示无符号。
signed int a = -1;
unsigned int b = 1u;
无符号类型适合位运算和明确非负的二进制表示。 但它不是“更安全的正整数”。 有符号和无符号混合运算时,可能发生隐式转换。
int x = -1;
unsigned int y = 1;
if (x < y) {
/* 结果可能不符合直觉 */
}
这类代码要避免。 边界和长度计算尤其要审计。
浮点类型
C 常见浮点类型:
| 类型 | 含义 |
|---|---|
float |
单精度 |
double |
双精度 |
long double |
扩展精度,依平台 |
浮点数不是十进制小数的精确模型。 它使用二进制近似表示。
double x = 0.1 + 0.2;
x 不一定精确等于 0.3。
金额、账务和权限阈值不要直接依赖普通浮点相等比较。
字符类型
char 用于存储字符编码单位或小整数。
char c = 'A';
'A' 是字符常量。
字符串使用双引号:
char text[] = "hello";
字符串后面会有一个 '\0' 结束符。
这点对后面的数组和字符串非常重要。
布尔类型
C23 中 bool、true、false 成为语言关键字背景下的重要能力。
在旧 C 标准里通常通过 <stdbool.h> 使用。
#include <stdbool.h>
bool ok = true;
工程中要根据编译标准决定写法。 如果项目仍以 C11/C17 为基线,引入头文件更稳妥。
sizeof 查看对象大小
sizeof 返回类型或对象占用的字节数。
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof age);
sizeof 的结果类型是 size_t。
打印它要用 %zu。
不要用 %d 打印 size_t。
格式不匹配会造成未定义行为风险。
常量和 const
const 表示不能通过当前名字修改对象。
const int max_count = 100;
const 不是“编译期常量”的唯一形式。
它更像只读限定。
指针和 const 组合时尤其要读清楚。
const int* p; // p 指向的 int 不能通过 p 改
int* const q; // q 这个指针本身不能改
从右往左读有助于理解复杂声明。
存储类关键字
C 有一组关键字影响名字的链接和存储期。
| 关键字 | 常见意义 |
|---|---|
auto |
自动存储期,局部变量默认就是它 |
static |
静态存储期或内部链接 |
extern |
声明外部定义 |
register |
历史优化提示,现代意义很弱 |
thread_local |
线程局部存储,C23 背景 |
static 最容易混淆。
在函数内部,它让局部变量跨调用保留。
在文件作用域,它限制名字只在当前源文件可见。
static int counter = 0;
全局 static 是封装手段,不是随便加的修饰。
类型限定关键字
| 关键字 | 作用 |
|---|---|
const |
只读视图 |
volatile |
特殊访问,不用于线程同步 |
restrict |
指针独占访问承诺 |
atomic 相关 |
原子访问,后续并发章节讲 |
volatile 不是并发同步工具。
这点必须从入门阶段记住。
线程共享状态应使用原子或锁。
控制流关键字概览
这些关键字控制程序执行路径:
if else switch case default
for while do
break continue return goto
入门阶段先掌握 if、switch、for、while、break、continue、return。
goto 后面会在 C 错误清理路径中解释,它不是默认控制流工具。
类型构造关键字概览
struct union enum typedef
它们用于构造更复杂的数据模型。
struct 聚合多个字段。
union 让多个字段共享同一段存储。
enum 定义命名整数集合。
typedef 给类型起别名。
这些不是语法点,而是数据布局工具。
标识符命名规则
变量名、函数名和类型名都属于标识符。 基本规则:
- 由字母、数字和下划线组成。
- 不能以数字开头。
- 不能使用关键字。
- 不要使用保留给实现的名字。
以下划线开头,尤其是双下划线或下划线加大写字母的名字,通常保留给实现。 项目代码不要使用。
隐式转换要谨慎
C 会在表达式中执行很多隐式转换。 例如小整数提升、整数和浮点混合、signed 和 unsigned 转换。
int a = 5;
double b = 2.0;
double c = a / b;
这里 a 会转换为 double。
而下面代码不同:
int x = 5;
int y = 2;
double z = x / y;
x / y 先做整数除法,结果是 2,再转换成 double。
这类细节会影响业务计算。
工程风险
基础类型也会制造严重问题:
- 未初始化变量导致不可预测行为。
- 有符号溢出触发未定义行为。
- 无符号回绕破坏边界检查。
- 格式化输出类型不匹配导致栈读取错误。
sizeof用错对象和指针,造成分配大小错误。- 把
volatile当线程同步工具。
这些问题不是高级主题,而是第一阶段就要建立警觉。
小结
C 的变量是给对象存储命名。 类型决定对象如何被解释。 关键字决定声明、控制流、存储期和限定规则。 基础语法看似简单,但每个语法都会落到内存大小、转换规则、链接边界和资源安全上。 从一开始按这个角度学习,后面的指针和编译器行为才不会突然变难。