当前位置: 首页 > 科技观察

C语言:如何给全局变量起别名?

时间:2023-03-20 13:14:05 科技观察

什么是别名?我在stackoverflow上看到一个有趣的话题:Howtoassignanaliastoavariable?(如何给一个变量赋别名?)所谓变量别名就是通过不同的标识符,来表示同一个变量。我们知道变量名是给程序员的。在编译器看来,所有的变量都变成了地址。请注意:这里讨论的别名只是通过不同的标识符引用同一个变量。与强符号、弱符号的概念无关,那是另一个话题。在上面的帖子中,作者首先想到了通过宏定义来重命名变量。这样在编译前的预处理环节,宏标识符就会被变量标识符代替。网友回答的大部分答案都是通过指针实现的:让不同的标识符指向同一个变量。无论如何,这是一个别名。但是,这些答案有一个局限性:代码必须一起编译,否则可能会出现找不到符号的错误信息。插件编程现在很流行。如果开发者想通过插件中的变量别名来引用主程序中的变量,应该如何处理?本文提供了两种方法来实现这一目的,并通过两个简单的示例代码来进行演示。方法一:在反向注册之前接触过一些CodeSys的代码,里面的代码质量真的很高,尤其是软件架构设计部分。误解:CodySys是工业控制领域的Android。有一个反向注册的思路,可以用于变量别名。示例代码中有2个文件:main.c和plugin.c。main.c中定义了一个全局变量数组,编译成可执行程序main。Plugin.c使用别名来使用main.c中的全局变量。plugin.c被编译成动态链接库,由可执行程序main动态加载(dlopen)。在plugin.c中,提供了一个函数func_init。动态库maindlopened时调用该函数,将真正的全局变量地址作为参数传入。这样就可以通过别名在插件中使用真正的变量(例如:修改变量的值)。本质上,这仍然是通过指针进行引用。它只是利用动态注册的思想,在时间和空间上隔离了指针和变量之间的绑定关系。plugin.c源文件#includeint*alias_data=NULL;voidfunc_init(int*data){printf("libplugin.so:func_initiscalled.\n");alias_data=data;}voidfunc_stage1(void){printf("libplugin.so:func_stage1被调用。\n");如果(别名数据){别名数据[0]=100;别名_数据[1]=200;}}main.c源文件#include#include#include//定义在libplugin.sotypedefvoid(*pfunc_init)(int*);typedefvoid(*pfunc_stage1)(void);intdata[100]={0};voidmain(void){data[0]=10;数据[1]=20;printf("数据[0]=%d\n",数据[0]);printf("数据[1]=%d\n",数据[1]);//打开libplugin.sovoid*handle=dlopen("./libplugin.so",RTLD_NOW);if(!handle){printf("dlopen失败。\n");返回;}//获取并调用libplugin.so中的init函数pfunc_initfunc_init=(pfunc_init)dlsym(handle,"func_init");if(!func_init){printf("获取func_init失败。\n");返回;}func_init(数据);//获取并调用libplugin.so中的例程函数pfunc_stage1func_stage1=(pfunc_stage1)dlsym(handle,"func_stage1");if(!func_stage1){printf("获取func_stage1失败。\n");返回;}func_stage1();printf("数据[0]=%d\n",数据[0]);printf("数据[1]=%d\n",数据[1]);return;}编译指令如下:gcc-m32-fPIC--sharedplugin.c-olibplugin.sogcc-m32-omainmain.c-ldl执行结果:data[0]=10data[1]=20libplugin.so:func_init被调用。libplugin.so:调用func_stage1。data[0]=100data[1]=200可以看动态链接库的符号表:readelf-slibplugin.so|grepdata可以看到alias_data标识,是在这个文件中定义的全局变量方法二:嵌入汇编代码在动态加载的插件中使用变量别名。除了上面演示的动态注册方式外,还可以嵌入汇编代码来:设置一个全局标签来实现。直接上显示示例代码:plugin.c源文件#includeasm(".Globalalias_data");asm("alias_data=data");externintalias_data[];voidfunc_stage1(void){printf("libplugin.so:func_stage1被调用。\n");*(别名_数据+0)=100;*(alias_data+1)=200;}main.c源文件#include#include#include//在libplugin.sotypedef中定义void(*pfunc_init)(int*);typedefvoid(*pfunc_stage1)(void);intdata[100]={0};voidmain(void){data[0]=10;数据[1]=20;printf("数据[0]=%d\n",数据[0]);printf("数据[1]=%d\n",数据[1]);//打开libplugin.sovoid*handle=dlopen("./libplugin.so",RTLD_NOW);if(!handle){printf("dlopen失败。\n");返回;}//获取并调用libplugin.so中的例程函数pfunc_stage1func_stage1=(pfunc_stage1)dlsym(handle,"func_stage1");if(!func_stage1){printf("获取func_stage1失败。\n");返回;}func_stage1();printf("数据[0]=%d\n",数据[0]);printf("数据[1]=%d\n",数据[1]);return;}编译命令:gcc-m32-fPIC--sharedplugin.c-olibplugin.sogcc-m32-rdynamic-omainmain.c-ldl执行结果:data[0]=10data[1]=20libplugin.so:调用func_stage1。data[0]=100data[1]=200再看看libplugin.so中的符号信息:readelf-slibplugin.so|grepdata总结本文档通过两个示例代码讨论了如何在插件(动态链接库)中通过别名访问真实变量。不知道大家会不会有这样的疑问:直接用extern来声明外部定义的变量是不行的,何必呢?理由很对!但是在一些特殊的领域或者场景下(比如一些二次开发),这样的需求是真实存在的,而且是强烈的需求。