GCC编译器函数属性声明之__attribute__((alias (“target“)))

文章介绍了GCC编译器的函数属性alias如何用于创建函数别名,以实现版本兼容。当需要修改函数接口但又需保持向后兼容时,alias属性允许创建新的函数名作为原有函数的别名,避免影响已依赖旧接口的程序。文中通过例子展示了如何使用alias在动态库升级中保持与旧版本的兼容性,以及与weak属性结合的潜在应用场景。
摘要由CSDN通过智能技术生成

GCC编译器函数属性 

gcc编译器函数属性声明概括请看往期博客:

http://t.csdn.cn/a9bCBicon-default.png?t=N7T8http://t.csdn.cn/a9bCB

alias("target")

函数属性声明——alias("target"),起到的作用是:给函数起一个别名

alias 属性导致函数声明将会作为另一个符号(函数)的别名被“触发”,该符号(函数)之前必须使用相同的类型进行声明,对于函数的参数,也必须具有相同的大小和对齐方式。

声明与目标类型不同的别名是未定义的,并且会在编译阶段被诊断为错误。

例如,以下声明:

void __func(){
    printf("I'm __func()\n");
    return;
}

void func() __attribute__((alias("__func")));

定义了‘func’作为‘__func’的别名。此时调用func和直接调用__func的效果是一致的:

#include <stdio.h>

void __func(){
    printf("I'm __func()\n");
    return;
}

void func() __attribute__((alias("__func")));

int main(){
	func();
	__func();
	
	return 0;
}

gcc编译运行,输出内容如下:

I'm __func()
I'm __func()

可以看出,调用func()实际上就是调用了__func()。

那么,实际开发过程中,alias (“target“) 的经典应用场景是什么呢?

版本兼容

现在,你已经开发完成一套动态库a,a库内包括三个函数接口:void alice(void)void bob(void)void celia(void),该库版本号被你命名为1.0.0。a库源代码如下:(省略若干细节,专注重点)

a.h:

#ifndef A_H
#define A_H

void alice(void);

void bob(void);

void celia(void);

#endif

 a.c:

#include "a.h"
void alice(void){
	printf("I'm alice\n");
	return;
}

void bob(void){
	printf("I'm bob\n");
	return;
}

void celia(void){
	printf("I'm celia\n");
	return;
}

很久之后,根据项目的一贯命名规则,你发现该库中的函数接口命名需要修改,需要将首字母大写,即:void Alice(void)void Bob(void)void Celia(void)

首先可以想到的解决方案是,将源代码中函数名修改,重新编译动态库a,然后替换交付。

但是,由于你的客户Davis公司已经基于a库的1.0.0版本编写了他们的程序Elbert 1.0.0Fredy 1.0.0,并且已经发布,其他用户已经下载使用中。Elbert 1.0.0Fredy 1.0.0两者使用同一个动态库a,在某个特定的Davis公司的安装目录下(至于为什么不是每个软件都各自维护一个动态库,这是考虑到实际情况,有些库的体积是相当大的,而且分开不好维护)。按照刚才的解决思路,那么Davis公司需要根据你库的修改重新修改他们的程序,然后重新编译发行,然后向用户发布更新1.0.1版本。

现在某些用户有这样的需求:我想升级Elbert 1.0.0Elbert 1.0.1版本,但由于Fredy 1.0.0我用的特别顺手,不想升级。此需求带来的问题是升级Elbert会将a库替换为1.0.1版本,届时Fredy 1.0.0无法正常使用a库,必然崩溃。

车到山前必有路,让Elbert 1.0.1依赖的库名称改一下,不叫a库了,改叫a.1.0.1库,之后的库后面都加个版本。这样做带来的后果是,Davis公司某个存放动态库的路径下,若干年后出现了这样的情况:

a        a.1.0.1        a.1.0.2        a.1.0.3        a.1.0.4        a.1.0.5 .............

另一个后果是,你所在公司将会受到极大谴责,被批判随意命名库,库名随意更换,基于a库进行开发时,需要时刻注意版本,根据版本选择库名称。

痛定思痛,考虑更佳的解决方案——alias("target")

a.h:

#ifndef A_H
#define A_H

void alice(void);

void bob(void);

void celia(void);

#endif

 a.c:

#include <stdio.h>
#include "a.h"
void __alice(void){
	printf("I'm alice\n");
	return;
}

void alice(void) __attribute__((alias("__alice")));

void __bob(void){
	printf("I'm bob\n");
	return;
}

void bob(void) __attribute__((alias("__bob")));

void __celia(void){
	printf("I'm celia\n");
	return;
}

void celia(void) __attribute__((alias("__celia")));

 如果a库的1.0.0版本是这种方式实现的,那么想要达到更改函数首字母为大写这个需求,需要修改源代码中相关部分,然后重新编译,发布1.0.1版本。

a.h:(1.0.1)

//1.0.0
void alice(void);

void bob(void);

void celia(void);

//1.0.1
void Alice(void);

void Bob(void);

void Celia(void);

 a.c:(1.0.1)

#include <stdio.h>
#include "a.h"
void __alice(void){
	printf("I'm alice\n");
	return;
}

void alice(void) __attribute__((alias("__alice")));

void Alice(void) __attribute__((alias("__alice")));

void __bob(void){
	printf("I'm bob\n");
	return;
}

void bob(void) __attribute__((alias("__bob")));

void Bob(void) __attribute__((alias("__bob")));

void __celia(void){
	printf("I'm celia\n");
	return;
}

void celia(void) __attribute__((alias("__celia")));

void Celia(void) __attribute__((alias("__celia")));

 现在,a库的1.0.1版本完全可以兼容1.0.0版本,编译好新版本库后,发给Davis公司,Davis公司无需对Elbert 1.0.0Fredy 1.0.0重新编译,只需要替换打包过程中的a库即可,重新打包a库ElbertFredy的安装程序中,即可得到对应的1.0.1版本了。

再回到某些用户的需求上,如果只升级了Elbert至1.0.1,Fredy保持1.0.0版本,两者共用a库1.0.1版本,a库中既包含老的小写字母的接口,也包含大写字母开头的接口,完全可以兼容两者。

其他场景

另一个常见的应用场景,是和 weak 相结合,给函数起一个别名的同时,声明为弱符号

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值