我遇到this question询问如何在C中的main()之前执行代码,提到有C ++策略。我主要住在应用程序空间,所以在main()之前执行从未发生过。什么样的东西需要这种技术?
答案 0 :(得分:14)
“这种技术需要什么样的东西?”
事实:没有。
然而,由于各种原因,您可能需要在主要之前做很多有用的事情。仅举一个实际例子,假设您有一个构建doohickies的抽象工厂。您可以确保构建工厂实例,将其分配到某个特殊区域,然后将各种具体的doohickies注册到它...是的,您可以这样做。
另一方面,如果您将工厂实现为单例并使用全局值初始化的事实来“欺骗”实现在主要启动之前注册具体的doohickies,您可以以很少的成本获得多个好处(使用的事实单身,这里基本上没有问题,几乎是唯一的一个。)
例如你:
不必维护必须明确调用的所有注册列表。实际上,您甚至可以在私人范围内声明和定义整个类,在任何人看不到的情况下,并且在程序启动时可以使用它。
main()不需要对一堆它不关心的对象做一堆垃圾。
所以,这些都不是必要的。但是,如果您利用在主要开始之前初始化全局变量的事实,则可以减少耦合和维护问题。
编辑:
此处应注意,我已经了解到语言无法保证这一点。 C ++只保证在main之前发生 zero 或常量初始化。我在这个答案中谈到的是动态初始化。这个C ++保证在第一次使用变量之前发生,就像函数本地静态变量一样。
每个编译器似乎都在main之前进行动态初始化。我以为我碰到了一次没有,但我相信问题的根源是别的。
答案 1 :(得分:5)
此技术可用于库初始化例程或初始化将在程序执行期间隐式使用的数据。
GCC提供构造函数和析构函数 function attributes,导致在执行main()
或main()
之前自动调用函数已完成或exit()
已被调用。
void __attribute__ ((constructor)) my_init(void);
void __attribute__ ((destructor)) my_fini(void);
在库初始化的情况下,如果在运行时加载库,或者在加载时加载库,则在dlopen()
启动之前,在main()
返回之前执行构造函数例程。当用于库清理时,如果在运行时加载库,则在dlclose()
返回之前执行析构函数例程,如果在加载时加载库,则在exit()
之后或main()
完成时执行析构函数例程。 p>
答案 2 :(得分:3)
在main之前完成的东西:
main
的顶部,但我看到使用_init
而不是_main
作为入口点的编译器:
.cfi_startproc
.cfi_personality 0x3,__gxx_personality_v0
pushq %rbp
.cfi_def_cfa_offset 16
movq %rsp, %rbp
.cfi_offset 6, -16
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
# My code follows
答案 3 :(得分:3)
在main
涉及全局变量之前,你可能想要做的唯一事情是坏的,并且总是可以通过延迟初始化(在首次使用时初始化)来完成相同的事情。当然,根本不使用全局变量可以更好地完成它们。
一个可能的“异常”是在运行时初始化全局常量表。但这是一种非常糟糕的做法,因为如果在运行时填充它们,则表之间的库/进程实例之间不能共享。编写脚本以在构建时生成static const
表作为C或C ++源文件更加明智。
答案 4 :(得分:2)
在main
启动后需要运行代码以保证代码不变量的任何内容都需要在main
之前运行。像全局iostreams,C运行时库,操作系统绑定等等。
现在,你是否真的需要编写执行此类操作的代码,这是其他人都在回答的内容。
答案 5 :(得分:1)
如果你有一个库,在调用main()之前能够初始化一些数据,创建线程等非常方便,并且知道实现了所需的状态而没有负担并且信任客户端应用程序显式调用一些库初始化和/或关闭代码。从表面上看,这可以通过一个静态对象来实现,该构造函数和析构函数执行必要的操作。不幸的是,不同翻译单元或库中的多个静态对象将具有未定义的初始化顺序,因此如果它们彼此依赖(更糟糕的是,以循环方式),那么在请求到来之前它们可能仍未达到其初始化状态。类似地,一个静态对象可以创建线程并在另一个尚未线程安全的对象中调用服务。因此,面对任意使用时,需要一个具有适当的单例实例和锁的更结构化的方法来保持稳健性,并且整个事情看起来不那么吸引人,尽管在某些情况下它可能仍然是一个净胜利。