14.8 Изменения, которые мы не хотим вносить ¶
В этом разделе перечислены изменения, которые часто запрашивают пользователи, но которые мы не вносим, так как считаем, что GCC лучше без них.
- Проверка количества и типов аргументов функции, имеющей устаревшее определение без прототипа.
Такая функция будет работать только в отдельных случаях — только для вызовов, которые появляются в том же файле, что и вызываемая функция, после определения. Единственный способ надёжно проверить все вызовы — добавить прототип функции. Но добавление прототипа устраняет мотивацию для этой функции. Поэтому эта функция не стоит усилий.
- Предупреждение об использовании выражения со знаком в качестве счётчика сдвига.
Операторы счёта сдвига, вероятно, чаще бывают со знаком, чем без знака. Предупреждение об этом вызовет гораздо больше проблем, чем пользы.
- Предупреждение об присваивании значения со знаком беззнаковой переменной.
Такие присваивания должны быть очень распространёнными; предупреждение об этом вызовет больше проблем, чем пользы.
- Предупреждение, когда значение функции, отличной от void, игнорируется.
C содержит много стандартных функций, которые возвращают значение, которое большинство программ предпочитают игнорировать. Один очевидный пример —
printf. Предупреждение об этой практике заставит защищённого программиста загромождать программы десятками преобразований кvoid. Такие преобразования требуются так часто, что они становятся визуальным шумом. Написание этих преобразований становится настолько автоматическим, что они больше не передают полезной информации о намерениях программиста. Для функций, где значение возврата никогда не должно игнорироваться, используйте атрибут функцииwarn_unused_result(см. Объявление атрибутов функций). - Установка -fshort-enums по умолчанию.
Это приведёт к несовместимости структуры хранения с большинством других компиляторов C. И это не кажется очень важным, учитывая, что тот же результат можно получить другими способами. Самый важный случай — когда перечисление-значенное значение находится внутри структуры, и в этом случае вы можете явно указать ширину поля.
- Установка по умолчанию для полей-битов беззнакового типа на некоторых машинах, где «стандарт ABI» предписывает это делать.
Стандарт ISO C оставляет за реализацией право выбора, является ли поле-бит, объявленное как
int, со знаком или без знака. Это фактически создаёт две альтернативные диалекты C.Компилятор GNU C поддерживает оба диалекта; вы можете указать диалект со знаком с помощью -fsigned-bitfields, а диалект без знака с помощью -funsigned-bitfields. Однако это оставляет открытым вопрос, какой диалект использовать по умолчанию.
В настоящее время предпочтительным диалектом являются поля-биты со знаком, так как это проще. Поскольку
intэквивалентноsigned intво всех других контекстах, для них естественно, чтобы они были одинаковыми и в полях-битах.Некоторые производители компьютеров опубликовали стандарты двоичного интерфейса приложений, которые предписывают, что обычные поля-биты должны быть беззнаковыми. Однако ошибочно делать какие-либо заявления об этой проблеме в ABI. Это связано с тем, что обработка обычных полей-битов отличает два диалекта C. Оба диалекта имеют смысл на любом типе машины. То, был ли конкретный объектный файл скомпилирован с полями-битами со знаком или без знака, не имеет значения для других объектных файлов, даже если они обращаются к тем же полям-битам в тех же структурах данных.
Программа написана на одном из этих двух диалектов. Программа имеет шанс работать на большинстве машин, если она скомпилирована с правильным диалектом. Она, вероятно, вообще не будет работать, если скомпилирована с неправильным диалектом.
Многие пользователи ценят компилятор GNU C, потому что он обеспечивает единообразную среду на всех машинах. Эти пользователи испытали бы неудобства, если бы компилятор по-разному обрабатывал обычные поля-биты на определённых машинах.
Иногда пользователи пишут программы, предназначенные только для определённого типа машины. В этих случаях пользователи выиграли бы, если бы компилятор GNU C по умолчанию поддерживал тот же диалект, что и другие компиляторы на этой машине. Но такие приложения редки. И пользователи, пишущие программу для работы на нескольких типах машин, не могут получить выгоду от такого рода совместимости.
Вот почему GCC будет и впредь обрабатывать обычные поля-биты одинаково на всех типах машин (по умолчанию).
Существуют некоторые аргументы в пользу установки по умолчанию беззнакового типа для полей-битов на всех машинах. Если, например, это станет универсальным фактическим стандартом, будет логично, что GCC ему последует. Это следует рассмотреть в будущем.
(Конечно, пользователи, сильно озабоченные переносимостью, должны явно указывать в каждом поле-бите, со знаком оно или без знака. Таким образом, они пишут программы, которые имеют одинаковый смысл в обоих диалектах C.)
- Отмена определения
__STDC__при отсутствии -ansi.В настоящее время GCC определяет
__STDC__безусловно. Это даёт хорошие результаты на практике.Программисты обычно используют условные операторы с
__STDC__, чтобы узнать, безопасно ли использовать определённые функции ISO C, такие как прототипы функций или конкатенацию токенов ISO. Так как обычныйgccподдерживает все функции ISO C, правильный ответ на эти вопросы — «да».Некоторые пользователи пытаются использовать
__STDC__для проверки наличия определённых функций библиотеки. Это неправильное использование в программе ISO C, поскольку стандарт ISO C гласит, что соответствующая реализация самостоятельной среды должна определять__STDC__, даже если у неё нет функций библиотеки. ‘gcc -ansi -pedantic’ — это соответствующая реализация самостоятельной среды, и поэтому она должна определять__STDC__, даже если она не поставляется с библиотекой ISO C.Иногда люди говорят, что определение
__STDC__в компиляторе, который не полностью соответствует стандарту ISO C, каким-то образом нарушает стандарт. Это нелогично. Стандарт — это стандарт для компиляторов, которые утверждают, что поддерживают ISO C, например, ‘gcc -ansi’, а не для других компиляторов, таких как обычныйgcc. То, что говорит стандарт ISO C, относится к проектированию обычногоgccбез -ansi только по прагматическим соображениям, а не как требование.GCC обычно определяет
__STDC__как 1, а также определяет__STRICT_ANSI__, если вы укажете опцию -ansi или опцию -std для строгого соответствия какой-либо версии ISO C. На некоторых хостах системные заголовочные файлы используют другую конвенцию, где__STDC__обычно 0, но 1, если пользователь указывает строгое соответствие стандарту C. GCC следует конвенции хоста при обработке системных заголовочных файлов, но при обработке пользовательских файлов следует обычной конвенции GNU C. - Отмена определения
__STDC__в C++.Программы, написанные для компиляции с помощью трансляторов C++ в C, получают значение
__STDC__, которое соответствует компилятору C, который используется впоследствии. Эти программы должны проверять__STDC__, чтобы определить, какой тип препроцессора C использует компилятор: следует ли конкатенировать токены в стиле ISO C или в традиционном стиле.Эти программы правильно работают с GNU C++ при условии, что
__STDC__определён. В противном случае они не будут работать.Кроме того, многие заголовочные файлы написаны так, чтобы предоставлять прототипы в ISO C, но не в традиционном C. Многие из этих заголовочных файлов могут работать без изменений в C++, при условии, что
__STDC__определён. Если__STDC__не определён, все они потерпят неудачу и все потребуют изменения для явной проверки на C++. - Удаление «пустых» циклов.
Исторически GCC не удалял «пустые» циклы, исходя из предположения, что наиболее вероятная причина их включения в программу — создание задержки, так что их удаление не ускорит реальные программы.
Однако логика заключается в том, что оптимизация непустого цикла не может привести к пустому. Это было справедливо для тщательно написанного C, скомпилированного менее мощными оптимизаторами, но не всегда верно для тщательно написанного C++ или с более мощными оптимизаторами. Таким образом, GCC удаляет операции из циклов всякий раз, когда может определить, что эти операции не видны извне (кроме, конечно, времени, затраченного на их выполнение). В случае, если цикл может быть доказан как конечный, GCC также удалит сам цикл.
Учитывайте это при выполнении тестов на время, например, следующий цикл может быть полностью удалён, при условии, что
some_expressionдоказуемо не изменяет никакого глобального состояния.{ int sum = 0; int ix; for (ix = 0; ix != 10000; ix++) sum += some_expression; }Хотя
sumнакапливается в цикле, сумма не используется, поэтому накопление можно удалить. - Приведение побочных эффектов к тому же порядку, что и в некоторых других компиляторах.
Никогда не безопасно полагаться на порядок вычисления побочных эффектов. Например, вызов функции, подобный этому, может работать по-разному в разных компиляторах:
void func (int, int); int i = 2; func (i++, i++);
Нет гарантии (ни в стандарте языка C, ни в стандарте языка C++), что инкременты будут вычислены в каком-либо определённом порядке. Любой инкремент может произойти первым.
funcможет получить аргументы ‘2, 3’, или ‘3, 2’, или даже ‘2, 2’. - Превращение некоторых предупреждений в ошибки по умолчанию.
Некоторые тестовые наборы ISO C сообщают об ошибке, когда компилятор не выводит сообщение об ошибке для определённой программы.
ISO C требует сообщения «диагностики» для определённых типов некорректных программ, но предупреждение определяется GCC как диагностическое сообщение. Если GCC выводит предупреждение, но не ошибку, это корректная поддержка ISO C. Если тестовые наборы называют это «ошибкой», их следует запускать с опцией GCC -pedantic-errors, которая превратит эти предупреждения в ошибки.
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-11.4.0/gcc/Non-bugs.html