【问题标题】:External linkage of const in CC中const的外部链接
【发布时间】:2017-03-12 08:40:46
【问题描述】:

当我遇到这种奇怪的行为时,我正在使用 extern 关键字 in C。 我有两个文件:

file1.c

#include<stdio.h>
int main()
{
    extern int a;
    a=10;
    printf("%d",a);
    return 0;
}

file2.c

const int a=100;

当我将这些文件一起编译时,没有错误或警告,当我运行它们时,输出为10。我原以为编译器应该在a=10; 行报告错误。

此外,如果我将 file2.c 的内容更改为

const int a;

也就是说,如果我删除全局常量变量a的初始化,然后编译文件,仍然没有错误或警告,但是当我运行它们时,就会发生Segmentation Fault。

为什么会出现这种现象?它是否被归类为未定义的行为?这是编译器还是机器相关的?

PS:我见过很多与此相关的问题,但要么是针对 C++ 的,要么只讨论 extern

【问题讨论】:

标签: c constants extern linkage


【解决方案1】:

您的程序会导致未定义的行为,无需诊断(无论const int a 是否具有初始化程序)。 C11中的相关文本是6.2.7/2:

引用同一对象或函数的所有声明都应具有兼容的类型;否则,行为未定义。

还有 6.2.2/2:

在构成整个程序的一组翻译单元和库中,每个带有外部链接的特定标识符的声明都表示相同的对象或函数。

在 C 中,const int a = 100; 表示a 具有外部链接。所以它表示与extern int a; 相同的对象。但是这两个声明的类型不兼容(intconst int 不兼容,“兼容类型”的定义见 6.7.2)。

【讨论】:

    【解决方案2】:

    编译和链接是两个不同的阶段。在编译期间,单个文件被编译成目标文件。编译器会发现 file1.c 和 file2.c 在内部是一致的。在链接阶段,链接器只会将所有出现的变量a 指向相同的内存位置。这就是您看不到任何编译或链接器错误的原因。

    为了完全避免您提到的问题,建议将 extern 放在头文件中,然后将该头文件包含在不同的 C 文件中。这样编译器可以捕捉到头文件和 C 文件之间的任何不一致

    以下 stackoverflow 还谈到了链接器无法对外部变量进行类型检查。

    Is there any type checking in C or C++ linkers?

    同样,全局变量的类型(和类的静态成员等)不会被链接器检查,所以如果你声明 extern int test;在一个翻译单元中定义浮动测试;在另一种情况下,你会得到不好的结果。

    【讨论】:

      【解决方案3】:

      这是 C 编译器的已知行为。这是强制执行强编译时类型检查的 C 和 C++ 之间的差异之一。 尝试将值分配给 const 时会发生分段错误,因为链接器将 const 值放在只读 elf 段中,并且写入此内存地址是运行时(分段)错误。 但在编译期间,编译器不检查任何“外部”,C 链接器也不测试类型。因此它通过了编译/链接。

      【讨论】:

      • Yanir,你有一句话'这是 C 和 C++ 之间的区别之一,其中强制执行强编译时类型检查'。你的意思是说 C++ 编译器会发现这个问题吗?由于文件不同,它们可能会被单独编译成目标文件。
      • @JayRajput C++ 编译器不会捕捉到这一点,但链接器会捕捉到。
      • @user4581301,网上的信息好像很混乱。例如:这篇 stackoverflow 文章建议链接器不会对外部变量进行类型检查。 stackoverflow.com/questions/28090854/…。您是否有一些资料证明链接器确实对外部变量进行了类型检查?我想了解更多。
      • @JayRajput 我必须做一些标准阅读才能找到确切的措辞。您链接的问题试图回答 C 和 C++,在类型检查方面,它们是非常非常不同的野兽。请参阅 Yakk 的评论和 Josh Kelly 的回答。
      • @JayRajput 看起来它的工作方式与我想象的有点不同。根据Basic.Linkconst 类型具有内部链接,除非明确声明为extern,否则无法在当前文件之外看到。所以const int x = 0; 不能是externed,但@ 987654326@ 可以是externed。我找不到任何有关丢失的const 是否被链接器捕获的信息。 GCC 4.8 和 6.1 似乎没有陷阱,所以目前我假设它不是必需的。
      【解决方案4】:

      这是未定义的行为,但编译器不会警告您。怎么可能?它不知道你如何在另一个文件中声明一个变量。

      试图修改声明为const 的变量是未定义的行为。变量有可能(但不是必须)存储在只读存储器中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多