Техническая законность несовместимых заданий указателя
В стандарте C11 ISO/IEC 9899: 2011 (E) указаны следующие ограничения для простых назначений в п. 6.5.16.1/1:
Одно из следующего:
- левый операнд имеет атомный, квалифицированный или неквалифицированный арифметический тип, а правый имеет арифметический тип;
- левый операнд имеет атомную, квалифицированную или неквалифицированную версию структуры или объединения тип, совместимый с типом права;
- левый операнд имеет атомный, квалифицированный или неквалифицированный тип указателя и (учитывая тип, который будет иметь левый операнд после преобразования lvalue) оба операнда указатели на квалифицированные или неквалифицированные версии совместимых типов, а тип слева - все определители типа, на которые указывает справа;
- левый операнд имеет атомный, квалифицированный или неквалифицированный тип указателя и (учитывая тип, который будет иметь левый операнд после преобразования lvalue), один операнд является указателем к типу объекта, а другой - указатель на квалифицированную или неквалифицированную версию void, а тип, на который указывает левый, имеет все квалификаторы типа, указывающего на по праву;
- левый операнд является атомарным, квалифицированным или неквалифицированным указателем, а право - нулевым постоянная указателя; или
- левый операнд имеет тип атомный, квалифицированный или неквалифицированный
_Bool
, а правый - указатель.
Меня интересует случай, когда обе стороны являются указателями на несовместимые типы, отличные от void
. Если я правильно понимаю, это должно, по крайней мере, вызвать UB, поскольку оно нарушает это ограничение. Один пример для несовместимых типов должен быть (согласно §6.2.7 и §6.7.2) int
и double
.
Следовательно, следующая программа должна быть нарушена:
int main(void) {
int a = 17;
double* p;
p = &a;
(void)p;
}
Оба gcc и clang предупреждают о "-Wincompatible-pointer-types", но не прерывают компиляцию (компиляция с помощью -std=c11 -Wall -Wextra -pedantic
).
Аналогично, следующая программа только приводит к предупреждению "-Wint-conversion", при компиляции просто отлично.
int main(void) {
int a;
double* p;
p = a;
(void)p;
}
Исходя из С++, я ожидал, что для любого из этих тестовых случаев потребуется компиляция. Есть ли причина, по которой любая из программ будет стандартно-правовой? Или существуют ли по крайней мере значительные исторические причины для поддержки этого стиля кода даже при отключении развлекательных расширений GNU C, явно используя -std=c11
вместо -std=gnu11
?
Ответы
Ответ 1
Флаг компилятора (как gcc, так и clang) для запроса проверок на соответствие строгим стандартам и отказа от компиляции несоответствующего кода -pedantic-errors
:
$ gcc -std=c11 -pedantic-errors x.c
x.c: In function ‘main’:
x.c:3:15: error: initialization from incompatible pointer type [-Wincompatible-pointer-types]
double* p = &a;
^
Clang:
$ clang -std=c11 -pedantic-errors x.c
x.c:3:11: error: incompatible pointer types initializing 'double *' with an
expression of type 'int *' [-Werror,-Wincompatible-pointer-types]
double* p = &a;
^ ~~
1 error generated.
Значительная доля (по меньшей мере) типичного кода C в дикой природе несоконформна, поэтому -pedantic-errors
приведет к сбою большинства программ и библиотек C.
Ответ 2
Пример вашего кода и ваша ссылка на стандарт не совпадают. Примером является инициализация и 6.5.16 говорит о назначении.
Смутно требование соответствия типа находится в разделе 6.5.16 ограничений для назначения, но "только" в разделе семантики (6.7.9) для инициализации. Поэтому компиляторы имеют "право" не выдавать диагностику для инициализации.
В C нарушениям ограничений требуется только "диагностика", компилятор может продолжить компиляцию, но нет гарантии, что результирующий исполняемый файл будет действительным.
На моей платформе, тестирование Debian, оба компилятора дают мне диагностику без каких-либо опций, поэтому я предполагаю, что ваша установка должна быть довольно старой и устаревшей.
Ответ 3
Есть ли причина, по которой любая из программ будет стандартно-правовой?
Эти программы не являются "нормативными". Они содержат нарушения ограничений, и вы уже указали правильный текст из стандарта.
Компиляторы соответствуют стандарту, производя диагностику для нарушения ограничений. Стандарт не требует компиляции для прерывания в случае нарушения ограничения или другой ошибочной программы.
Он не говорит ни на сколько слов, но единственный разумный вывод заключается в том, что любой исполняемый файл, созданный в результате программы, содержащей нарушение ограничения, имеет полностью поведение undefined. (Я видел, что люди пытаются спорить иначе).
Следуют следующие предположения: C (и С++) используются для многих целей; иногда люди хотят "ассемблера высокого уровня" для своей машины и не заботятся о переносимости или стандартах. Предположительно, поставщики компиляторов устанавливают значения по умолчанию для того, что, по их мнению, предпочитает их целевая аудитория.
Ответ 4
Нет, Да
Это действительно очень просто.
![Диаграмма Венна соответствующего кода]()
Нет, не стандарты законны. Да, существенные исторические причины.
С изначально даже не было бросков. Как язык системного программирования, использование его как ультрамощного прославленного ассемблера было не только разумным, но и лучшим практическим опытом и практикой - только в тот же день.
Ключевая часть информации должна освещать вещи: на самом деле задача компилятора не выполнять или не выполнять спецификацию. Спецификация, фактически, буквально, только предположение. Фактическое задание компилятора заключается в компиляции всего кода C, который когда-либо был написан, включая pre-C11, pre-C99, pre-C89 и даже pre-K & R. Вот почему существует так много дополнительных ограничений. Проекты с современными стилями кода включают строго соответствующие режимы.
Существует способ, которым C определяется в стандартах, и существует способ, которым C используется на практике. Итак, вы можете видеть, компилятор просто не может отказаться от создания кода.
В течение десятилетий разработчики переходили на портативный, строго соответствующий код, но когда C впервые появился, он был немного похож на действительно потрясающе мощный ассемблер, и это был своего рода открытый сезон по арифметике адреса и типу punning. Программы в те дни были в основном написаны для одной архитектуры за раз.