Инициализация массива указателей - PullRequest
2 голосов
/ 19 октября 2010

Я столкнулся с проблемой инициализации массива с указателями на элементы структуры. Члены структуры должны быть доступны через указатель структуры. Причина этого заключается в том, что мы инициализируем указатель во время выполнения для расположения адреса в памяти. Следующий фрагмент кода является примером проблемы:

#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 ]

3 голосов
/ 19 октября 2010
T t[] =
{
    { &f->l }
};

Адрес элемента (например, & f-> l) известен только во время выполнения.

Такое значение нельзя использовать для инициализации во время компиляции (что здесь и делается).

1 голос
/ 19 октября 2010

Массив 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;
 }  
}
0 голосов
/ 19 октября 2010

Предположим, что вы можете использовать неконстантные данные для инициализации глобального: у вас все еще есть огромная проблема.

Когда инициализируется t, f все еще имеет неопределенное значение: это происходит перед init () выполняет и назначает ваш магический адрес.Из-за этого, даже если бы вы могли использовать &f->l, вам все равно пришлось бы сбросить все места, где он использовался.

0 голосов
/ 19 октября 2010

С технической точки зрения, для компилятора C90 это невозможно.Для идиомы инициализации

декларатор = последовательность инициализации

последовательность инициализации должна быть константным выражением, т.е.во время компиляции или во время компоновки.Таким образом,

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

работает, в то время как

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

не будет, потому что адрес автоматического а не может быть вычислен до времени выполнения., последний будет работать.Ваш код, однако, все еще находится за пределами того, что компилятор C99 может предварительно вычислить.Если вы переключитесь на C ++, ваш код будет работать, по крайней мере Comeau не возражает.Редактировать: конечно, Роджер прав в том, что это не решит проблему неправильной разыменования через указатель NULL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...