15.3 Несовместимости GCC
Между GNU C и версиями C по K&R (не ISO) есть несколько заслуживающих внимания несовместимостей.
-
GCC обычно делает строковые константы доступными только для чтения. Если используется несколько одинаковых строковых констант, GCC сохраняет только одну копию строки.
Одно из следствий этого — нельзя вызывать
mktempс аргументом-строковой константой. Функцияmktempвсегда изменяет строку, на которую указывает её аргумент.Другое следствие:
sscanfне работает в некоторых очень старых системах, если в качестве управляющей строки формата или входных данных передать строковую константу. Это происходит потому, чтоsscanfошибочно пытается записать данные в строковую константу. То же относится кfscanfиscanf.Чтобы устранить эти проблемы, нужно изменить программу и вместо строковых констант использовать для этих целей переменные-массивы
char, инициализированные строками. -
-2147483648является положительным.Это происходит потому, что 2147483648 не помещается в тип
int, поэтому (согласно правилам ISO C) его тип данных —unsigned long int. Отрицание этого значения снова даёт 2147483648. - GCC не подставляет аргументы макроса, когда они встречаются внутри строковых констант. Например, следующий макрос в GCC
#define foo(a) "a"
выведет
"a"независимо от значения аргумента a. -
При использовании
setjmpиlongjmpединственные автоматические переменные, сохранность значений которых гарантируется, — это переменные, объявленныеvolatile. Это следствие автоматического распределения регистров. Рассмотрим эту функцию:jmp_buf j; foo () { int a, b; a = fun1 (); if (setjmp (j)) return a; a = fun2 (); /*longjmp (j)may occur infun3. */ return a + fun3 (); }Здесь значение
aможет быть восстановлено или не восстановлено до первоначального при возникновенииlongjmp. Еслиaразмещена в регистре, её первоначальное значение будет восстановлено; в противном случае в ней останется последнее сохранённое значение.Если использовать параметр -W вместе с параметром -O, GCC выдаст предупреждение, если сочтёт такую проблему возможной.
- Программы, в которых директивы препроцессора используются внутри аргументов макроса, не работают с GCC. Например, такая программа работать не будет:
foobar ( #define luser hack)Стандарт ISO C не допускает такие конструкции.
- Компиляторы K&R допускают, чтобы комментарии пересекали границу включения (то есть начинались во включаемом файле, а заканчивались в файле, который его включает).
-
Объявления внешних переменных и функций внутри блока действуют только в пределах блока, содержащего объявление. Иными словами, они имеют ту же область видимости, что и любые другие объявления в том же месте.
В некоторых других компиляторах C объявление
externвлияет на весь остаток файла, даже если оно находится внутри блока. - В традиционном C можно сочетать
longи другие спецификаторы с именем типа typedef, как показано здесь:typedef int foo; typedef long foo bar;
В ISO C это запрещено:
longи другие модификаторы типа требуют явногоint. - PCC разрешает использовать имена типов typedef в качестве параметров функции.
- Традиционный C допускает совместное появление в одной области видимости следующих ошибочных объявлений:
typedef int foo; typedef foo foo;
- GCC учитывает все символы идентификаторов. Согласно K&R-1 (2.2), «Значимыми являются не более первых восьми символов, хотя можно использовать и больше». Кроме того, согласно K&R-1 (2.2), «Идентификатор представляет собой последовательность букв и цифр; первым символом должна быть буква. Символ подчёркивания _ считается буквой», однако GCC также разрешает использовать в идентификаторах знак доллара.
- PCC допускает пробелы внутри составных операторов присваивания, таких как ‘+=’. GCC, следуя стандарту ISO, этого не допускает.
-
GCC сообщает об ошибке при незавершённых символьных константах внутри условных конструкций препроцессора, условие которых ложно. В некоторых программах английские комментарии заключены в условные конструкции, которые гарантированно не выполняются; если в таких комментариях есть апострофы, GCC, скорее всего, сообщит об ошибке. Например, этот код вызовет ошибку:
#if 0 You can't expect this to work. #endif
Лучшее решение такой проблемы — поместить текст в настоящий комментарий C, ограниченный символами ‘/*…*/’.
- Во многих пользовательских программах есть объявление ‘long time ();’. Раньше системные заголовочные файлы во многих системах фактически не объявляли
time, поэтому не имело значения, какой тип возвращаемого значения указывала программа. Однако в системах с заголовочными файлами ISO C функцияtimeобъявлена как возвращающаяtime_t, и если этот тип не совпадает сlong, объявление ‘long time ();’ ошибочно.Решение — изменить программу: подключить соответствующие системные заголовочные файлы (
<time.h>в системах с заголовочными файлами ISO C) и не объявлятьtime, если это уже сделано в системных заголовочных файлах; в противном случае использоватьtime_tв качестве типа возвращаемого значенияtime. -
При компиляции функций, возвращающих
float, PCC преобразует его в double. GCC фактически возвращаетfloat. Если важна совместимость с PCC, следует объявлять возвращаемый тип функций какdouble; так вы хотя бы укажете тип, который действительно имеете в виду. -
При компиляции функций, возвращающих структуры или объединения, GCC обычно генерирует код, используя метод, отличающийся от метода большинства версий Unix. В результате код, скомпилированный GCC, не может вызывать функцию, возвращающую структуру и скомпилированную PCC, и наоборот.
GCC использует следующий метод: структура или объединение размером 1, 2, 4 или 8 байт возвращается как скалярное значение. Структура или объединение любого другого размера записывается по адресу, предоставленному вызывающей стороной (обычно в специальный фиксированный регистр, но на некоторых машинах адрес передаётся в стеке). Целевая точка настройки
TARGET_STRUCT_VALUE_RTXсообщает GCC, куда передавать этот адрес.В отличие от этого, PCC на большинстве целевых машин возвращает структуры и объединения любого размера, копируя данные в область статической памяти, а затем возвращая адрес этой области как значение указателя. Вызывающая сторона должна скопировать данные из этой области памяти в место, где требуется значение. GCC не использует этот метод, поскольку он медленнее и не поддерживает повторный вход.
На некоторых более новых машинах PCC использует соглашение, поддерживающее повторный вход, для возврата всех структур и объединений. GCC на большинстве таких машин использует совместимое соглашение при возврате структур и объединений через память, но по-прежнему возвращает небольшие структуры и объединения в регистрах.
Чтобы указать GCC использовать совместимое соглашение для возврата всех структур и объединений, задайте параметр -fpcc-struct-return.
-
GCC сообщает об ошибке для фрагментов программы, таких как ‘0x74ae-0x4000’, которые выглядят как две шестнадцатеричные константы, разделённые оператором вычитания. На самом деле эта строка является одним токеном препроцессора. Каждый такой токен должен соответствовать одному токену C. Поскольку здесь это не так, GCC выводит сообщение об ошибке. Хотя может показаться очевидным, что подразумеваются оператор и два значения, стандарт ISO C прямо требует считать такую запись ошибочной.
Токен препроцессора является числом препроцессора, если он начинается с цифры, за которой следуют буквы, символы подчёркивания, цифры, точки и последовательности символов ‘e+’, ‘e-’, ‘E+’, ‘E-’, ‘p+’, ‘p-’, ‘P+’ или ‘P-’. (В строгом режиме C90 последовательности ‘p+’, ‘p-’, ‘P+’ и ‘P-’ не могут встречаться в числах препроцессора.)
Чтобы приведённый выше фрагмент программы стал допустимым, поставьте пробел перед знаком минус. Этот пробел завершит число препроцессора.
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-15.3.0/gcc/Incompatibilities.html