【问题标题】:How do I prevent my 'unused' global variables being compiled out?如何防止我的“未使用”全局变量被编译出来?
【发布时间】:2009-08-04 19:26:49
【问题描述】:

我正在使用静态初始化来简化在 C++ 中向工厂注册某些类的过程。不幸的是,我认为编译器正在优化“未使用”的对象,这些对象旨在在其构造函数中完成有用的工作。有没有办法告诉编译器不要优化全局变量?

class SomeClass {
    public:
        SomeClass() {
            /* do something useful */
        }
};

SomeClass instance;

我在 SomeClass 的构造函数中的断点没有被命中。在我的实际代码中,SomeClass 在头文件中,而实例在源文件中,或多或少是单独的。

编辑:正如 KJAWolf 所猜测的,这段代码实际上被编译成一个静态库,而不是可执行文件。其目的是将静态库也提供的某些类型注册到类型及其创建者的静态列表中,以便工厂在构造时从中读取。由于这些类型是随 lib 提供的,因此不希望将此代码添加到可执行文件中。

我还发现,通过将代码移动到另一个包含其他现有代码的源文件,它可以正常工作。似乎有一个完全由这些全局对象组成的文件是导致问题的原因。就好像那个翻译单元被完全忽略了一样。

【问题讨论】:

  • 你的源文件是可执行文件的一部分,还是静态库的一部分?
  • 啊哈,很好的占卜工作。实际上源文件是静态库的一部分。这会导致问题吗?我有 80% 的把握我以前做过完全相同的事情,而且效果很好。
  • 我遇到了同样的问题:stackoverflow.com/questions/599035/…
  • 为什么要使用全局变量?
  • 正如我所说,在静态初始化时执行一些代码,向工厂注册一个类。我有意识地决定将它们用于此目的,以防这会导致讲座:)我遇到麻烦的原因是我是一个优秀的程序员,所以我从来没有真正使用过它们,也不明白为什么他们没有像我预期的那样工作!

标签: c++ global-variables


【解决方案1】:

不允许编译器优化全局对象。
即使它们从未使用过。

您的代码中发生了其他事情。
现在,如果您使用全局对象构建了一个静态库,并且该全局对象未从可执行文件中引用,则链接器不会将其拉入可执行文件中。

【讨论】:

  • 我对此表示赞同,因为我认为这是解决真正问题的唯一答案;正如提问者所说“似乎有一个完全由这些全局对象组成的文件是导致问题的原因。就好像那个翻译单元被完全忽略了”。翻译单元被忽略,因为它在库中,因此被链接器省略。如果一个对象在一个库中并且没有被应用程序中的任何东西调用,它就会被省略,这就是库的全部思想!
  • 同上,一针见血。我也赞成 Pavel 的回答,因为他也做对了 :)
  • @Sam:如何解决它取决于你在做什么。仅将全局对象用于构造/销毁的副作用并不是一个好主意。
  • 这里似乎有一个很好的解决方案:stackoverflow.com/questions/599035/…
  • 我有同样的经历,而且很糟糕 (social.msdn.microsoft.com/Forums/vstudio/en-US/…)。未引用全局变量,但它们的构造函数具有可观察到的副作用(即在工厂注册)。我最终对这些全局对象进行了人为引用以防止删除。只有当链接器可以证明全局变量没有做任何事情时,它才可能把它们扔掉。
【解决方案2】:

编译器永远不应该优化掉这样的全局变量——如果这样做,它就会被破坏。

【讨论】:

  • 如果编译器可以检测到没有使用全局,它可以省略它们。 GCC(在此上下文中为 G++)可以并且确实省略了一些变量 - 例如,旧的“在目标文件中嵌入版本”技巧不适用于现代 GCC - 文件静态值被检测为未使用并在对象之外进行了优化文件。
  • 如果他们有一个 cnstructor,那么使用该构造函数会产生潜在的副作用。由于 gcc 不进行跨功能分析,它永远无法检测到潜在的副作用,因此不允许将其删除。
  • 马丁说了什么。如果构造函数具有可观察到的副作用,则无论它是否为静态,都无法将其删除。另外,我不确定我是否理解您的评论,Neil - IIRC,C 规范没有以任何方式定义“全局”,因此它的定义是开放的,并且“全局是任何不是本地或成员的东西”是一个相当合理的一个(虽然不是唯一的,显然)。
  • @Pavel 类似于 static int x;显然不能是“全局的”,并且绝对可以被编译器忽略。我的观点是,可能是全局的(即由其他编译单元链接到的)不能被编译器、构造函数或否省略,因为编译器不知道它们是否会被链接到。
  • 就我而言,它仍然是一个全局的(编译单元中的所有函数都可以看到它,那么它是什么?),但是争论术语定义是没有意义的。然而,即使对于“静态全局”,如果它的构造函数有可观察的副作用,编译器也不能合法地忽略它。
【解决方案3】:

您可以通过在构建完整的静态库之前部分链接它们来强制一个对象(您的类型列表)与它一起拉动一些其他对象。

GNU linker:

ld -Ur -o TypeBundle.o type1.o type2.o type3.o static_list.o
ld -static -o MyStaticLib.a type_bundle.o other_object.o another_object.o ...

因此,每当使用库的代码引用静态列表时,完整的“TypeBundle.o”对象将链接到生成的二进制文件中,包括 type1.o、type2.o 和 type3.o。

同时,请检查the manual中“-Ur”的含义。

【讨论】:

    【解决方案4】:

    在 Arthur Ulfeldt 的基础上构建,volatile 告诉编译器这个变量可以在编译器不知道的情况下发生变化。我用它来放置一个语句以允许调试器设置断点。它对于可以根据环境更改或需要特殊序列的硬件寄存器也很有用。即串口接收寄存器和某些看门狗寄存器。

    【讨论】:

    • 不,它只是告诉编译器它不能缓存以前读取的变量值。
    【解决方案5】:

    你可以使用

    #pragma 优化关闭 全局变量 #pragma 优化

    但我不知道这是否仅适用于 Visual Studio (http://msdn.microsoft.com/en-us/library/chh3fb0k(VS.80).aspx)。

    您也可以告诉编译器根本不进行优化,尤其是在调试时...

    【讨论】:

    • 这与每个 IDE/编译器都不兼容。
    • 但我希望是这样!
    • 在 Visual Studio 上,语法是 #pragma optimize("", off) 但对我来说,它不会阻止在静态库中删除全局变量。
    【解决方案6】:

    你在使用 gcc 和 gdb 吗?过去有一个问题是gdb不能准确的在构造函数中设置断点。

    另外,您是否使用了允许编译器在类定义中内联方法的优化级别。

    【讨论】:

      【解决方案7】:

      链接时需要使用-whole-archive。在这里查看答案:

      ld linker question: the --whole-archive option

      【讨论】:

        【解决方案8】:

        我在 VS2008 上有相同的设置和问题。 我发现如果你用dllexport 声明你的类,它不会优化。

        class __declspec( dllexport ) Cxxx
        {
        .
        }
        

        但是,在我的情况下,这会产生很多警告,因为我必须将此类中使用的所有类也声明为 dllexport。

        所有优化都已关闭(在调试模式下),但仍处于优化状态。还 volatile/pragma 优化关闭。在这个类创建的全局变量上(在同一个 cpp 文件中)等不起作用。

        刚刚发现dllexport确实需要至少将这些类的头文件包含在exe的其他一些cpp文件中才能工作!所以唯一的选择是为每个类添加一个调用一些静态成员的文件,并将这个文件添加到所有使用这些类的项目中。

        【讨论】:

          【解决方案9】:

          使用关键字 volatile 怎么样?会防止编译器过度优化。

          【讨论】:

          • 有时候,这有点用处。
          猜你喜欢
          • 1970-01-01
          • 2014-08-29
          • 2013-03-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-02-04
          • 1970-01-01
          相关资源
          最近更新 更多