我们在学习和编写C程序时,都是从main函数开始,main函数作为入口函数已经深深地印在我们的脑海中,那么main函数真的是C程序的入口函数吗?带着这个问题我们先来看下面一段代码。
#include
#include
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
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");
我们的例子中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))
}
__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
#include
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;
}
__attribute__((destructor))
属性 __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
#include
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;
}
__attribute__ ((constructor))
和__attribute__ ((destructor))
类似于C++类中构造函数和析构函数。在main函数之前,执行一个函数,便于我们做一些准备工作;在main()函数退出或者调用了exit()之后调用。多个函数时,GCC为我们提供了一个参数叫优先级,constructor按从小到大,destructor函数相反 void __attribute__((constructor(5)) initFunction1(void); void __attribute__((constructor(10)) initFunction2(void);
实用 | 10分钟教你搭建一个嵌入式web服务器
嵌入式设备AP配网实例分享
嵌入式Linux单板连接飞燕物联网平台
在公众号聊天界面回复1024,可获取嵌入式资源;回复 m ,可查看文章汇总