【问题标题】:C extern pointer to function pointer指向函数指针的 C 外部指针
【发布时间】:2013-11-17 14:36:50
【问题描述】:

我正在编写自己的初始化代码,它应该遍历一组全局构造函数并调用它们。指向这些构造函数的指针存储在 .init_array 部分中。我的代码如下所示:

extern void (**_init_array_start)();
extern void (**_init_array_end)();


void _init()
{
    void (**ctor)();
    for (ctor = _init_array_start ; ctor != _init_array_end ; ctor++) {
        (*ctor)();
    }
}

外部在链接描述文件中定义,如下所示:

.init_array :
{
    . = ALIGN(4);
    _init_array_start = .;
    KEEP(*(SORT(.init_array.*)))
    KEEP(*(.init_array))
    . = ALIGN(4);
    _init_array_end = .;
} >rom

但是,最终的汇编代码如下所示:

00000010 <_init>:
  10:   b538        push    {r3, r4, r5, lr}
  12:   4b05        ldr r3, [pc, #20]   ; (28 <_init+0x18>)
  14:   4d05        ldr r5, [pc, #20]   ; (2c <_init+0x1c>)
  16:   681c        ldr r4, [r3, #0]
  18:   682b        ldr r3, [r5, #0]
  1a:   429c        cmp r4, r3
  1c:   d003        beq.n   26 <_init+0x16>
  1e:   f854 3b04   ldr.w   r3, [r4], #4
  22:   4798        blx r3
  24:   e7f8        b.n 18 <_init+0x8>
  26:   bd38        pop {r3, r4, r5, pc}
  28:   00000144    andeq   r0, r0, r4, asr #2
  2c:   00000150    andeq   r0, r0, r0, asr r1

现在,这里有趣的部分是 0x16 和 0x18。这两条指令在循环头中取消引用 _init_array_start 和 _init_array_end。这不是我的本意。我想处理指向函数指针的指针,而不是它们的值(即直接的函数指针)。 现在,当我将 extern 声明更改为以下内容时,两个取消引用指令神奇地消失了:

extern void (*_init_array_start[0])();
extern void (*_init_array_end[0])();

那么,为什么编译器首先取消引用指针,为什么它不使用数组语法这样做呢?这两种语法不应该是等价的吗?

【问题讨论】:

    标签: c arrays pointers assembly function-pointers


    【解决方案1】:

    符号(几乎)总是地址,而指针与数组不同。

    extern int* i; 表示“符号'i'是一个int*的地址。” i 将表示“由符号'i'指定的地址处的值”,这是一个指针取消引用。 &amp;i 表示“符号‘i’指定的地址”,不是。

    对于数组,“extern int a[42];”表示“符号'a'是42个整数数组的地址。”当一个数组名在 C 中单独使用时,它指的是数组的地址,所以这里的a 相当于前面例子中的&amp;i&amp;a 也指向同一个地址,但类型不同。两者都具有由符号“a”指定的地址。

    如果您不想使用数组语法,则将extern void (**_init_array_start)(); 更改为extern void (*_init_array_start)(); 并将_init_array_start 更改为&amp;_init_array_start,您发布的第一个代码应该可以工作。

    编辑:另一种看待这一点的方式是 _init_array_start 不是指向第一个初始化程序的指针——它 第一个初始化程序——因此将其声明为指向函数指针的指针是错误的。数组语法将其声明为函数指针数组,实际上就是这样。

    【讨论】:

      【解决方案2】:

      _init_array_start 是存储指向构造函数的第一个指针的地址。您必须从该变量中获取地址并迭代到_init_array_end 的地址。当您考虑到这一点时,您将不得不调整指针类型以适应这种方法。

      【讨论】:

        【解决方案3】:

        这是一个指向指针类型的对象的声明:

        extern void (**_init_array_start)();
        

        这是与数组开头关联的符号名称的定义:

        _init_array_start = .;
        

        但是你没有为指针类型的指针分配存储空间,只有数组。

        听起来您可能希望以这种方式处理 C 端:

        extern void (*_init_array_start)();
        extern void (*_init_array_end)(); // One past the end; not a real object.
        
        void _init()
        {
            void (**ctor)();
            for (ctor = &_init_array_start ; ctor != &_init_array_end ; ctor++) {
                (*ctor)();
            }
        }
        

        生成一个全局整数值来表示数组的大小并从 C 中访问它可能会更简洁,尽管这意味着为所述整数分配存储空间。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-06-14
          • 1970-01-01
          • 2018-07-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多