main之前执行的代码(初始化系统相关资源)
1.设置栈
- 设置栈指针
2.初始化操作
-
初始化静态
static
变量和global
全局变量,即.data
段的内容 -
全局对象初始化,在
main
之前调用构造函数,这是可能会执行前的一些代码-
__attribute__((constructor))
-
在 C 和 C++ 编程语言中,`__attribute__((constructor))` 是一个 GCC 扩展,它允许开发人员指定一个函数,以便在程序开始运行时,在执行 `main()` 之前自动调用。 该函数通常称为“构造函数”。 `__attribute__((constructor))` 语法用于指定应在程序启动时自动调用特定函数。 这对于初始化全局变量或设置程序正确运行所需的资源很有用。 例如,构造函数可用于初始化日志系统或建立数据库连接。 通过使用构造函数,可以在程序开始运行之前自动设置这些资源,而无需在 `main()` 中显式初始化。 重要的是要注意构造函数的调用顺序是无法保证的。 如果构造函数之间存在依赖关系,则必须注意确保以正确的顺序调用它们。 此外,构造函数不能依赖于其他全局变量的状态,因为它们可能尚未初始化。 总体而言,`__attribute__((constructor))` 是在执行 `main()` 之前初始化资源和设置程序环境的有用工具。 它在具有许多全局变量和复杂初始化要求的大型程序或库中特别有用。 eg: #include <stdio.h> void __attribute__((constructor)) my_constructor(void) { printf("Constructor called\n"); }//构造函数 int main() { printf("Main function called\n"); return 0; } 输出: Constructor called Main function called
-
-
3.赋初值
- 将未初始化部分的全局变量赋初值:数值型
short
,int
,long
等为0
,bool
为FALSE
,指针为NULL
等等,即.bss
段的内容
4.传参
- 将main函数的参数
argc
,argv
等传递给main
函数,然后才真正运行main
函数
main之后执行的代码
1.析构函数
-
__attribute__((destructor))
-
在 C 和 C++ 编程语言中,`__attribute__((destructor))` 是一个 GCC 扩展,允许开发人员指定一个函数,在程序正常退出时,在 `main()` 执行完毕后自动调用。 该函数通常称为“析构函数”。 `__attribute__((destructor))` 语法用于指定应在程序退出时自动调用特定函数。 这对于在程序退出前执行清理操作或保存程序状态很有用。 例如,析构函数可用于关闭打开的文件、释放分配的内存或将数据写入日志文件。 通过使用析构函数,这些清理操作可以在程序退出时自动执行,即使程序意外退出也是如此。 eg: #include <stdio.h> void __attribute__((destructor)) my_destructor(void) { printf("Destructor called\n"); } int main() { printf("Main function called\n"); return 0; } /*在此示例中,使用 __attribute__((destructor)) 语法将函数 my_destructor() 指定为析构函数。 这意味着当程序正常退出时,`my_destructor()` 将在 `main()` 执行完毕后自动调用。`my_destructor()` 函数只是向控制台打印一条消息,表明它已被调用。 `main()` 函数还会向控制台打印一条消息,因此我们可以看到调用函数的顺序。*/ 输出: Main function called Destructor called 这表明在 main() 完成执行后,析构函数会在程序退出时自动调用。 通过使用 __attribute__((destructor)) ,我们可以确保在程序退出时自动执行任何所需的清理操作,而不需要在 main 函数或程序的其他部分中进行显式清理。
-
2.可以用 atexit
注册一个函数,它会在main 之后执行;
-
atexit()
-
`atexit()` 是一个标准的 C 库函数,允许开发者注册在程序正常退出时自动调用的函数。 用 atexit() 注册的函数通常被称为“退出处理函数”。 `atexit()` 将函数指针作为参数,并注册该函数以在程序退出时调用。 已注册的函数按其注册的相反顺序调用(即,最后注册的函数最先调用)。 eg: #include <stdlib.h> #include <stdio.h> void exit_handler1() { printf("Exit handler 1 called\n"); } void exit_handler2() { printf("Exit handler 2 called\n"); } //在这个例子中,我们定义了两个退出处理函数,`exit_handler1()` 和 `exit_handler2()`。 然后我们使用 atexit() 来注册这 //两个函数,以便在程序正常退出时调用。 int main() { atexit(exit_handler1); atexit(exit_handler2); printf("Main function called\n"); return 0; } 输出: Main function called Exit handler 2 called Exit handler 1 called 这表明退出处理程序函数在程序退出时以与注册相反的顺序自动调用。 退出处理函数对于在程序退出前执行清理操作或保存程序状态很有用。例如,退出处理程序函数可能会关闭打开的文件、释放分配的内存或将数据写入日志文件。 通过使用 atexit() ,我们可以确保自动执行这些清理操作,即使程序意外退出也是如此。
析构函数与构造函数详解
-
//在面向对象的编程中,构造函数是一种特殊的方法,在创建对象或其生命周期开始时自动调用。 构造函数负责初始化对象的数据成员并设置任何所需的资源。
//另一方面,析构函数是一种特殊方法,当对象被销毁或其生命周期结束时,它会自动调用。 析构函数负责释放对象在其生命周期内分配的所有资源。
//在 C++ 中,构造函数使用与类相同的名称定义,并且没有返回类型。
class MyClass {
public:
MyClass() {
// constructor code
}
};
//在这个例子中,我们定义了一个带有构造函数的类“MyClass”。 创建类的对象时会自动执行构造函数代码。
//构造函数通常用于初始化对象的数据成员并设置任何所需的资源。 例如,构造函数可能会分配内存、打开文件或初始化其他对象。
//这是构造函数如何用于初始化对象的数据成员的示例:
class MyClass {
public:
MyClass(int value) {
m_value = value;
}
private:
int m_value;
};
//在此示例中,构造函数采用整数参数并使用提供的值初始化对象的“m_value”数据成员。
//另一方面,析构函数是使用与类相同的名称定义的,前面有波浪号 (~)。 这是一个例子:
class MyClass {
public:
MyClass() {
// constructor code
}
~MyClass() {
// destructor code
}
};
//在此示例中,我们定义了一个带有构造函数和析构函数的类“MyClass”。 当类的对象被销毁时,析构函数代码会自动执行。
//析构函数通常用于释放对象在其生命周期内分配的资源,例如内存、文件句柄或其他系统资源。 如果没有正确清理这些资源,它们可能会导致内存泄漏或其他问题。
//以下是如何使用析构函数释放内存的示例:
class MyClass {
public:
MyClass() {
// allocate memory
m_data = new int[100];
}
~MyClass() {
// free memory
delete[] m_data;
}
private:
int* m_data;
};
//在此示例中,构造函数使用“new”运算符在堆上分配一个整数数组,析构函数使用“delete[]”运算符释放内存。 这样可以确保在销毁对象时正确清理内存。