FAQ 1: C 语言的常量类型¶
在 C 语言中,有下面三种看起来是“常数”的东西:
- 字面常数 (例如,
1958、0x2025等) - 一个宏定义 (例如,
#define YEAR 2025之后的YEAR) - 一个
const修饰的量 (例如,const int year = 2025;之后的year)
这三种“常数”的差别在哪里?差别在,前两个是真的“常数”,而最后一个不是。
显然,字面常数就是常数。这样的常数可以放在任何一个需要数值的地方,并且自动具有合适的类型。如果数值太大,超过了标准类型能容纳的最大数值 (例如 unsigned long long),那么会报错。如果试图用超过可容纳范围的数值给变量赋值 (例如用 11111111111 给 int 变量赋值),即使数值本身合法,也会报错。
宏定义在编译之前的预处理部分就会被替换成对应的内容,所以如果写 #define YEAR 2025 之后使用 YEAR,那么和直接使用 2025 是一样的,也遵循上面一条关于字面常数的说法。
const 修饰的对象不是常数。尽管给这样的对象赋予新值在语言层面是不合法的,这样的对象仍然存在可变内存中,是可能通过间接方式被修改的。因此,这样的对象即使在语言层面是“不能修改的”、“只读的”,但并不是意味着它是“常数”,它的值也不是编译器能决定的,因此不能用于数组定义时的长度这样的需要编译器能决定的常数的地方。
例如,下面的代码演示了这一点。
#include <stdio.h>
#define const1 2025
const int const3 = 2025;
int main(void)
{
// 修改 const1 是不合法的,因为这相当于 2025 = 1958;
// error: lvalue required as left operand of assignment
const1 = 1958;
// 定义一个 const 修饰的变量。
const int const2 = 2025;
// 给局部的变量 const2 (const 修饰的) 赋值是不合法的,这是因为编译器看到了它“const”的修饰。
// error: assignment of read-only variable 'const2'
const2 = 1958;
// 但是,我们可以间接地修改 const2。
*(int*)(&const2) = 2025; // 可以的
printf("const2 = %d\n", const2);
*(int*)(&const3) = 1234; // 可以通过编译,但运行时可能报告段错误,因为全局的 const 变量可能存在内存中的只读数据区域,试图写入将引发异常。
printf("const3 = %d\n", const2);
return 0;
}
另一个例子是这样的:
void foo(void)
{
const int MAXN = 100;
int array[MAXN];
}
这在 GCC 中居然是可以编译的。看起来这和上面我们说的 const int 不一样。这是因为,GCC 有 C 标准提供的可变长数组功能,因此在局部变量中可以使用变量来指定数组的大小。于是,这个例子就可以工作了。但如果把这段定义换到全局变量就不行了,这是因为全局变量一般放在 .data 段,和别的一般的变量一样,需要有确定的大小。