FILE * ..=stdout: элемент инициализатора ошибки не является постоянным

Мой код C был следующим:

[Linux:/si/usr/hrl]vi test.c    

#include <stdio.h>

FILE * hw = stdout;

int main(void)
{
        return 0;
}

Когда я компилирую в SUSE, он делает такую ​​​​ошибку:

[Linux:/si/usr/hrl]cc test.c -o test
test.c:3: error: initializer element is not constant

Я ищу заголовочный файл stdio.h и обнаруживаю, что stdout, похоже, определен как константа. Так почему же выдает ошибку? Кстати, я компилирую тот же код на AIX, результат успешен.


person hrl    schedule 24.02.2016    source источник
comment
Гум. Как stdout определяется в вашей реализации стандартной библиотеки C? Какой это его тип?   -  person Bathsheba    schedule 24.02.2016
comment
Я не выгляжу так внимательно. Может быть, это extern struct _IO_FILE *stdout; /* Standard output stream. */. Кстати, я использую c++ и компилирую, компилируется успешно.   -  person hrl    schedule 24.02.2016
comment
@hrl Этот вопрос очень актуален, поскольку некоторые, вероятно, скажут, что ваш вопрос здесь является его дубликатом: stackoverflow.com/questions/3025050/   -  person Andrew Henle    schedule 24.02.2016


Ответы (1)


Стандарт не требует, чтобы stdin, stdout и stderr были константами.

Проект n1256 для C99 говорит в 7.19.1 Ввод/вывод ‹stdio.h›

Заголовок объявляет ...
...
TMP_MAX
, который расширяется до целочисленного постоянного выражения ...

stderr stdin stdout
которые являются выражениями типа «указатель на ФАЙЛ» ...

(подчеркните мое)

Явно указано, что некоторые другие значения являются постоянными, тогда как для stdin, stdout и stderr ничего не сказано.

Итак, вы должны поместить инициализацию в main:

#include <stdio.h>

FILE * hw;

int main(void)
{
        hw = stdout;
        ...
        return 0;
}

В стандартной библиотеке AIX stdout является константой, но это всего лишь деталь реализации, и на нее нельзя полагаться, так как она может сломаться в любой более новой версии.

Но вы не должны полагаться ни на то, что stdout является переменной (и даже не на lvalue), потому что это также деталь реализации в библиотеке GCC.


Если вы не можете изменить большую часть своего кода и вам просто нужен хак GCC, чтобы сделать его компилируемым, вы можете попробовать использовать расширение gcc constructor attribute.

Выдержка из документации gcc

6.31 Объявление атрибутов функций

В GNU C вы объявляете определенные вещи о функциях, вызываемых в вашей программе, которые помогают компилятору оптимизировать вызовы функций и более тщательно проверять ваш код.

Ключевое слово __attribute__ позволяет указать специальные атрибуты при создании объявления. За этим ключевым словом следует спецификация атрибута в двойных скобках. Следующие атрибуты в настоящее время определены для функций во всех целях: ... ,конструктор, ...
...
Атрибут конструктора вызывает автоматический вызов функции перед выполнением. входит в main()...

Итак, вы можете использовать:

#include <stdio.h>

FILE * hw;

void initHw(void) __attribute__((constructor)) {
    hw = stdout;
}

int main(void)
{
        return 0;
}

Но ОСТОРОЖНО: это расширение gcc, что означает, что ваш код неправильный C.

person Serge Ballesta    schedule 24.02.2016
comment
Я знаю, что ваше редактирование правильное. Но я хочу найти способ успешно скомпилировать его без инициализации в main(). Потому что это были небольшие кусочки программы, которые я должен пересадить в Suse из AIX. Мне трудно изменить реальный код, как вы говорите. - person hrl; 24.02.2016