我在使用指向结构成员的指针初始化数组时遇到了问题。必须通过结构指针访问结构成员。原因是我们在运行时将指针初始化为内存映射地址位置。以下代码段是问题的一个示例;
#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
这里的问题是我们在运行时初始化指针,编译器不知道它在哪里可以找到结构成员。我们不能解决结构指针,因为我们不想使用链接描述文件。
如何解决这个问题?
答案 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
,也必须重置所有使用过的地方。