当定义不是const时,将extern全局视为const是否有效?

时间:2014-12-29 12:39:28

标签: c standards standards-compliance c89

假设我有一个编译单元 file1.c ,它声明了一个文件范围变量,如下所示:

int my_variable = 12;

然后,在另一个编译单元 file2.c 中,我为该变量创建了一个extern声明,但声明为const

extern const int my_variable;

这将使用-Wall -Wextra -ansi -pedantic编译并使用gcc正常工作。但是,C89标准说要兼容两种合格类型,两者都应具有相同类型的兼容类型。在声明中添加const会增加限制而不是避免限制。这是安全有效的C吗?使用头文件设置它的最佳做法是什么?

2 个答案:

答案 0 :(得分:5)

由于声明不匹配,因此明确未定义。如您所述,const intint并非兼容类型。只有当它们出现在同一范围内时才需要诊断。

在实践中也不安全,请考虑

$ 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>抱怨。通过将它们放在单独的转换单元中,编译器永远不会看到两者,因此无法执行检查。