Существует два контекста, в которых этот код может появляться - внутри функции и вне функции.
- Код действителен в одном контексте - внутри функции.
- Код недопустим в другом контексте - вне функции.
Это, вероятно, объясняет расходящиеся представления о том, принимает ли компилятор код.
С учетом кода:
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
необходим доступ к значению объекта, что недопустимо.