Могу ли я статически инициализировать char * в структуре в C? - PullRequest
3 голосов
/ 30 апреля 2011

Есть ли способ сделать что-то подобное в C?

char* str[] = { "abc" };
struct test { char* str_in_struct; } tests[] = {
  { str[0] }
};

Если я попытаюсь скомпилировать это, gcc скажет:

main.c:6: error: initializer element is not constant
main.c:6: error: (near initialization for ‘tests[0].str_in_struct’)
main.c:6: warning: missing initializer
main.c:6: warning: (near initialization for ‘tests[0].str_in_struct’)

1 Ответ

4 голосов
/ 30 апреля 2011

Существует два контекста, в которых этот код может появляться - внутри функции и вне функции.

  • Код действителен в одном контексте - внутри функции.
  • Код недопустим в другом контексте - вне функции.

Это, вероятно, объясняет расходящиеся представления о том, принимает ли компилятор код.

С учетом кода:

char* str[] = { "abc" };
struct test { char* str_in_struct; } tests[] = { { str[0] } };

void somefunc(void)
{ 
    char* str[] = { "abc" };
    struct test tests[] = { { str[0] } };
}

компиляция (с GCC 4.1.2 на MacOS X 10.6.7) дает:

$ /usr/bin/gcc -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -c xx.c
xx.c:2: error: initializer element is not constant
xx.c:2: error: (near initialization for ‘tests[0].str_in_struct’)
xx.c:2: warning: missing initializer
xx.c:2: warning: (near initialization for ‘tests[0].str_in_struct’)
xx.c:5: warning: no previous prototype for ‘somefunc’
xx.c: In function ‘somefunc’:
xx.c:7: warning: unused variable ‘tests’
$

Предупреждение для строк 5 и 7 довольно точное; ошибки в строке 2 тоже точны.

В чем проблема?

По сути, str[0] требует вычисления, которое компоновщик не может (или не обязан) делать.

В этой редакции кода test2 в порядке, но tests и test3 не являются:

struct test { char* str_in_struct; };
char *str[] = { "abc" };
char  pqr[]  =   "abc";
char *xyz    =   "abc";
struct test tests[] = { { str[0] } };
struct test test2[] = { { pqr } };
struct test test3[] = { { xyz } };

void somefunc(void)
{
    char* str[] = { "abc" };
    struct test tests[] = { { str[0] } };
}

Вы можете ссылаться на имена массивов во внешних инициализаторах. Вы не можете ссылаться на элементы массива и не можете ссылаться на значение переменной-указателя.


Перефразируя комментарий / вопрос:

Кажется, что если char str4[][4] = { "abc", "d" };, то struct test { char* str_in_struct; } test4[] = { { str4[1] } }; допустимо. Итак, вы можете ссылаться на элементы массива, но только если их размер известен?

Я не совсем правильно охарактеризовал это - вы правы. Я дал часть ответа, но не полный ответ. По сути, выражения в инициализаторах «вне функции» должны быть константами. Это не так просто, как «только если размер известен». Вопрос в том, можно ли вычислить выражение в инициализаторе без чтения значения из памяти.

При str[0] (оригинальная версия) вы должны прочитать значение, хранящееся в str[0]; аналогично с xyz. В версии pqr и версии str4 (обратите внимание на дополнительные 4 по сравнению с комментарием), значение (адрес) pqr или str4[1] вычисляется компоновщиком без считывания значения, хранящегося там.

В стандарте C99, §6.7.8 Инициализация гласит:

¶4 Все выражения в инициализаторе для объекта со статической продолжительностью хранения должны быть константные выражения или строковые литералы.

§6.6. Постоянные выражения говорят:

¶2 Постоянное выражение может быть оценено во время перевода, а не во время выполнения, и соответственно может использоваться в любом месте, где константа может быть.

и

¶7 Допускается больше широты для константных выражений в инициализаторах. Такая постоянная Выражение должно быть или оценивать одно из следующего:

- выражение арифметической константы,
- константа нулевого указателя,
- адресная константа или
- константа адреса для типа объекта плюс или минус целочисленное константное выражение.

¶8 Выражение арифметической константы должно иметь арифметический тип и иметь только операнды, которые являются целочисленными константами, плавающими константами, константами перечисления, символом константы и sizeof выражения. Операторы приведения в выражении арифметической константы должен преобразовывать только арифметические типы в арифметические типы, кроме как как часть операнда в sizeof оператор, результатом которого является целочисленная константа.

¶9 * Адресная константа - нулевой указатель, указатель на l-значение, обозначающее объект static длительность хранения или указатель на обозначение функции; он должен быть создан явно с использованием унарный оператор & или целочисленная константа, приведенная к типу указателя, или неявно с помощью выражение массива или тип функции. Массив-индекс [] и членский доступ . и -> операторы, адрес & и однонаправленные * унарные операторы, а также приведение указателей могут использоваться при создании адресной константы, но значение объекта не должно быть доступ с помощью этих операторов.

Обратите внимание наквалификатор ', но значение объекта не должно быть доступно с помощью этих операторов' . Это согласуется с тем, что я написал ранее в этом расширении ответа. В частности, значение str4[1] требует только адресную константу массива str4 плюс целочисленное константное выражение (последняя альтернатива в списке маркеров). Точно так же pqr является адресной константой (третий вариант в списке маркеров). Но инициализаторам str[0] и xyz необходим доступ к значению объекта, что недопустимо.

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