Почему препроцессор C в GCC интерпретирует слово linux
(маленькие буквы) как константу 1
?
test.c:
#include <stdio.h>
int main(void)
{
int linux = 5;
return 0;
}
Результат $ gcc -E test.c
(остановка после этапа предварительной обработки):
....
int main(void)
{
int 1 = 5;
return 0;
}
Что, конечно, приводит к ошибке.
(Кстати: нет #define linux
в stdio.h
файле.)
c
linux
gcc
c-preprocessor
ahmedaly50
источник
источник
#undef linux
или, возможно, использовали другую переменную? Я думаю, что константаlinux
используется для тестирования операционных систем, например, если вы разрабатываете кроссплатформенное приложение и вам нужно точно знать, какой API использовать (windows, mac, linux, BSD и т. Д.). Его нет в stdio.h, но он все равно определен, если ядром является linux. Тот же самый код не должен вызывать ошибки в Windows, но использование чего-то вроде windows или WINDOWS в качестве переменной, вероятно, будет, и наоборотОтветы:
В старые времена (до ANSI) предопределенные символы, такие как
unix
и,vax
позволяли коду определять во время компиляции, для какой системы он компилируется. В то время не было никакого официального языкового стандарта (кроме справочного материала в конце первого издания K & R), и C-код любой сложности, как правило, представлял собой сложный лабиринт#ifdef
s для учета различий между системами. Эти определения макросов обычно устанавливались самим компилятором, а не определялись в заголовочном файле библиотеки. Поскольку не было настоящих правил о том, какие идентификаторы могут использоваться реализацией, а какие зарезервированы для программистов, разработчики компиляторов могут свободно использовать простые имена, такие как,unix
и предполагают, что программисты просто избегают использовать эти имена в своих целях.Стандарт ANSI C 1989 года ввел правила, ограничивающие символы, которые могут быть юридически определены реализацией. Макрос, предопределенный компилятором, может иметь только имя, начинающееся с двух подчеркиваний или со знака подчеркивания, за которым следует заглавная буква, что позволяет программистам использовать идентификаторы, не соответствующие этому шаблону и не используемые в стандартной библиотеке.
В результате любой компилятор, который предопределяет
unix
илиlinux
не соответствует, так как он не сможет скомпилировать совершенно легальный код, который использует что-то вродеint linux = 5;
.Так получилось, что gcc по умолчанию не соответствует стандарту, но его можно сделать так, чтобы он соответствовал (достаточно хорошо) нужным параметрам командной строки:
Смотрите руководство gcc для более подробной информации.
gcc будет постепенно отменять эти определения в будущих выпусках, поэтому вы не должны писать код, который зависит от них. Если ваша программа должна знать, компилируется ли она для цели Linux или нет, она может проверить,
__linux__
определена ли она (при условии, что вы используете gcc или совместимый с ним компилятор). См. Руководство препроцессора GNU C для получения дополнительной информации.В значительной степени это не имеет значения: победитель «Best One Liner» Международного конкурса по кодированию кода C в 1987 году Дэвид Корн (да, автор Korn Shell) воспользовался предопределенным
unix
макросом:Он печатает
"unix"
, но по причинам, которые не имеют абсолютно никакого отношения к написанию имени макроса.источник
unix
иvax
» - а? Насколько я понимаю, в старые времена весь мир был такимvax
!unix
определением). Я объяснил, почему в комментарии к этой сути, если вам или кому-то еще это интересно.Похоже, это (недокументированное) «расширение GNU»: [ исправление : я наконец-то нашел упоминание в документации. См. ниже.]
Следующая команда использует
-dM
опцию для печати всех определений препроцессора; так как входной «файл» пуст, он показывает именно предопределенные макросы. Он был запущен с gcc-4.7.3 при стандартной установке Ubuntu. Вы можете видеть, что препроцессор поддерживает стандарт. Всего существует 243 макроса с-std=gnu99
и 240 с-std=c99
; Я отфильтровал выходные данные по релевантности.Версии "GNU Standard" также
#define unix
. (Использованиеc11
иgnu11
дает те же результаты.)Я предполагаю, что у них были свои причины, но мне кажется, что установка gcc по умолчанию (которая компилирует код C,
-std=gnu89
если не указано иное) не соответствует требованиям, и - как в этом вопросе - удивляет. Загрязнение глобального пространства имен макросами, имена которых не начинаются с подчеркивания, не допускается в соответствующей реализации. (6.8.10p2: «Любые другие предварительно определенные имена макросов должны начинаться с начального подчеркивания, за которым следует заглавная буква или второе подчеркивание», но, как упоминалось в Приложении J.5 (проблемы переносимости), такие имена часто предопределены.)Когда я первоначально написал этот ответ, я не смог найти в gcc никакой документации по этой проблеме, но я наконец-то обнаружил ее не в поведении, определяемом реализацией C, ни в расширениях C, а в разделе
cpp
руководства 3.7.3 , где он отмечает, что:источник
-std=gnu89
это значение по умолчанию, и, насколько я знаю, это значение по умолчанию для соляриса, Linux и Mac OS X ( developer.apple.com/library/mac/documentation/Darwin/Reference/… для последнего), и это тот, который загрязняет пространство имен.c89
действительно пытается соответствовать стандарту, поэтому, если бы это было по умолчанию, у меня не было бы жалоб.Потому что
linux
что это встроенный макрос, определенный, когда компилятор работает или компилируется (если это кросс-компилятор) для Linux.Существует много таких предопределенных макросов. С GCC вы можете использовать:
чтобы получить список макросов. (Мне не удалось убедить GCC принять
/dev/null
напрямую, но кажется, что пустой файл работает нормально.) Когда GCC 4.8.1 работает на Mac OS X 10.8.5, я получил вывод:Это 236 макросов из пустого файла. Когда я добавил
#include <stdio.h>
в файл, число определенных макросов возросло до 505. Они включают в себя все виды макросов, определяющих платформу.источник
cpp -dM < /dev/null
linux
) не определен на моей машине Mac OS X - он не работает под управлением Linux (ну, есть виртуальная машина под управлением Linux, но ...). Это может быть определено заголовками, включенными<stdio.h>
; запуск пустого файла может не совпадать./dev/null
как исходный файл, так-x
как он не имеет расширения, поэтому gcc не знает, является ли он C или C ++. Вы можете использоватьgcc -E -dM -x c /dev/null
или,gcc -E -dm -x c++ /dev/null
чтобы получить список, не создавая пустой файл.cpp
(препроцессор) нетgcc
. Отметим также , я не даю его в/dev/null
качестве входного файла, а я использую переадресацию , чтобы сделать/dev/null
вstdin
. Также @KerrekSB: на моем компьютере с Debianlinux
есть в списке.> emptyfile.c
или: > emptyfile.c
илиdd if=/etc/passwd of=emptyfile.c count=0
или ...С
info gcc
(акцент мой):(В примере используется vax вместо linux потому что когда он был написан, возможно, он был более популярным ;-).
Основная идея заключается в том, что GCC пытается полностью соответствовать стандартам ISO только тогда, когда он вызывается с
-ansi
опцией.источник
-ansi
илиstd=cXX
, гдеXX
это89
,90
,99
или11
, и либо-pedantic
или-pedantic-errors
. Даже это упрощение; см. руководство для деталей.Используйте эту команду
чтобы получить это
источник