13.8 Некоторые изменения, которые мы не хотим вносить
В этом разделе перечислены изменения, которые часто запрашивают пользователи, но мы не вносим их, так как считаем, что GCC работает лучше без них.
- Проверка количества и типа аргументов функции, имеющей устаревшее определение и без прототипа.
Такая функция будет работать только в некоторых случаях — только для вызовов, которые находятся в том же файле, что и вызываемая функция, после определения. Единственный способ надежно проверить все вызовы — добавить прототип для функции. Но добавление прототипа устраняет мотивацию для этой функции. Поэтому эта функция не стоит усилий.
- Предупреждение об использовании выражения, тип которого является знаковым, в качестве сдвига.
Операнды сдвига, скорее всего, являются знаковыми чаще, чем беззнаковыми. Предупреждение об этом вызовет гораздо больше проблем, чем пользы.
- Предупреждение об присваивании знакового значения беззнаковой переменной.
Такие присваивания, скорее всего, очень распространены; предупреждение об них вызовет больше проблем, чем пользы.
- Предупреждение, когда значение ненулевой функции игнорируется.
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 удаляет операции из циклов, когда может определить, что эти операции не видны извне (кроме, конечно, времени, затрачиваемого на их выполнение).
Помните об этом при проведении тестов времени выполнения, например, следующий цикл может быть полностью удалён, при условии, что
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, которая превратит эти предупреждения в ошибки.
Далее: Предупреждения и ошибки, Предыдущее: Непонимание C++, Вверх: Проблемы [Оглавление][Индекс]
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-7.5.0/gcc/Non-bugs.html