假设我有一个编译单元 file1.c ,它声明了一个文件范围变量,如下所示:
int my_variable = 12;
然后,在另一个编译单元 file2.c 中,我为该变量创建了一个extern声明,但声明为const
:
extern const int my_variable;
这将使用-Wall -Wextra -ansi -pedantic
编译并使用gcc正常工作。但是,C89标准说要兼容两种合格类型,两者都应具有相同类型的兼容类型。在声明中添加const
会增加限制而不是避免限制。这是安全有效的C吗?使用头文件设置它的最佳做法是什么?
答案 0 :(得分:5)
由于声明不匹配,因此明确未定义。如您所述,const int
和int
并非兼容类型。只有当它们出现在同一范围内时才需要诊断。
在实践中也不安全,请考虑
$ cat test1.c
#include <stdio.h>
extern const int n;
void foo(void);
int main(void) {
printf("%d\n", n);
foo();
printf("%d\n", n);
}
$ cat test2.c
int n;
void foo(void) { ++n; }
$ gcc -std=c99 -pedantic test1.c test2.c && ./a.out
0
1
$ gcc -O1 -std=c99 -pedantic test1.c test2.c && ./a.out
0
0
Gcc假设n
在优化时未被foo()
更改,因为它可能认为n
的定义属于兼容类型,因此const
您可能会volatile
获得预期的行为 - n
中符合test1.c
条件,但就C标准而言,这仍未定义。
我能想到的阻止用户意外修改n
的最佳方法是声明指向const
的指针,
int my_real_variable;
const int *const my_variable = &my_real_variable;
或者某些宏
#define my_variable (*(const int *)&my_variable)
使用C99,可以通过复合文字来避免my_real_variable
:
const int *const my_variable_ptr = &(int){ 12 };
在此处抛弃const
是合法的(因为int
对象本身不是const
),但是需要进行强制转换,以防止意外修改。< / p>
答案 1 :(得分:4)
在这种情况下,定义和声明出现在单独的翻译单元中,因此编译器不能执行任何类型或限定符检查。这些符号由链接器解析,在这种情况下,链接器似乎没有强制执行此限定符匹配。
如果定义和声明出现在同一个翻译单元中;例如,如果您将extern
声明放在头文件中并且包含它在 file1.c 中,那么我会想象编译器会< / em>抱怨。通过将它们放在单独的转换单元中,编译器永远不会看到两者,因此无法执行检查。