2

简介: C++ 中的全局静态对象在启动前被初始化main()。考虑:

#include <stdio.h>
int calc_it() {
   return 1;
}
int glob = calc_it();
int main() {
   printf("glob = %d\n", glob);
   return 0;
}

输出是glob = 1, 因为calc_it()和分配是在main()开始之前执行的。代码的顺序与它无关。

现在想象一下,您有多个带有类似代码的源文件,并进一步想象它们以某种方式相互依赖(无论出于何种原因,您都想要某种执行顺序。我们不要讨论这是好的还是坏的设计。)

执行的顺序不是由标准定义的,但是在 Visual C++ 中有一些方法可以对它们施加一定的顺序。对于全局静态对象,可以#pragma init_seg(SECTIONNAME)在对象定义的前面使用来指定某个节名

但最后这只会导致编译器将(__cdecl *)(void)指向函数的指针放在某些链接器部分(它们都以 开头.CRT$XC)。在链接器确定内存布局之前,节名称按字典顺序排列。默认部分名称是.CRT$XCU. 然后,C/C++ 初始化代码将这些段的内容.CRT$XCA视为.CRT$XCZ函数的指针,并一一调用。

我们也可以使用#pragma data_seg(SECTIONNAME)指令手动执行此操作。所以这:

#include <stdio.h>
void hi_there() {
   printf("hi there!\n");
}
int main() {
   printf("bye!\n");
   return 0;
}
#pragma data_seg(".CRT$XCM")
typedef void (__cdecl *atexit_func)(void);
atexit_func _init_ptr[] = { hi_there };

将输出:

hi there!
bye!

那有多好?:)

问题描述:/GL AFAIK,自 Visual C++ 2015 起,如果您将选项(整个程序优化)与/OPT:REF链接器选项(删除未使用的函数和数据)一起 使用,这将不再有效。原因可能是从链接器的角度来看,_init_ptr从未使用过。在较旧的 Visual Studio 版本中,这仍然有效,因为它们从不删除未使用的数据,只删除使用的代码

问题:如何仅针对单个符号避免这种情况?

4

1 回答 1

1

Visual Studio 链接器可以选择包含某个符号,无论它是否被引用:/INCLUDE:symbol. Visual C++ 提供了一种将此链接器选项添加到已编译目标文件的方法:#pragma comment(linker, "/include:symbol").

即使使用cl /O2 /GL x.cpp /link /OPT:REF(即,对于 x86,32 位)编译,以下代码也将运行:

#include <stdio.h>
void hi_there() {
   printf("hi there!\n");
}
int main() {
   printf("bye!\n");
   return 0;
}
#pragma data_seg(".CRT$XCM")
typedef void (__cdecl *atexit_func)(void);
extern "C" atexit_func _init_ptr[] = { hi_there };
#pragma comment(linker, "/include:__init_ptr")

注意 the/include:和 the之后的前导额外下划线,extern "C"以防止名称混淆。

更新:为了使代码也可以为 x64 编译,我们需要添加一些额外的内容:

#include <stdio.h>
void hi_there() {
   printf("hi there!\n");
}
int main() {
   printf("bye!\n");
   return 0;
}

#ifdef _M_X64
#define INCLUDE_SYM(s) comment(linker, "/include:" #s)
#else
#define INCLUDE_SYM(s) comment(linker, "/include:_" #s)
#endif

#pragma data_seg(".CRT$XCM")
#pragma section(".CRT$XCM", read)
typedef void (__cdecl *atexit_func)(void);
extern "C" atexit_func _init_ptr[] = { hi_there };
#pragma INCLUDE_SYM(_init_ptr)
于 2016-04-25T13:20:29.207 回答