代码之家  ›  专栏  ›  技术社区  ›  Max Senft

未初始化的C字符串没有警告

  •  24
  • Max Senft  · 技术社区  · 7 年前

    我现在想知道为什么在编译/链接一个小的C程序的过程中没有从GCC得到错误。

    我宣布了 version.h 以下字符串:

    const char* const VERSION;
    

    version.c 我已经设置了变量的初始化:

    const char* const VERSION = "0.8 rev 213";
    

    没问题。我可以在程序的其余部分使用字符串。

    如果C文件丢失,编译/链接过程中不会发生错误,但是当程序试图访问变量时,它会使用sigsegv(当然)失败。

    是我设置变量的方法吗? VERSION 正确还是有更好的方法?还是有机会在编译/链接期间出错?

    3 回复  |  直到 7 年前
        1
  •  31
  •   Jonathan Leffler    7 年前

    你有 定义 (不只是声明)头中的变量。

    如果包含来自多个源文件的此头文件,则行为为 未定义 . 以下是标准的相关引用:

    J.2 Undefined behavior

    艾斯

    使用具有外部链接的标识符,但在程序中不存在该标识符的唯一外部定义,或者不使用该标识符,并且该标识符存在多个外部定义。

    艾斯

    您依赖于gcc特有的(实际上对许多编译器都是通用的,但仍然是非标准的)行为,它合并了重复的 实验性的 数据定义。请参阅帮助 -fcommon -fno-common GCC编译标志。并不是所有的编译器都是这样。从历史上看,这是链接器的常见行为,因为Fortran在C之前就是这样工作的。

    假设使用此语言扩展,其中一个定义(具有显式初始值设定项的定义)初始化变量以指向字符串文字。但是如果省略这个定义,它将保持零初始化。也就是说,它将是一个常量空指针。不是很有用。

    长话短说,永远不要这样做。要在头中声明(但不定义)全局变量,请使用 extern . 如果你这样做,并且试图省略其他地方的定义,你会 可能的 获取一个链接器错误(尽管标准不需要对此冲突进行诊断,但所有已知的实现都会产生一个)。

        2
  •  14
  •   Petr Skocik    7 年前

    您的示例之所以工作是因为调用了C(而不是C++)的Fortran灵感(MIS)特性。 暂定定义 ( 6.9.2p2 )它通常但不标准地扩展到多个文件。

    暂定定义是变量声明,没有 extern 和 没有初始值设定项。在常见的实现中(pun的意图),临时定义创建了一种特殊的符号,称为 common 符号。在链接过程中,如果存在同名的常规符号,其他常用符号将成为对常规符号的引用,这意味着所有空符号 VERSION 在您的翻译单元中,由于包含而创建的s将成为对常规符号的引用。 const char* const VERSION = "0.8 rev 213"; . 如果没有这样的规则符号,普通符号将被合并为一个零初始化变量。

    我不知道如何使用C编译器来获得针对这一点的警告。

    这个

    const char* const VERSION;
    const char* const VERSION = "0.8 rev 213";
    

    不管我怎么尝试,似乎都能和GCC合作。( g++ 不接受它——C++没有临时定义特性,它不喜欢未显式初始化的const变量。但是你可以用 -fno-common (这是一个相当“常见”(强烈推荐)的非标准选项(gcc、clang和tcc都有),如果非初始化和初始化的无外部声明在不同的翻译单元中,那么您将得到一个链接器错误。

    例子:

    V.C:

    const char * VERSION;
    

    主C

    const char* VERSION;
    int main(){}
    

    编译和链接:

    gcc main.c v.c #no error because of tentative definitions
    g++ main.c v.c #linker error because C++ doesn't have tentative definitions
    gcc main.c v.c -fno-common #linker error because tentative defs. are disabled
    

    (我去掉了第二个 const 在例子中,为了C++的例子,C++另外使const全局静态或类似于只是使临时定义特征的演示复杂化。

    使用禁用的或带有C++的临时定义,头中的所有变量声明都应该具有 外部的 在他们里面

    版本H:

    extern const char* const VERSION;
    

    对于每个全局,您应该只有一个定义,并且该定义应该有一个初始值设定项或不带初始值设定项 外部的 (如果应用,某些编译器将发出警告 外部的 初始化变量)。

    C版:

    #include "version.h" //optional; for type checking
    const char* const VERSION = "0.8 rev 213";
    
        3
  •  6
  •   Achal    7 年前

    version.h 你应该申报 VERSION 作为一个 extern 喜欢

    extern const char* const VERSION;
    

    而在 version.c 您应该像这样定义外部变量

    const char* const VERSION = "0.8 rev 213";
    

    编辑: 此外,还需要确保只有一个源文件定义了变量 版本 当其他源文件声明它时 外部的 你可以通过在源文件中定义它来实现。 VERSION.c 然后把 外部的 头文件中的声明 VERSION.h .

    推荐文章