指针数组的初始化

时间:2010-10-19 08:10:47

标签: c arrays pointers

我在使用指向结构成员的指针初始化数组时遇到了问题。必须通过结构指针访问结构成员。原因是我们在运行时将指针初始化为内存映射地址位置。以下代码段是问题的一个示例;

#include <stdio.h>
#include <stdlib.h>

typedef struct
{
  long* lp;
}T;

typedef struct
{
  long l;
}F;

F* f;

T t[] =
{
    { &f->l }
};

void init (void)
{
  f = (F*) 0x08000100; 
}

int main (void)
{
  init();

  return EXIT_SUCCESS;
}

编译器输出如下;

gcc -O0 -g3 -Wall -c
-fmessage-length=0 -osrc\Test.o ..\src\Test.c ..\src\Test.c:18: 
error: initializer element is not constant
..\src\Test.c:18: error: (near initialization for `t[0].lp')
..\src\Test.c:18: error: initializer element is not constant
..\src\Test.c:18: error: (near initialization for `t[0]') 
Build error occurred, build is stopped

这里的问题是我们在运行时初始化指针,编译器不知道它在哪里可以找到结构成员。我们不能解决结构指针,因为我们不想使用链接描述文件。

如何解决这个问题?

4 个答案:

答案 0 :(得分:3)

T t[] =
{
    { &f->l }
};

元素的地址(例如&amp; f-&gt; l)仅在运行时才知道。

这样的值不能用于编译时初始化(这是在这里完成的)。

答案 1 :(得分:1)

在运行时之前无法填充t []数组 - 因为直到运行时才知道F的地址。

您可以将T []初始化为{NULL}并在post-init中对其进行修补。

另一种方法是将T的成员初始化为仅仅是结构中的偏移量,并在初始化f之后,遍历数组并通过添加f的地址来调整指针位置。这种技术类似于链接中常用的技术。

这样的事情:

#define MEMBER_OFFSET_OF(a,b) &(((a*)0)->b)

T t[] = 
{
   {(long*)MEMBER_OFFSET_OF(F, l)}
};
const int numElementsInT = sizeof(t) / sizeof(t[0]);

void init()
{
 f = (F*) 0x08000100; 
 for (int i= 0; i < numElementsInT; i++)
 {
   t[i].lp += (unsigned int)f;
 }  
}

答案 2 :(得分:0)

从技术上讲,对于C90编译器来说,没有办法解决这个问题。对于初始化习语,

声明者 = 初始化序列

初始化序列需要是一个常量表达式,即可以在编译时或链接时计算的表达式。所以,

int a;
int *b[] = { &a };

有效,而

void foo() {
    int a;
    int *b[] = { &a };
}

不会,因为自动a的地址在运行之前是不可计算的。

如果切换到C99,后者将起作用。但是,您的代码仍然超出了C99编译器可以预先计算的范围。如果切换到C ++,您的代码将起作用,至少Comeau不会反对。 编辑:当然Roger是正确的,因为这不能解决你通过NULL指针进行错误解除引用的问题。

答案 3 :(得分:0)

让我们假设你可以使用非常量数据来初始化全局:你仍然有一个很大的问题。

当初始化t时,f仍然具有不确定的值:在 init()执行之前发生并分配你的魔术地址。因此,即使您可以使用&f->l,也必须重置所有使用过的地方。