前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >main函数真的是C程序的开始吗?

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

作者头像
Linux兵工厂
发布2023-02-28 13:48:38
4490
发布2023-02-28 13:48:38
举报
文章被收录于专栏:Linux兵工厂Linux兵工厂

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

1. 实验程序

  • 示例代码
代码语言:javascript
复制
#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参数来显示编译过程,其中输出部分如下
代码语言:javascript
复制
/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命令来查看
代码语言:javascript
复制
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部分。

代码语言:javascript
复制
  .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函数中用到的东西,便于我们做一些准备工作。

带有优先级的参数

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

代码语言:javascript
复制
#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()之后,调用我们的函数。

代码语言:javascript
复制
#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);
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-01-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Linux兵工厂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 实验程序
  • 2. __attribute__((constructor))属性
    • 带有优先级的参数
    • 3. __attribute__((destructor))属性
    • 总结
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档