main函数真的是C程序的开始吗

我们在学习和编写C程序时,都是从main函数开始,main函数作为入口函数已经深深的印在我们的脑海中,那么main函数真的是C程序的入口函数吗?带着这个问题我们先来看下面一段代码。

1. 实验程序

  • 示例代码
#include <stdlib.h>
#include <stdio.h>

static void __attribute__ ((constructor)) beforeMain(void)
{
    
    
    printf("Before main...\n");
}

int main(void)
{
    
    
    printf("Main!\n");
    return 0;
}
  • 输出结果
    在这里插入图片描述

  • 为什么最开始执行的不是main函数?怎么和我们刚开始学习C程序时说的不一样呢?从运行结果中,我们可以看出来beforeMain是在进入main函数之前被调用的,这对于C语言的初学者来说似乎有点难以理解。究竟是谁调用的beforeMain呢?怎么还没有进入main就可以有代码被执行呢?

  • 带着以上问题,我们先用-v参数来显示编译过程,其中输出部分如下

/usr/lib/gcc/x86_64-linux-gnu/5/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/5/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper -plugin-opt=-fresolution=/tmp/ccHn29zY.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/5 -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/5/../../.. /tmp/ccMKdwTx.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-linux-gnu/5/crtend.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o

在这里插入图片描述

  • 从输出结果可以看出,在链接生成最后的可执行文件时,有很多的C库二进制文件参与进来。而最终的可执行文件除了我们编写的这个简单的C代码以外,还有大量的C库文件参与了链接,并包含在了最终的可执行文件中。这个链接的过程,是由链接器ld的链接脚本来决定的。如果我们没有指定链接脚本,会默认使用ld的默认脚本。
  • 通过ld -verbose命令来查看
using internal linker script:
==================================================
/* Script for -z combreloc: combine and sort reloc sections */
/* Copyright (C) 2014-2015 Free Software Foundation, Inc.
   Copying and distribution of this script, with or without modification,
   are permitted in any medium without royalty provided the copyright
   notice and this notice are preserved.  */
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib");
  • 从上面输出可以看出这里定义了输出的文件格式、目标机器的类型,以及重要的信息和程序的入口ENTRY(_start)。

我们的例子中beforeMain函数使用的gcc扩展属性__attribute__((constructor))就是将函数对应的指令归属于.ctors section部分。

  .ctors          :
  {
    
    
    /* gcc uses crtbegin.o to find the start of
       the constructors, so we make sure it is
       first.  Because this is a wildcard, it
       doesn't matter if the user does not
       actually link against crtbegin.o; the
       linker won't look for a file to match a
       wildcard.  The wildcard also means that it
       doesn't matter which directory crtbegin.o
       is in.  */
    KEEP (*crtbegin.o(.ctors))
    KEEP (*crtbegin?.o(.ctors))
    /* We don't want to include the .ctor section from
       the crtend.o file until after the sorted ctors.
       The .ctor section from the crtend file contains the
       end of ctors marker and it must be last */
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
  }

2. __attribute__((constructor))属性

The constructor attribute causes the function to be called automatically before execution enters main ().
构造函数属性使函数在执行进入main()之前自动被调用

  • GNU C的一大特色就是__attribute__机制。__attribute__可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute )。
    __attribute__写法是__attribute__前后都有两个下划线,并切后面会紧跟一对原括弧,括弧里面是相应的__attribute__参数。
    __attribute__格式为__attribute__((attribute-list))

  • 就是指在函数上方加上__attribute__((constructor))可以让这个函数在main函数执行前运行

  • 作用:__attribute__((constructor))可以提前初始化一些在main函数中用到的东西,便于我们做一些准备工作。

带有优先级的参数

我们还可以给属性设置优先级,看下面示例代码

#include <stdlib.h>
#include <stdio.h>

static void __attribute__ ((constructor(101))) beforeMain1(void)
{
    
    
    printf("Before main...1\n");
}
static void __attribute__ ((constructor(102))) beforeMain2(void)
{
    
    
    printf("Before main...2\n");
}
static void __attribute__ ((constructor(103))) beforeMain3(void)
{
    
    
    printf("Before main...3\n");
}

int main(void)
{
    
    
    printf("Main!\n");
    return 0;
}

在这里插入图片描述

3. __attribute__((destructor))属性

  • 查阅了GNU的文档你还会发现有提及这么一个写法__attribute__((destructor)),文档中关于这两个用法的说明如下:

The constructor attribute causes the function to be called automatically before execution enters main (). Similarly, the destructor attribute causes the function to be called automatically after main () completes or exit () is called. Functions with these attributes are useful for initializing data that is used implicitly during the execution of the program.

同理, destructor让系统在main()函数退出或者调用了exit()之后,调用我们的函数。

#include <stdlib.h>
#include <stdio.h>

static void __attribute__ ((constructor)) beforeMain(void)
{
    
    
    printf("Before main...\n");
}

static void __attribute__ ((destructor)) afterMain(void)
{
    
    
    printf("After main...\n");
}

int main(void)
{
    
    
    printf("Main!\n");
    return 0;
}

在这里插入图片描述

总结

C程序中__attribute__ ((constructor))__attribute__ ((destructor))类似于C++类中构造函数和析构函数。在main函数之前,执行一个函数,便于我们做一些准备工作;在main()函数退出或者调用了exit()之后调用。多个函数时,GCC为我们提供了一个参数叫优先级,constructor按从小到大,destructor函数相反 void __attribute__((constructor(5)) initFunction1(void); void __attribute__((constructor(10)) initFunction2(void);
了一个参数叫优先级,constructor按从小到大,destructor函数相反 void __attribute__((constructor(5)) initFunction1(void); void __attribute__((constructor(10)) initFunction2(void);

第一时间获取干货文章,领取海量Linux学习资料,请私信联系或公人从号:Linux兵工厂

猜你喜欢

转载自blog.csdn.net/tgdzsjh/article/details/128832278