Разве компилятор не может предупредить (еще лучше, если он бросает ошибки), когда он замечает оператор с undefined/unspecified/implementation-defined поведением?
Вероятно, для установки флага оператора как ошибки в стандарте должно быть сказано так, но это может предупредить кодер, по крайней мере. Есть ли какие-либо технические трудности в реализации такой опции? Или это просто невозможно?
Обоснуйте, что я добрался, этот вопрос в операторах как a[i] = ++i;
не будет это знать, что код пытается сослаться на переменную и изменяет ее в том же операторе, прежде чем точка последовательности будет достигнута.
Все сводится к
Качество реализации : чем точнее и полезнее предупреждения, тем они лучше. Компилятор, который всегда печатал: «Эта программа может вызывать или не вызывать неопределенное поведение» для каждой программы, а затем компилировать ее, довольно бесполезен, но соответствует стандартам. К счастью, никто не пишет такие компиляторы :-).
Простота определения : компилятору может быть нелегко определить неопределенное поведение, неопределенное поведение или поведение, определяемое реализацией. Допустим, у вас есть стек вызовов глубиной 5 уровней, с аргументом const char *
, передаваемым с верхнего уровня последней функции в цепочке, и последними вызовами функции printf ( )
с этим const char *
в качестве первого аргумента. Вы хотите, чтобы компилятор проверял const char *
, чтобы убедиться, что это правильно? (Предполагая, что первая функция использует буквальную строку для этого значения.) Как насчет случая, когда const char *
считывается из файла, но вы знаете, что файл всегда будет содержать допустимый спецификатор формата для значений, являющихся напечатано?
Коэффициент успешности : компилятор может обнаруживать многие конструкции, которые могут быть или не быть неопределенными, неопределенными и т.д .; но с очень низким «успехом». В этом случае пользователь не хочет видеть много сообщений «может быть неопределенным» - слишком большое количество ложных предупреждающих сообщений может скрывать настоящие предупреждающие сообщения или предлагать пользователю выполнить компиляцию с настройкой «низкий уровень предупреждений». Это плохо.
В вашем конкретном примере gcc
выдает предупреждение о том, что "может быть не определено". Он даже предупреждает о несоответствии формата printf ()
.
Но если вы надеетесь на компилятор, который выдает диагностику для всех неопределенных / неуказанных случаев, не ясно, должно ли это работать / может ли это работать.
Допустим, у вас есть следующее:
#include <stdio.h>
void add_to(int *a, int *b)
{
*a = ++*b;
}
int main(void)
{
int i = 42;
add_to(&i, &i); /* bad */
printf("%d\n", i);
return 0;
}
Должен ли компилятор предупреждать вас о строке * a = ++ * b;
?
Как gf говорит в комментариях , компилятор не может проверять разные единицы перевода на предмет неопределенного поведения. Классический пример - объявление переменной как указателя в одном файле и определение ее как массива в другом, см. comp.lang.c FAQ 6.1 .
Если ваш компилятор не предупреждает об этом, вы можете попробовать ЛИНТЕР.
Splint бесплатен, но проверяет только C http://www.splint.org/
Gimpel Lint поддерживает C ++, но стоит 389 долларов США - может быть, вашу компанию можно убедить купить копию? http://www.gimpel.com/
GCC предупреждает как можно больше, когда вы делаете что-то, выходящее за рамки норм языка, оставаясь синтаксически правильным, но за пределами определенной точки необходимо достаточно информирован.
Вы можете вызвать GCC с флагом -Wall
, чтобы увидеть больше.
Разные компиляторы отлавливают разные условия; у большинства компиляторов есть опции уровня предупреждения, у GCC их много, но -Wall -Werror включит большинство полезных и заставит их ошибиться. Используйте \W4 \WX для аналогичной защиты в VC++.
В GCC вы могли бы использовать -ansi -pedantic, но педантичность - это то, что она говорит, и вызовет множество нерелевантных вопросов и затруднит использование большого количества стороннего кода.
В любом случае, поскольку компиляторы ловят разные ошибки или выдают разные сообщения для одной и той же ошибки, полезно использовать несколько компиляторов, не обязательно для развертывания, но в качестве статического анализа. Другой подход для кода на Си - попытаться скомпилировать его как C++; более сильная проверка типов C++ обычно приводит к лучшему коду на Си; но будьте уверены, что если вы хотите, чтобы компиляция на Си работала, не используйте исключительно компиляцию на C++; скорее всего, вы внедрите специфические особенности C++. Опять же, это не обязательно должно быть развернуто как C++, а просто использоваться в качестве дополнительной проверки.
Наконец, компиляторы обычно создаются с учетом баланса производительности и проверки ошибок; исчерпывающая проверка потребовала бы времени, на которое многие разработчики не согласятся. По этой причине существуют статические анализаторы, для языка C есть традиционный lint и splint с открытым исходным кодом. C++ более сложен для статического анализа, и инструменты часто очень дороги. Один из лучших, который я использовал, это QAC++ от Programming Research. Я не знаю ни одного бесплатного анализатора C++ с открытым исходным кодом, имеющего какую-либо репутацию.
gcc предупреждает в этой ситуации (по крайней мере, с -Wall
):
#include <stdio.h>
int main(int argc, char *argv[])
{
int a[5];
int i = 0;
a[i] = ++i;
printf("%d\n", a[0]);
return 0;
}
Gives:
$ make
gcc -Wall main.c -o app
main.c: In function ‘main’:
main.c:8: warning: operation on ‘i’ may be undefined
Edit:
Быстрое чтение man page показывает, что -Wsequence-point
сделает это, если вы по какой-то причине не хотите -Wall
.
Напротив, компиляторы не требуются для выполнения какой-либо диагностики неопределенного поведения:
§1.4.1:
Набор диагностируемых правил состоит из всех синтаксических и семантические правила в этом международном стандарте, за исключением тех правил, которые содержат явную нотацию, что «диагностика не требуется» или которые описаны как приводящие к «неопределенному поведению»
. Хотя я согласен, что это может быть хорошо, у компилятора достаточно проблем, пытаясь соответствовать стандартам, не говоря уже о том, чтобы научить программиста программированию.