- 1. Problems
- 2. Elements of the mission
-
3. Solution strategies
- 3.1 Option 1: Use of GCC extensions
- 3.2 Option 2: Use of global variables
- 3.3 Option 3: atexit
-
4. Demo testing
- 4.1. test code
- 4.2 Results of implementation
-
5. Abnormal program exit scenarios
- 5.1 Problems
-
5.2 Solutions
- 5.2.1 Principles
- 5.2.2 Example code
- 5.2.3 Implementation results
- 5.2.4 Special instructions
- 6. Reference documents
1. Problems
We know that the execution logic of a C/C++ program starts from the main function and ends with the main function. However, sometimes we need to execute a piece of logic before the main function starts or after it ends, for example:
- How to execute a piece of logic before the main function starts?
- How can I execute a piece of logic after the main function ends?
Is there a way to make it happen? Think about that for a moment before reading down the page.
2. Elements of the mission
Code execution logic for C++ programs.
Understanding of Global Variables|Static Variables.
3. Solution strategies
3.1 Option 1: Use of GCC extensions
GCC compiler extensions through the__attribute__
The keyword registers a callback function to be executed "before or after the start of the main function".
__attribute((constructor)) void before_main() {
std::cout << "before main" << std::endl;
}
__attribute((destructor)) void after_main() {
std::cout << "after main" << std::endl;
}
3.2 Option 2: Use of global variables
Global variables are initialized when the process is started and destroyed when the process is finished. So: the initialization of global objects will be executed before the execution of the main function; the destruction of global objects will be executed after the execution of the main function.
Combining the features of constructors and dummy functions of C++ classes, it is possible to define a class specifically to handle the logic before the main function starts and after it finishes (it is recommended to design this class as a singleton pattern in order to ensure that this class has only one global object), and then declare a global variable of this class before main.
class BeforeAndAfterMain
{
public:
static BeforeAndAfterMain& GetInstance()
{
static BeforeAndAfterMain instance;
return instance;
}
~BeforeAndAfterMain()
{
std::cout << "Global object destory after main" << std::endl;
}
private:
BeforeAndAfterMain()
{
std::cout << "Global object construct before main" << std::endl;
}
BeforeAndAfterMain(const BeforeAndAfterMain&) = delete;
BeforeAndAfterMain& operator=(const BeforeAndAfterMain&) = delete;
};
auto& g_before_and_after_main = BeforeAndAfterMain::GetInstance();
3.3 Option 3: atexit
For the logic after the end of the main function, you can use the atexit function to register a callback function that is executed after the main function is executed.
#include <cstdlib>
void at_main_exit(){
std::cout << "at_main_exit" << std::endl;
}
4. Demo testing
4.1. test code
The full test code is below:
#include <iostream>
#include <cstdlib>
__attribute((constructor)) void before_main() {
std::cout << "before main" << std::endl;
}
__attribute((destructor)) void after_main() {
std::cout << "after main" << std::endl;
}
class BeforeAndAfterMain
{
public:
static BeforeAndAfterMain& GetInstance()
{
static BeforeAndAfterMain instance;
return instance;
}
~BeforeAndAfterMain()
{
std::cout << "Global object destory after main" << std::endl;
}
private:
BeforeAndAfterMain()
{
std::cout << "Global object construct before main" << std::endl;
}
BeforeAndAfterMain(const BeforeAndAfterMain&) = delete;
BeforeAndAfterMain& operator=(const BeforeAndAfterMain&) = delete;
};
auto& g_before_and_after_main = BeforeAndAfterMain::GetInstance();
void at_main_exit(){
std::cout << "at_main_exit" << std::endl;
}
int main() {
// /w/cpp/header/cstdlib
atexit(at_main_exit);
std::cout << "main begin" << std::endl;
int a = 10;
int b = 5;
// crash to exit
// int b = 0;
int c = a / b;
std::cout << "a /b = " << c << std::endl;
std::cout << "main end" << std::endl;
return 0;
}
4.2 Results of implementation
before main
Global object construct before main
main begin
a /b = 2
main end
at_main_exit
Global object destory after main
after main
5. Abnormal program exit scenarios
5.1 Problems
Demo above, put the
int b = 5;
replace with
// crash to exit
int b = 0;
This causes the program to exit with an exception (the divisor cannot be 0) and the output is as follows:
before main
Global object construct before main
main begin
Floating point exception
The logic at the end of all three main functions is not executed. Note: When the program exits abnormally (e.g., crash), the "logic after the end of the main function" is not executed, and cannot cover this scenario.
5.2 Solutions
5.2.1 Principles
When a program crashes, the operating system sends a signal to the program informing it that an exception has occurred. In C++, a signal handler can be registered with the signal function, allowing the program to execute custom code when it receives that signal.
The execution flow of the program:
- Execute the program, following normal logic.
- Programs crash, exit abnormally, and depending on the cause of the crash, the operating system recognizes different crash signals (signals).
- The operating system sends a corresponding crash signal to the executing program.
- The execution program executes the corresponding signal processing logic according to the signal processing function that has been registered in advance.
- The signal processing function is executed and the program is exited through the exit function.
This ensures that even if the main process of the program crashes, the program will still be able to end normally. This way, even if the program crashes, it will still be able to complete important logic such as "resource release", "state save or reset", etc. by itself.
5.2.2 Example code
void signal_handler(int sig) {
// Write your exception signal handling logic here, such as printing logs, saving state, capturing stack info, etc.
std::cerr << "signal_handler" << std::endl;
// Note: When signal handler execution is complete, be sure to call exit to exit, otherwise the signal handler function may be executed in a loop.
exit(1);
}
int main() {
// Register the signal handler
// signal(SIGSEGV, signal_handler); signal(SIGFPE, signal_handler); // register the signal handling function
signal(SIGFPE, signal_handler); signal(SIGFPE, signal_handler).
// // /w/cpp/header/cstdlib
atexit(at_main_exit);
std::cout << "main begin" << std::endl;
// int b = 5; // crash to exit
// crash to exit
int b = 0; int c = a / b; // crash to exit
int c = a / b; std::cout <
std::cout << "a /b = " << c << std::endl.
std::cout << "main end" << std::endl;
return 0;
}
5.2.3 Implementation results
before main
Global object construct before main
main begin
signal_handler
at_main_exit
Global object destory after main
after main
5.2.4 Special instructions
- When a program crashes, it may no longer be possible to execute the code properly, so signal handlers need to be written carefully to avoid further crashes or data corruption.
- When the signal handler execution is complete, be sure to call exit to exit, otherwise the signal handler function may be executed in a loop.
- Consider the various exception signals that may occur, such as SIGSEGV, SIGFPE, SIGILL, SIGABRT, and so on. These possible exceptions require registration of the corresponding signal handler. To avoid the occurrence ofabnormal leakage captureThe situation.
6. Reference documents
/zhizhengguan/article/details/122623008
/MldXTieJiang/article/details/129620160