简体   繁体   English

指针数组的初始化

[英]Initialization of a pointer array

I'm facing a problem initializing an array with pointers to members of a structure. 我在用指向结构成员的指针初始化数组时遇到问题。 The structure members have to be accessed through a structure pointer. 必须通过结构指针访问结构成员。 The reason for this is we initialize the pointer at runtime to a memory mapped address location. 这样做的原因是我们在运行时初始化了指向内存映射地址位置的指针。 The following code snippet is an example of the problem; 以下代码段是问题的一个示例;

#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;
}

The compiler output is the following; 编译器输出如下:

\ngcc -O0 -g3 -Wall -c gcc -O0 -g3 -Wall -c\n-fmessage-length=0 -osrc\\Test.o ..\\src\\Test.c ..\\src\\Test.c:18: -fmessage-length = 0 -osrc \\ Test.o .. \\ src \\ Test.c .. \\ src \\ Test.c:18: \nerror: initializer element is not constant 错误:初始化元素不是恒定的\n..\\src\\Test.c:18: error: (near initialization for `t[0].lp') .. \\ src \\ Test.c:18:错误:(在“ t [0] .lp”的初始化附近)\n..\\src\\Test.c:18: error: initializer element is not constant .. \\ src \\ Test.c:18:错误:初始化元素不是恒定的\n..\\src\\Test.c:18: error: (near initialization for `t[0]') .. \\ src \\ Test.c:18:错误:(在t [0]的初始化附近) \nBuild error occurred, build is stopped 发生构建错误,构建已停止\n

The problem here is we initialize the pointer at runtime, the compiler doesn't know where it can find the structure members. 这里的问题是我们在运行时初始化指针,编译器不知道在哪里可以找到结构成员。 We cannot work around the structure pointer as we don't wan't to use the linker script for this. 我们无法解决结构指针,因为我们不想为此使用链接程序脚本。

Any ideas how to get around this one? 任何想法如何解决这个问题?

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

The address of an element (eg &f->l) is only known at run-time. 元素的地址(例如&f-> l)仅在运行时知道。

Such a value cannot be used for compile-time initialization (which is what's being done here). 这样的值不能用于编译时初始化(这是在此处进行的操作)。

The t[] array cannot be filled out until runtime - because the address of F isn't known until runtime. t []数组要等到运行时才能填写-因为直到运行时才知道F的地址。

You could initialize T[] to {NULL} and patch it in post-init. 您可以将T []初始化为{NULL}并在初始化后对其进行修补。

Another approach is to initialize the members of T to just simply be the offset within the structure, and after you init f, to walk through the array and adjust the pointer locations by adding the address of f. 另一种方法是将T的成员初始化为只是结构内的偏移量,然后在初始化f之后,遍历数组并通过添加f的地址来调整指针位置。 This technique is similar to what is often used in linking. 此技术类似于链接中经常使用的技术。

Something like this: 像这样:

#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;
 }  
}

Technically speaking for a C90 compiler there is no way around this. 从技术上讲,对于C90编译器而言,这是无法解决的。 For the initialization idiom, 对于初始化习语,

declarator = initialization sequence 声明符 = 初始化序列

the initialization sequence needs to be a constant expression, ie one which can be computed at compile-time or at link-time. 初始化序列必须是一个常量表达式,即可以在编译时或链接时计算的一个常量表达式。 So, 所以,

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

works, while 工作,而

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

will not because the address of the automatic a isn't computable before runtime. 不会因为在运行时之前无法计算自动地址a的地址。

If you switch to C99, the latter will work. 如果切换到C99,则后者可以使用。 Your code however still is beyond what a C99 compiler can precompute. 但是,您的代码仍然超出了C99编译器可以预计算的范围。 If you switch to C++ your code would work, at least Comeau doesn't object. 如果切换到C ++,您的代码将正常运行,至少Comeau不反对。 Edit: of course Roger is correct in that this doesn't solve your problem of having an incorrect dereferencing through a NULL pointer. 编辑:Roger当然是正确的,因为这不能解决通过NULL指针进行不正确的取消引用的问题。

Lets imagine that you could use non-constant data to initialize a global: you still have a huge problem. 假设您可以使用非恒定数据初始化全局变量:您仍然遇到很大的问题。

When t is initialized, f still has an indeterminate value: this happens before init() executes and assigns your magic address. 当t初始化时,f仍然具有不确定的值:这发生 init()执行并分配魔术地址之前。 Because of this, even if you could use &f->l , you'd have to reset all places it's been used, anyway. 因此,即使您可以使用&f->l ,也无论如何都必须重置它的所有使用位置。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM