14.3 Несовместимости GCC ¶
Существует несколько примечательных несовместимостей между GNU C и K&R (не-ISO) версиями C.
-
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 Ctimeобъявляется возвращающим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-10.5.0/gcc/Incompatibilities.html