代码之家  ›  专栏  ›  技术社区  ›  Ayrosa

下面的代码中是否有编译器标志可以用来报告有关ub的警告?

  •  1
  • Ayrosa  · 技术社区  · 7 年前

    我不太熟悉gcc编译器,我正在尝试运行一个使用两个源文件的代码 wandbox 。让我们从第一个代码开始,使用主文件(默认)和第二个名为 other.cc ,如下:

    主文件

    #include <iostream>
    struct A {
        int i = 1;
        static const int k = 1;
    };
    extern A a;    // The object a is defined in other.cc
    
    int main() {
        std::cout << a.i << '\n';
        std::cout << a.k << '\n';
    }
    

    其他.cc

    struct A {
        int i = 1;
        static const int k = 1;
    };
    A a{2};
    

    请注意,我必须插入文件名 其他.cc 在盒子里 编译器选项 ,位于WandBox的左侧,以便将此文件编译并链接到最终对象文件中。运行这段代码,我会得到下面打印的数字2和1,它们是正确的。

    2
    1
    

    现在,如果我排除文件 其他.cc 从编译和链接进程中,通过从 编译选项 Box,我得到一个链接错误,关于对非静态数据成员的引用 A::i 在表达式中使用

    std::cout << a.i << '\n';
    

    如果我从代码中删除这个语句,它 显然地 正常运行,因为编译器替换变量 a.k 按剩余语句中的常量值1 std::cout << a.k << '\n'; 在main()中,打印1。但这被认为是未定义的行为,根据 [basic.def.odr]/10 [intro.compliance]/2 (2.3) . 注意[basic.def.odr]/10中提到的“无需诊断”。

    在这种情况下,我试图通过使用一些阻止优化的标志来强制编译器发出错误。我已经试过了 -fkeep-static-consts -fno-keep-static-consts ,无济于事。是否还有其他标记可以用来避免这种未定义的行为?

    这个例子是从 this discussion 在C++中进行STD讨论。

    我知道通过定义对象 a 在第一个文件中可以解决这个问题。但这不是我用这个奇怪的例子要找的。我只是想更好地了解编译器在这些异常情况下是如何工作的。

    2 回复  |  直到 7 年前
        1
  •  4
  •   JaMiT    7 年前

    我认为另一个问题的答案会更有启发性。对未定义行为的恐惧在于使用不同编译器时无法保证相同的运行时结果。所以:

    我不需要一条错误消息来确保运行时行为不依赖于所使用的编译器吗? 不,有两个案例需要考虑。

    第一种情况: 编译器将替换 a.k 有价值的 1 . 运行时行为是预期的结果。

    第二种情况: 编译器不替换 AK 使用一个固定值,而不是生成一些引用 a 变量。(这是您试图用编译器选项触发的操作。)在这种情况下,链接器将看到引用,并在任何翻译单元中都找不到该变量时发出错误。您不能前进到运行时行为,因此您不会得到与第一种情况不一致的运行时结果。

    因此,理论上,在切换编译器时可能会有一些困难,但不是典型的未定义行为的模糊错误。

    如果有些人没有发现这个答案有启发性,我现在回到原来的问题,重新措辞了一点。

    当发生这种违反一个定义规则的情况时,是否有任何标志可用于强制诊断消息(错误或警告)? 取决于编译器,但可能不是。请注意,[basic.def.odr]/10提到“不需要诊断”,因此当发生这种特定的冲突时,不需要编译器提供诊断消息。为什么不?可能是因为上述原因:消息将在稍后的阶段触发(即链接),或者预期的行为将发光。在这种情况下,通过要求错误/警告来增加编译器的簿记负担是没有好处的。

        2
  •  1
  •   John Perry    7 年前

    编译器可能没有使它失败的选项,因为编译器只检查语言,而语言允许这样做。

    这个 链接器 将有一个使其失败的选项,您可以通过大多数编译器将选项传递给链接器,包括 g++ . 我可以通过以下方式使GCC失败:

    g++ -std=c++11 -Xlinker --require-defined=a main.cpp
    

    这就产生了错误

    /usr/bin/ld: required symbol `a' not defined
    collect2: error: ld returned 1 exit status
    

    你没有要求,但是 clang++ 有点简单:

    clang++ -std=c++11 -u a main.cpp 
    

    更详细的是:

    Undefined symbols for architecture x86_64:
      "a", referenced from:
         -u command line option
         (maybe you meant: __ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_m, _main , __ZNSt3__111char_traitsIcE3eofEv , __ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_ , __ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_c , ___clang_call_terminate , __ZNSt3__111char_traitsIcE11eq_int_typeEii )
    ld: symbol(s) not found for architecture x86_64
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    

    试一试。