C指向函数指针的extern指针

时间:2013-11-17 14:36:51

标签: c arrays pointers assembly function-pointers

我正在编写自己的init代码,该代码应遍历全局构造函数数组并调用它们。指向这些构造函数的指针存储在.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)();
    }
}

externals在链接器脚本中定义如下:

.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])();

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

3 个答案:

答案 0 :(得分:1)

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

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

在数组的情况下,“extern int a [42];”表示“符号'a'是42个整数的数组的地址。”当数组名在C中单独使用时,它引用数组的地址,因此a在此处等同于前一个示例中的&i&a也指向相同的地址,但具有不同的类型。两者都具有由符号'a'指定的地址。

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

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

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访问它可能会更清晰,尽管这意味着为所述整数分配存储空间。