来源:《周哥教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函数之后执行。

代码如下:

 #include <stdio.h>

 __attribute((constructor)) void before_main()
 {
     printf("before main!\n");
 }

 __attribute((destructor)) void after_main()
 {
     printf("after main!\n");
 }

 int main(void)
 {
     printf("This is main function.\n");
     ;
 }

before_main.c

运行结果:

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
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");
    ;
}

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");
    ;
}

before_main.c

3. C++编程中利用定义全局类对象or全局变量

mainCRTStartup会对全局对象a初始化,也就是说a的构造含税会先于main执行,所以只需要在a的构造函数中定义我们要执行的函数。

另一种方式是定义一个全局变量为函数运行后的结构,那么该函数就会用于初始化,会先于main执行。

代码如下:

 #include <iostream>
 using namespace std;
 using std::cout;

 int func()
 {
     cout <<"before main: func()" << endl;
     ;
 }

 class A
 {
 public:
     A()
     {
         cout << "A() constructor" << endl;
     }
     ~A()
     {
         cout << "A() destructor" << endl;
     }
 };

 A a;

 int g_iValue = func();

 int main(void)
 {
     cout << "This is main function." << endl;
     ;
 }

before_main.cpp

运行结果:

A() constructor
before main: func()
This is main function.
A() destructor

C/C++程序在main之前执行代码的更多相关文章

  1. 编写Java程序,观察类启动时静态代码块和main()的执行顺序

    返回本章节 返回作业目录 需求说明: 观察类启动时静态代码块和main()的执行顺序 在Book类中定义静态代码块. 在Book中分别定义一个普通实例方法和静态方法. 在Book类的静态代码块中调用静 ...

  2. main函数执行前、后再执行的代码

    一.main结束 不代表整个进程结束  (1)全局对象的构造函数会在main 函数之前执行,          全局对象的析构函数会在main函数之后执行:          用atexit注册的函数 ...

  3. C 语言main 函数终极探秘(&& 的含义是:如果 && 前面的程序正常退出,则继续执行 && 后面的程序,否则不执行)

           所有的C程序必须定义一个称之为main的外部函数,这个函数是程序的入口,也就是当程序启动时所执行的第一个函数,当这个函数返回时,程序也将终止,并且这个函数的返回值被看成是程序成功或失败的 ...

  4. c/c++ main函数执行之前/后

    转载自:http://bbs.csdn.net/topics/300103318#r_78088969 main函数之前--真正的函数执行入口或开始 一种解释: 实际上,在可执行文件被加载之后,控制权 ...

  5. (转)Java程序利用main函数中args参数实现参数的传递

    Java程序利用main函数中args参数实现参数的传递 1.运行Java程序的同时,可以通过输入参数给main函数中的接收参数数组args[],供程序内部使用!即当你在Java命令行后面带上参数,J ...

  6. [学习笔记]java基础Java8SE开发环境搭建、第一个Java Hello World、Java程序的编译与执行

    本文作者:sushengmiyan 本文地址:http://blog.csdn.net/sushengmiyan/article/details/25745945 内容简介: ------------ ...

  7. 如何用C#动态编译、执行代码

    在开始之前,先熟悉几个类及部分属性.方法:CSharpCodeProvider.ICodeCompiler.CompilerParameters.CompilerResults.Assembly. 一 ...

  8. [转]如何用C#动态编译、执行代码

    在开始之前,先熟悉几个类及部分属性.方法:CSharpCodeProvider.ICodeCompiler.CompilerParameters.CompilerResults.Assembly. 一 ...

  9. C#动态执行代码

          在开始之前,先熟悉几个类及部分属性.方法:CSharpCodeProvider.ICodeCompiler.CompilerParameters.CompilerResults.Assem ...

随机推荐

  1. Springboot - 学习笔记 ②

    前言 这一篇是关于spring boot中的配置(configuration)的介绍,我们接下来要说的男主就是 “application.properties”. “男神”默认是生成在“/src/ma ...

  2. jdbc与mybatis区别

    jdbc的缺点: 1.频繁创建连接,浪费资源 2.SQL语句硬编码,不利于维护 3.传参是硬编码,不利于维护 4.结果集是硬编码,不利于维护 但是mybatis很好的解决了这些问题.

  3. jQuery中的常用内容总结(二)

    jQuery中的常用内容总结(二) 转载请注明地址: http://www.cnblogs.com/funnyzpc/p/7571993.html 前言 距离上次博客更新已经有二十来天了(●′ω`●) ...

  4. 搭建git远程服务器三步骤

    以前都是使用git,这次由于工作需要,需要自己搭建一个远程git服务器.根据网上的 介绍,捣鼓了一下午,终于把远程git服务器搞定了,这里,做个总结. 搭建git远程服务,首先要安装git和ssh,以 ...

  5. JavaScript设计模式--简单工厂模式

    一,介绍 工厂模式创建对象(视为工厂里的产品)时无需指定创建对象的具体类. 工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类.该模式使一个类的实例化延迟到了子类.而子类可以重写接口 ...

  6. Spring MVC Ajax 嵌套表单数据的提交

    概述 在一些场景里,某个大表单里常常嵌套着一个或若干个小逻辑块,比如以下表单里"设计预审"中包括了一个子模块表单"拟定款项". 在这种情况下该怎么去设计实体类以 ...

  7. HTTP错误代码大全

    HTTP出错大全 101 - Switching Protocols Top Success Codes 200 - OK201 - Created202 - Accepted203 - Non-Au ...

  8. 简述static关键字、void与void *(void指针)、函数指针

    static关键字1.修饰局部变量,延长局部变量的生命周期.使变量成为静态局部变量,在编译时就为变量分配内存,直到程序退出才释放存储单元.2.修饰全局变量,限制全局变量的使用范围为本文件中.全局变量默 ...

  9. OpenWRT UCI命令实现无线中继

    本文主要功能主要是利用OpenWRT系统uci命令实现无线中继,主要是利用uci程序修改/etc/congfig/目录下的配置文件.实现步骤如下主要分为以下几步: 1) 安装 relayd (opkg ...

  10. 关于CSS 的position定位问题

    对于初学者来说,css的position定位问题是比较常见的.之前搞不清楚postion定位是怎么回事,排版一直歪歪斜斜的,老是排不好 css的定位一般来说,分为四种: position:static ...