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

main函数真的是C程序的开始吗?,第1张

c程序时时时时时,main函数开始,main函数函数入口函数已经已经深深深深地地印印脑海脑海脑海脑海脑海中中中中中先来看下面一段代码。

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程序的开始吗?,图片,第2张

  • main函数函数不的函数函数?我们刚刚开始开始开始开始开始开始时时说说说的的不不不不一样一样一样呢呢呢呢呢????从从从运行结果结果结果结果结果中中中中中中中中中中中中中一样一样不不不不不不不不不不不不学者来说好像有点难理解。研究竟是谁调用的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

main函数真的是C程序的开始吗?,图片,第3张

  • 输出,在看出看出看出最后文件文件时时链接链接,并包含了最终的执行文件中个个链接链接的过程过程
  • 通过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 部分。

  .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))属性

constructor 属性导致函数在执行进入main() 之前被自动调用。构建函数属性使函数在执行进入main()之前自动被调用

  • gnu c的一特色就是__attribute__机制。可以函数属性(((((((((,,,,,变量变量变量属性属性属性属性属性((((((((((((和和和和和和和属性((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((type__attribute__属性)。,比如弯曲里面是对应的参数。格式为__attribute____attribute____attribute____attribute____attribute__((attribute-list))

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

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

带有优先级的参数

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

#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;
}

main函数真的是C程序的开始吗?,图片,第4张


3.__attribute__((destructor))属性

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

constructor 属性导致函数在执行进入main() 之前被自动调用。同样,destructor 属性使函数在main() 完成或调用exit() 后自动调用。具有这些属性的函数对于初始化在程序执行期间隐式使用的数据很有用。

同理,析构函数让系统在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;
}

main函数真的是C程序的开始吗?,图片,第5张


总结

  • c程序类似于类似于类似于类似于类中__attribute__ ((constructor))构造__attribute__ ((destructor))函数析构函数析构函数。在在在在在在在函数函数,执行函数函数,执行个,便于函数,便于函数函数函数这时,GCC 为我们提供了一个参数被称为优先级,构造函数从小到大,析构函数随机数相反 void __attribute__((constructor(5)) initFunction1(void); void __attribute__((constructor(10)) initFunction2(void);

DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
白度搜_经验知识百科全书 » main函数真的是C程序的开始吗?

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情