C/C++程序在main之前執行代碼
來源:《周哥教IT.C語言深學活用》https://ke.qq.com/course/242707#tuin=a71606
我們在學習C/C++語言的時候,通常認為main函數是整個程序執行的開始。實際上,在main函數之前,會有一系列初始化的操作,這樣的操作通常是由鏈接器等完成的。具體說來,程序最早執行的函數其實並不是main,在windows中,是mainCRTStartup,這個函數是鏈接器執行以初始化運行時庫的,此函數又會調用CRTInit函數,該函數會對C全局變量、C內存分配以及C++中的全局類對象和構造函數進行初始化工作。所以想要在main函數之前執行一些自己的代碼,是有可能的。
1. Linux環境下利用gcc的__attribute關鍵字
在Linux環境的C編程中,可以利用__attribute關鍵字定義constructor和destructor,其中前者會在main函數之前執行,後者會在main函數之後執行。
代碼如下:
1 #include <stdio.h> 2 3 __attribute((constructor)) void before_main() 4 { 5 printf("before main!\n"); 6 } 7 8 __attribute((destructor)) voidbefore_main.cafter_main() 9 { 10 printf("after main!\n"); 11 } 12 13 int main(void) 14 { 15 printf("This is main function.\n"); 16 return 0; 17 }
運行結果:
natalie@ubuntu:~/Desktop/zhou_it_c/before_main$ gcc before_main.c -o before_main
natalie@ubuntu:~/Desktop/zhou_it_c/before_main$ ./before_main
This is main function.
after main!
2. Windows環境下利用#pragma預定義
上面我們說過CRTInit函數中會做一些初始化工作,包括C庫、C的初始化函數、C++庫、C++的初始化函數等。C和C++分別有一張表來保存初始化函數指針,每個表又會使用2個指針來明確範圍。在初始化過程中,__CRTInit函數會一次調用這兩個表中的函數,所以如果我們能把要執行的函數放在這兩個表中,那麽就可以達到在main之前執行代碼的目的了。
C初始化函數表的範圍是:[ __xi_a, __xi_a ] C++初始化函數表的範圍是:[ __xc_a, __xc_z]
我們在具體執行的時候,通過定義特殊的段名稱“.CRT$XIU”和“.CRT$XCU”,把要執行的函數放在段中。鏈接器就會形成日下的C初始化函數表:
[__xi_a, ..., before1(xiu), ..., __xi_z]
以及C++初始化函數表:
[__xc_a, ..., before2(xcu), ..., __xc_z]
代碼如下:
#include <stdio.h> int before_main(void) { printf("before main!\n"); return 0; } typedef int func(); #pragma data_seg(".CRT$XIU") static func *before[] = { before_main }; #pragma data_seg() int main(void) { printf("This is main function.\n"); return 0; }before_main.c
3. C++編程中利用定義全局類對象or全局變量
mainCRTStartup會對全局對象a初始化,也就是說a的構造含稅會先於main執行,所以只需要在a的構造函數中定義我們要執行的函數。
另一種方式是定義一個全局變量為函數運行後的結構,那麽該函數就會用於初始化,會先於main執行。
代碼如下:
1 #include <iostream> 2 using namespace std; 3 using std::cout; 4 5 int func() 6 { 7 cout <<"before main: func()" << endl; 8 return 0; 9 } 10 11 class A 12 { 13 public: 14 A() 15 { 16 cout << "A() constructor" << endl; 17 } 18 ~A() 19 { 20 cout << "A() destructor" << endl; 21 } 22 }; 23 24 A a; 25 26 int g_iValue = func(); 27 28 int main(void) 29 { 30 cout << "This is main function." << endl; 31 return 0; 32 }before_main.cpp
運行結果:
A() constructor
before main: func()
This is main function.
A() destructor
C/C++程序在main之前執行代碼