weak和alias
一、强符号和弱符号
在C语言中,如果多个模块定义同名全局符号时,链接器认为函数和已初始化的全局变量(包括显示初始化为0)是强符号,未初始化的全局变量是弱符号。
根据这个定义,Linux链接器使用下面的规则来处理多重定义的符号名:
1.不允许有多个同名的强符号
2.如果有一个强符号和多个弱符号同名,那么选择强符号
3.如果有多个弱符号同名,有些编译器从这些弱符号中任意选择一个,有些编译器选择占用内存最大的那个符号
符号链接原理:链接器发现同时存在弱符号和强符号,优先选择强符号,如果发现不存在强符号,只存在弱符号,则选择弱符号
二、weak的使用
对于初始化的各种函数,我们不确定其他地方是否有这个函数,但是我们不得不用这个函数,即在初始化过程中必须得有这个函数。在这种情况下,可以使用__attribute__关键字的weak属性来声明一个弱符号。
//weak.c
#include <stdio.h>
// int fun1()
// {
// printf("new %s\n",__FUNCTION__);
// return 0;
// }
int main()
{
fun1();
return ;
}
//test.c
#include <stdio.h>
int fun1() __attribute__((weak)); int fun1()
{
printf("%s\n",__FUNCTION__);
return ;
}
在注释掉weak.c中的fun1时,编译运行
gcc -o weak weak.c teat.c
./weak
输出结果为:
fun1
接下来,我们把对fun1的注释去掉,同样编译运行,运行结果为:
new fun1
三、alias的使用
在使用weak声明弱符号之后,我们需要将每个弱符号函数都定义为一个空函数,如果每个函数都分开写的话,重复量很大,也比较浪费时间。所以可以用alias属性来给每个函数起别名。
#include <stdio.h> int fun1()
{
printf("%s\n",__FUNCTION__);
return ;
} int fun() __attribute__((alias("fun1"))); int main()
{
fun();
return ;
}
代码中fun的别名是fun1,所以运行结果为:
fun1
四、weak和alias结合使用
//test_attribute.c
#include <stdio.h> int fun1()
{
printf("%s\n",__FUNCTION__);
return ;
} int fun2() __attribute__((weak, alias("fun1")));
int fun3() __attribute__((weak, alias("fun1")));
//attribute.c
#include <stdio.h> int fun2()
{
printf("%s\n",__FUNCTION__);
return ;
} int main()
{
fun2();
fun3();
return ;
}
fun2是强符号,fun3没有初始化,而fun2和fun3的别名均为fun1,运行结果为:
fun2
fun1
五、使用weak和alias对可执行文件的影响
先看使用weak和alias时的代码
//test_attribute.c
#include <stdio.h> int fun1()
{
printf("%s\n",__FUNCTION__);
return ;
} int fun2() __attribute__((weak, alias("fun1")));
int fun3() __attribute__((weak, alias("fun1")));
int fun4() __attribute__((weak, alias("fun1")));
int fun5() __attribute__((weak, alias("fun1")));
int fun6() __attribute__((weak, alias("fun1")));
int fun7() __attribute__((weak, alias("fun1")));
int fun8() __attribute__((weak, alias("fun1")));
int fun9() __attribute__((weak, alias("fun1")));
int fun10() __attribute__((weak, alias("fun1")));
int fun11() __attribute__((weak, alias("fun1")));
int fun12() __attribute__((weak, alias("fun1")));
int fun13() __attribute__((weak, alias("fun1")));
//attribute.c
#include <stdio.h>
int main()
{
fun2();
fun3();
fun4();
fun5();
fun6();
fun7();
fun8();
fun9();
fun10();
fun11();
fun12();
fun13();
return ;
}
链接之后生成的文件大小为:8987。那么如果将weak和alias去掉,同时将这么多fun全部重新定义,
//test_attribute.c
#include <stdio.h> int fun1()
{
printf("%s\n",__FUNCTION__);
return ;
} // int fun2() __attribute__((weak, alias("fun1")));
// int fun3() __attribute__((weak, alias("fun1")));
// int fun4() __attribute__((weak, alias("fun1")));
// int fun5() __attribute__((weak, alias("fun1")));
// int fun6() __attribute__((weak, alias("fun1")));
// int fun7() __attribute__((weak, alias("fun1")));
// int fun8() __attribute__((weak, alias("fun1")));
// int fun9() __attribute__((weak, alias("fun1")));
// int fun10() __attribute__((weak, alias("fun1")));
// int fun11() __attribute__((weak, alias("fun1")));
// int fun12() __attribute__((weak, alias("fun1")));
// int fun13() __attribute__((weak, alias("fun1")));
//attribute.c
#include <stdio.h> int fun2()
{
printf("%s\n",__FUNCTION__);
return ;
}
int fun3()
{
printf("%s\n",__FUNCTION__);
return ;
}
int fun4()
{
printf("%s\n",__FUNCTION__);
return ;
}
int fun5()
{
printf("%s\n",__FUNCTION__);
return ;
}
int fun6()
{
printf("%s\n",__FUNCTION__);
return ;
}
int fun7()
{
printf("%s\n",__FUNCTION__);
return ;
}
int fun8()
{
printf("%s\n",__FUNCTION__);
return ;
}
int fun9()
{
printf("%s\n",__FUNCTION__);
return ;
}
int fun10()
{
printf("%s\n",__FUNCTION__);
return ;
}
int fun11()
{
printf("%s\n",__FUNCTION__);
return ;
}
int fun12()
{
printf("%s\n",__FUNCTION__);
return ;
}
int fun13()
{
printf("%s\n",__FUNCTION__);
return ;
} int main()
{
fun2();
fun3();
fun4();
fun5();
fun6();
fun7();
fun8();
fun9();
fun10();
fun11();
fun12();
fun13();
return ;
}
在这种情况下,生成的可执行文件的大小为:9473。可见利用weak和alias可以节省可执行文件的占用空间。
六、函数有形参的情况
如果函数有形参,具体情况是什么呢?看下面代码,如果别名函数有形参,
//test_attribute.c
#include <stdio.h> int fun1(int a, int b)
{
a = ;
printf("%s, %d\n", __FUNCTION__, a);
return ;
} int fun2() __attribute__((weak, alias("fun1")));
//attribute.c
#include <stdio.h>
int main()
{
fun2();
return ;
}
运行结果为:
fun1,
另外,还有一种情况,强符号函数声明有形参
//test_attribute.c
#include <stdio.h> int fun1(int a, int b)
{
a = ;
printf("%s, %d\n", __FUNCTION__, a);
return ;
} int fun2() __attribute__((weak, alias("fun1")));
int fun3() __attribute__((weak, alias("fun1")));
int fun4() __attribute__((weak, alias("fun1")));
int fun5() __attribute__((weak, alias("fun1")));
int fun6() __attribute__((weak, alias("fun1")));
int fun7() __attribute__((weak, alias("fun1")));
int fun8() __attribute__((weak, alias("fun1")));
int fun9() __attribute__((weak, alias("fun1")));
int fun10() __attribute__((weak, alias("fun1")));
int fun11() __attribute__((weak, alias("fun1")));
int fun12() __attribute__((weak, alias("fun1")));
int fun13() __attribute__((weak, alias("fun1")));
//attribute.c
#include <stdio.h> int fun2(int a, int b)
{
a = ;
printf("%s, %d\n", __FUNCTION__, a);
return ;
}
int main()
{
int a;
int b;
fun2(a, b);
return ;
}
运行结果为:
fun2,
个人理解:
在利用weak和alias设置弱符号函数的时候,实际是将fun2指向了fun1,
而如果另外定义了同样的fun2后,它属于强符号,那么test_sttribute.c声明中的fun2指向的是这个新定义的fun2,
这种实现是对函数名地址的改变,而函数内的形参在设置weak和alias可以不用写入
weak和alias的更多相关文章
- gcc/g++中weak弱符号及alias别名
最近查看linux内核代码时,表现了一些编译器选项如__attribute_((weak)).__attribute__( (alias("target"))),一开始不了解,后来 ...
- weak alias
Weak Alias 跟 Weak Reference 完全没有任何关系,不过是我在看到 Weak Reference 的时候想到的而已. Weak Alias 是 gcc 扩展里的东西,实际上是函数 ...
- [development] __attribute__((weak))是干嘛的
简单的说,就是当发生 “重复定义的时候”.被声明者会被冲突者覆盖掉. 这里还涉及了weak与alias连用的情况. 参见,里边有两个例子,很浅显易懂. https://my.oschina.net/s ...
- iOS宏和__attribute__
本文目录 iOS宏的经典用法 Apple的习惯 __attribute__ iOS宏的经典用法 1.常量宏.表达式宏 #define kTabBarH (49.0f) #define kScreenH ...
- 5.24 Declaring Attributes of Functions【转】
转自:https://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Function-Attributes.html 5.24 Declaring Attributes o ...
- GNU C/C++ __attributes__ GCC中的弱符号与强符号
最近在看一些源代码,遇到了一些使用__attribute__修饰函数和变量的属性方面的代码,不是太了解,很是汗颜,再此做个总结: GCC使用__attribute__关键字来描述函数,变量和数据类 ...
- 特殊的attribute机制
__attribute__机制是GNU C的一大特色,可以用来设置函数,变量和数据类型的属性,下面对工作中正好用到的两个属性做下简单介绍. 1. constructor 这个属性指定函数在main函数 ...
- linux内核中GNU C __attribute__ 机制的实用
很多东西,只看看是不行的,要想深入的去了解一个东西,一定要去不断地学习,实践,反思. 说白了就是要去打磨. 在linux中,最近遇到了这样一个定义: int board_usb_init(int in ...
- linux kernel笔记
文章目录 关于linux内核中的__attribute__关键字 Linux kernel启动参数 gdt / ldt PCB 关于linux内核中的__attribute__关键字 part I: ...
随机推荐
- Mysql干货收集
mysql优化:https://www.cnblogs.com/duanxz/tag/mysql/default.html?page=1
- java分页之假分页
假分页,顾名思义,不是真正的在数据库里进行过滤,而是从数据库查询之后,取得全部结果,在展现的时候做些手脚. import java.util.ArrayList; import java.util.L ...
- TCL命令(事务控制)
确认提交DML操作:commit; 撤销DML操作:rollback; 提示:rollback撤销的是与上一个commit之间 所做的DML操作.注意:仅对 ...
- Qt之设置应用程序图标
简述 应用程序图标,通常显示在应用程序的顶层窗口的左上角,通过调用QWindow:setWindowIcon()函数来实现. 为了改变可执行程序文件本身的图标,因为它被呈现在桌面上,它必须采用另一种依 ...
- 剑指offer_面试题_从上往下打印二叉树
题目:从上往下打印出二叉树的每一个结点.同一层的结点依照从左到右的顺序打印.比如输入图4.5中的二叉树.则依次打印出8.6.10.5.7.9.11. 8 / \ 6 10 / \ ...
- python部分
读取骨骼数据相关的多个json,拼接到一起 # -- coding: utf-8 -- import os path = "./test" #文件夹目录 files= os.lis ...
- 一个比NPM更快更安全可靠的JavaScript包管理工具——Yarn
yarn安装: npm intall -g yarn 查看安装是否成功: yarn -v yarn常用的命令以及和npm的对照如下: 更详细的请查看官方文档
- Nginx访问VM虚拟机CentOS 7系统与本地Windows系统共享目录403
用VMware安装了CentOS7系统,并搭建了Nginx,MySQL,PHP的web项目运行环境,为了方便Windows本地主机进行程序调试把Windows本地项目目录共享到了虚拟机CentOS中的 ...
- 关于hexo markdown添加的图片在github page中无法显示的问题
title: 关于hexo markdown添加的图片在github page中无法显示的问题 date: 2018-03-31 00:21:18 categories: methods tags: ...
- 【原创】JMS生产者和消费者【PTP异步接收消息】
PTP模式下,异步接收消息需要定义一个MessageListener来监听,当生产者有消息要发送时会主动通知Listener去处理该消息,会调用监听的onMessage方法去处理. 首先看生产者(和同 ...