常量
常量是在程序运行期间值不能被修改的数据。C 语言提供了多种常量形式:字面常量、const 限定符、枚举常量和宏定义。理解它们的区别和使用场景,能帮助你写出更安全、更清晰的代码。
整型常量
整型常量可以用十进制、八进制或十六进制表示:
int dec = 42; /* 十进制 */
int oct = 052; /* 八进制:以 0 开头,等于十进制 42 */
int hex = 0x2A; /* 十六进制:以 0x 开头,等于十进制 42 */
int bin = 0b101010; /* 二进制:C23 特性,部分编译器扩展支持 */
printf("%d %d %d\n", dec, oct, hex); /* 输出:42 42 42 */
printf("%o %x\n", dec, dec); /* 输出:52 2a */
整型常量的类型由值的大小决定:
42 /* int */
3000000000 /* 超出 int 范围时,是 unsigned int 或 long */
42U /* unsigned int */
42L /* long */
42UL /* unsigned long */
42LL /* long long(C99) */
42ULL /* unsigned long long */
没有后缀的小整数默认是 int。如果值超出 int 范围,编译器依次尝试更大的类型。
浮点常量
浮点常量默认是 double 类型:
3.14 /* double */
3.14f /* float */
3.14F /* float */
3.14L /* long double */
3.14e10 /* 科学计数法:3.14 × 10¹⁰ */
3.14E-5 /* 3.14 × 10⁻⁵ */
/* C99 十六进制浮点 */
0x1.0p0 /* 1.0 × 2⁰ = 1.0 */
0x1.8p1 /* 1.5 × 2¹ = 3.0 */
0x1.fffffep+127f /* float 最大值 */
十六进制浮点常量(C99 新增)用于精确表示 IEEE 754 浮点数的每一位,避免十进制转换的舍入误差。
字符常量
字符常量用单引号括起来,类型是 int(C 语言中字符常量实际上是 int 类型,这是历史遗留):
char c = 'A'; /* 字符常量 */
char newline = '\n'; /* 转义字符 */
char hex = '\x41'; /* 十六进制转义:'A' */
char oct = '\101'; /* 八进制转义:'A' */
printf("%d\n", 'A'); /* 输出 65(int 类型) */
printf("%d\n", sizeof('A')); /* 输出 sizeof(int),不是 1! */
在 C 中,sizeof('A') 等于 sizeof(int),这与 C++ 不同(C++ 中字符常量是 char 类型)。
字符串字面量
字符串字面量用双引号括起来,是字符数组,末尾自动添加 \0:
char *s = "Hello"; /* s 指向字符串常量 */
printf("%zu\n", sizeof("Hello")); /* 6:5 个字符 + '\0' */
/* 字符串字面量拼接 */
char *long_str = "Hello, " "World!"; /* 等价于 "Hello, World!" */
字符串字面量通常存储在只读数据段,修改它是未定义行为:
char *s = "Hello";
s[0] = 'h'; /* 未定义行为!可能崩溃 */
/* 正确做法:复制到可修改的数组 */
char buf[] = "Hello";
buf[0] = 'h'; /* 合法 */
const 限定符
const 修饰的变量是只读的,编译器会阻止直接修改:
const int max_size = 100;
max_size = 200; /* 编译错误:不能修改 const 变量 */
/* const 指针 */
const int *ptr; /* 指向常量的指针:不能通过 ptr 修改值 */
int *const ptr2; /* 常量指针:ptr2 本身不能指向别处 */
const int *const ptr3; /* 两者都是常量 */
const 变量仍然是变量(占用内存),只是编译器限制了修改。与常量表达式不同,const int 变量不能用于数组大小声明(C89 要求编译期常量,C99 VLA 除外):
const int n = 10;
int arr[n]; /* C89 错误;C99 允许(作为 VLA) */
#define N 10 /* 宏常量:编译期常量 */
int arr2[N]; /* 始终合法 */
枚举常量
枚举(enum)定义一组命名的整数常量:
enum Color { RED, GREEN, BLUE };
/* RED = 0, GREEN = 1, BLUE = 2 */
enum Color c = RED;
enum Status { OK = 0, ERROR = -1, PENDING = 1 };
/* 显式指定值 */
enum Priority { LOW = 1, MEDIUM = 5, HIGH = 10 };
枚举常量是编译期常量,可以用于 switch 的 case 标签、数组大小等需要常量表达式的地方。枚举提高了代码可读性,替代了魔法数字。
宏定义常量
#define 定义的宏在预处理阶段被文本替换:
#define PI 3.14159
#define MAX_BUFFER 1024
#define DEBUG 1
/* 使用 */
double circumference = 2 * PI * radius;
char buffer[MAX_BUFFER]; /* 编译期常量 */
宏常量没有类型检查,只是简单的文本替换。const 变量有类型,更安全,但某些场景(如数组大小)必须使用宏或枚举。
常量表达式
C99 区分三种常量表达式(§6.6):
整数常量表达式:仅含整数常量、枚举常量、字符常量、sizeof 表达式等,用于数组大小、case 标签等:
int arr[10 + 5]; /* 整数常量表达式 */
switch (x) {
case 1 + 2: /* 整数常量表达式 */
/* ... */
}
算术常量表达式:可含浮点常量,用于静态变量初始化:
static double pi = 3.14 * 2; /* 算术常量表达式 */
地址常量表达式:& 作用于静态对象或函数名,用于静态指针初始化:
static int global;
static int *p = &global; /* 地址常量表达式 */
选择指南
| 场景 | 推荐 |
|---|---|
| 编译期整数常量(数组大小、case 标签) | 枚举或 #define |
| 只读变量(运行期确定值) | const |
| 一组相关的命名常量 | 枚举 |
| 数学/物理常数 | const double 或 #define |
| 条件编译标志 | #define |
#define VERSION_MAJOR 1 /* 版本号,可能用于条件编译 */
#define VERSION_MINOR 0
const double PI = 3.141592653589793; /* 数学常数 */
enum ErrorCode {
ERR_OK = 0,
ERR_IO = 1,
ERR_MEMORY = 2,
ERR_INVALID = 3
};