• 发文
  • 评论
  • 微博
  • 空间
  • 微信

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

道哥分享 2022-05-24 18:36 发文

目录

· 别名是啥玩意?

· 方法1:反向注册

· 方法2:嵌入汇编代码

· 小结

别人的经验,我们的阶梯!

别名是啥玩意?

在stackoverflow上看到一个有趣的话题:如何给一个变量设置一个别名?(How to assign to a variable an alias?)

所谓的变量别名,就是通过通过不同的标识符,来表示同一个变量。

我们知道,变量名称是给程序员使用的。

在编译器的眼中,所有的变量都变成了地址。

· 请注意:这里所讨论的别名,仅仅是通过不同的标识符来引用同一个变量。

· 与强符号、弱符号的概念没有任何关系,那是另一个话题。

在上面这个帖子中,作者首先想到的是通过宏定义,对变量进行重新命名。

这样的做法,将会在编译之前的预处理环节,把宏标识符替换为变量标识符。

在网友回复的答案中,大部分都是通过指针来实现:让不同的标识符指向同一个变量。

不管怎么说,这也算是一种别名了。

但是,这些答案有一个局限:这些代码必须一起进行编译才可以,否则就可能出现无法找到符号的错误信息。

现在非常流行插件编程,如果开发者想在插件中通过一个变量别名来引用主程序中的变量,这该如何处理呢?

本文提供两个方法来实现这个目的,并通过两个简单的示例代码来进行演示。

文末有示例代码的下载地址。

方法1:反向注册

之前我接触过一些CodeSys的代码,里面的代码质量真的是非常的高,特别是软件架构设计部分。

传说:CodySys 是工控界的 Android。

其中有个反向注册的想法,正好可以用在变量别名上面。

示例代码中一共有 2 个文件:main.c和plugin.c。

main.c中定义了一个全局变量数组,编译成可执行程序main。

plugin.c中通过一个别名来使用main.c中的全局变量。

plugin.c被编译成一个动态链接库,被可执行程序main动态加载(dlopen)。

在plugin.c中,提供一个函数func_init,当动态库被main dlopen之后,这个函数就被调用,并且把真正的全局变量的地址通过参数传入。

这样的话,在插件中就可以通过一个别名来使用真正的变量了(比如:修改变量的值)。

本质上,这仍然是通过指针来进行引用。

只不过利用动态注册的思想,把指针与变量的绑定关系在时间和空间上进行隔离。

plugin.c 源文件

#include <stdio.h>

int *alias_data = NULL;

void func_init(int *data)

           printf("libplugin.so: func_init is called. ");
                  alias_data = data;

void func_stage1(void)

           printf("libplugin.so: func_stage1 is called. ");
                  if (alias_data)

          {
                           alias_data[0] = 100;
                           alias_data[1] = 200;
                   }



main.c 源文件

#include <stdio.h>

#include <stdlib.h>

#include <dlfcn.h>

// defined in libplugin.so

typedef void (*pfunc_init)(int *);

typedef void (*pfunc_stage1)(void);

int data[100] = { 0 };


void main(void)

    data[0] = 10;

    data[1] = 20;

    printf("data[0] = %d ", data[0]);

    printf("data[1] = %d ", data[1]);

    // open libplugin.so

    void *handle = dlopen("./libplugin.so", RTLD_NOW);

    if (!handle)

  {

       printf("dlopen failed. ");

       return;

    }

    // get and call init function in libplugin.so

    pfunc_init func_init =  (pfunc_init) dlsym(handle, "func_init");

    if (!func_init)

  {

       printf("get func_init failed. ");

       return;

    }

    func_init(data);

    // get and call routine function in libplugin.so

    pfunc_stage1 func_stage1 =  (pfunc_stage1) dlsym(handle, "func_stage1");

    if (!func_stage1)

  {

    printf("get func_stage1 failed. ");

    return;

    }

    func_stage1();

    printf("data[0] = %d ", data[0]);

    printf("data[1] = %d ", data[1]);

    

    return;

编译指令如下:

gcc -m32 -fPIC --shared plugin.c -o libplugin.so

gcc -m32 -o main main.c -ldl

执行结果:

data[0] = 10 

data[1] = 20 

libplugin.so: func_init is called. 

libplugin.so: func_stage1 is called. 

data[0] = 100 

data[1] = 200

可以看一下动态链接库的符号表:

readelf -s libplugin.so | grep data

可以看到alias_data标识符,并且是在本文件中定义的全局变量。

【关于作者】

号主:道哥,十多年的嵌入式开发老兵,专注于嵌入式 + Linux 领域,玩过单片机、搞过智能家居、研究过 PLC工业机器人,项目开发经验非常丰富。

他的文章主要包括 C/C++、Linux操作系统、物联网、单片机和嵌入式这几个方面。

厚积薄发、换位思考,以读者的角度来总结文章。

每一篇输出,不仅仅是干货的呈现,更是引导你一步一步的深入思考,从底层逻辑来提升自己。

方法2:嵌入汇编代码

在动态加载的插件中使用变量别名,除了上面演示的动态注册的方式,还可以通过嵌入汇编代码来: 设置一个全局标号来实现。

直接上示例代码:

plugin.c源文件#include <stdio.h>


asm(".Global alias_data");

asm("alias_data = data");


extern int alias_data[];


void func_stage1(void)



      printf("libplugin.so: func_stage1 is called. ");


     *(alias_data + 0) = 100;
            *(alias_data + 1) = 200;



main.c源文件

#include <stdio.h>

#include <stdlib.h>

#include <dlfcn.h>

// defined in libplugin.so

typedef void (*pfunc_init)(int *);

typedef void (*pfunc_stage1)(void);

int data[100] = { 0 };

void main(void)

      data[0] = 10;

      data[1] = 20;


      printf("data[0] = %d ", data[0]);

      printf("data[1] = %d ", data[1]);

      // open libplugin.so

      void *handle = dlopen("./libplugin.so", RTLD_NOW);

      if (!handle)

    {

          printf("dlopen failed. ");

          return;

      }

     // get and call routine function in libplugin.so
pfunc_stage1 func_stage1 =  (pfunc_stage1) dlsym(handle, "func_stage1");

      if (!func_stage1)

    {

          printf("get func_stage1 failed. ");

          return;

      }

      func_stage1();

 

      printf("data[0] = %d ", data[0]);

      printf("data[1] = %d ", data[1]);


      return;

编译指令:

gcc -m32 -fPIC --shared plugin.c -o libplugin.so

gcc -m32 -rdynamic -o main main.c -ldl

执行结果:

data[0] = 10 

data[1] = 20 

libplugin.so: func_stage1 is called. 

data[0] = 100 

data[1] = 200

也来看一下libplugin.so中的符号信息:

readelf -s libplugin.so | grep data

小结

这篇文档通过两个示例代码,讨论了如何在插件中(动态链接库),通过别名来访问真正的变量。

不知道您会不会有这样的疑问:直接使用extern来声明一下外部定义的变量不就可以了,何必这么麻烦?

道理是没错!

但是,在一些比较特殊的领域或场景中(比如一些二次开发中),这样的需求是的确存在的,而且是强需求。

如果你有任何疑问,或者文中有任务错误,欢迎留言讨论、指正。


声明:本文为OFweek维科号作者发布,不代表OFweek维科号立场。如有侵权或其他问题,请及时联系我们举报。
2
评论

评论

    相关阅读

    暂无数据

    道哥分享

    专注嵌入式软件开发,越分享、越幸...

    举报文章问题

    ×
    • 营销广告
    • 重复、旧闻
    • 格式问题
    • 低俗
    • 标题夸张
    • 与事实不符
    • 疑似抄袭
    • 我有话要说
    确定 取消

    举报评论问题

    ×
    • 淫秽色情
    • 营销广告
    • 恶意攻击谩骂
    • 我要吐槽
    确定 取消

    用户登录×

    请输入用户名/手机/邮箱

    请输入密码