разрешить только определенное поведение в C ++? - PullRequest
7 голосов
/ 06 апреля 2011

Возможно ли в gcc / g ++ или ms c ++ установить флаг, который допускает только определенное поведение?так что-то вроде ниже дает мне предупреждение или предпочтительно ошибку

func(a++, a, ++a)

Ответы [ 5 ]

8 голосов
/ 06 апреля 2011

Неопределенное и неуказанное поведение обозначено в стандарте именно так, потому что это может вызвать чрезмерную нагрузку на реализацию для диагностики всех его примеров (или было бы невозможно определить).

Ожидается, что программист позаботится о том, чтобы избежать тех областей, которые не определены.

Для вашего приведенного примера для программиста должно быть совершенно очевидно, что просто не писать этот код в первую очередь .

При этом g++ -Wall будет отлавливать какой-то плохой код, например, пропущенный возврат в не пустую функцию, чтобы привести один пример.

РЕДАКТИРОВАТЬ: @sehe также указывает -Wsequence-point, который поймает эту точную кодовую конструкцию, хотя должна быть точка последовательности между оценкой каждого аргумента (однако порядок, в котором оцениваются аргументы, не определен).

7 голосов
/ 06 апреля 2011

GNU C ++ имеет следующее

   -Wsequence-point
       Warn about code that may have undefined semantics because of violations of sequence point rules in the C and C++ standards.

Это правильно помечает вызов, который вы показали

  -Wstrict-overflow

  -Wstrict-overflow

   -fstrict-aliasing
   -fstrict-overflow

HTH

3 голосов
/ 07 апреля 2011

Нет.Например, рассмотрим следующее:

int badfunc(int &a, int &b) {
    return func(a++, b++);
}

Это имеет неопределенное поведение, если a и b имеют одинаковый реферранд.В общем, компилятор не может знать, какие аргументы будут переданы функции, поэтому он не может надежно уловить этот случай неопределенного поведения.Поэтому он не может перехватить все неопределенное поведение.

Предупреждения компилятора служат для идентификации некоторых случаев неопределенного поведения, но не всех.

Теоретически вы можете написать C ++реализация, которая выполняет огромное количество проверок во время выполнения, чтобы гарантировать, что неопределенное поведение всегда идентифицируется и обрабатывается способами, определенными этой реализацией.Он все равно не скажет вам во время компиляции (см .: проблема остановки), и на практике вам, вероятно, будет лучше с C #, который был разработан, чтобы сделать необходимые проверки во время выполнения достаточно эффективными ...

Даже если вы создали эту магическую реализацию C ++, она все равно может не сказать вам, что вы действительно хотите знать, а именно, является ли ваш код правильным.Иногда (держитесь за свои места), это определяется реализацией, является ли поведение неопределенным.Для простого примера tolower((char)-1); имеет определенное поведение [*], если тип char не подписан, но неопределенное поведение, если тип char подписан.

Так что, если ваша волшебная реализация проверки не выполнит всетот же выбор реализации, что и в «реальной» реализации, на которой вы хотите, чтобы ваш код выполнялся, он не скажет вам, определил ли код поведение для набора вариантов реализации, сделанных в «реальной» реализации, только если он определилповедение для вариантов реализации, сделанных в реализации магической проверки.

Чтобы знать, что ваш код корректен и переносим, ​​вам нужно знать (для начала), что он не дает неопределенного поведения для любого множество вариантов реализации.И, в этом отношении, для любого входа, а не только входов, используемых в ваших тестах.Вы можете подумать, что это большой недостаток C ++ по сравнению с языками без неопределенного поведения.Конечно, это иногда неудобно и влияет на то, как вы работаете с песочницами для безопасности.На практике, однако, чтобы вы считали свой код правильным, вам нужно не просто иметь определенное поведение, вам нужно поведение, соответствующее документу спецификации.Это намного большая проблема, и на практике написать ошибку в (скажем) Java или Python не намного сложнее, чем в C ++.Я написал бесчисленные ошибки во всех трех, и зная, что в Java или Python поведение было определено, но неправильное не очень мне помогло.

[*] Что ж, результат все еще определяется реализацией, это зависит от набора символов выполнения, но реализация должна возвращать правильный результат.Если подписано char, это может привести к сбою.

2 голосов
/ 06 апреля 2011

Это заставило меня посмеяться. Извините за это, не означает никакого оскорбления; это хороший вопрос.

На планете нет компилятора, который бы допускал только 100% определенное поведение. Это неопределенная природа вещей, которая делает это таким трудным. В стандарте рассматривается много случаев, но они часто слишком расплывчаты, чтобы их можно было эффективно реализовать в компиляторе.

Я знаю, что разработчики Clang проявили некоторый интерес к добавлению этой функциональности, но, насколько я знаю, они еще не начались.

Единственное, что вы можете сделать сейчас и в ближайшем / отдаленном будущем, это повысить уровень предупреждения и строгость вашего компилятора. К сожалению, даже в последних версиях MSVC является проблемой в этом отношении. На уровне предупреждения 4 и выше он выдает некоторые глупые предупреждения, которые не имеют ничего общего с правильностью кода, и вам часто приходится прыгать через обручи, чтобы заставить их уйти.

GCC лучше в моем личном опыте. Я лично использую эти опции, обеспечивая самые строгие проверки (в настоящее время я знаю)

-std=c++0x -pedantic -Wextra -Weffc++ -Wmissing-include-dirs -Wstrict-aliasing

Я, конечно, предупреждаю об отсутствии нуля, если вы хотите применить даже это, просто добавьте -Werror в строку выше, и любая ошибка приведет к ошибке. В основном опции std и pedantic обеспечивают стандартное поведение, Wextra ловит некоторые случайные полусообщения.

И, конечно, если возможно, скомпилируйте свой код с помощью различных компиляторов (и убедитесь, что они правильно диагностируют проблему, спросив здесь, где люди знают, что говорит / означает Стандарт).

1 голос
/ 06 апреля 2011

Хотя я согласен с ответом Марка, я просто подумал, что должен сообщить вам ...

#include <stdio.h>

int func(int a, int b, int c)
{
    return a + b + c;
}

int main()
{
    int a=0;
    printf("%d\n", func(a++, a, ++a)); /* line 11 */
    return 0;
}

При компиляции кода выше с помощью gcc -Wall я получаю следующие предупреждения:

test.c:11: warning: operation on ‘a’ may be undefined
test.c:11: warning: operation on ‘a’ may be undefined

из-за a++ и ++a, я полагаю.Так что в некоторой степени это было реализовано.Но, очевидно, мы не можем ожидать, что компилятор распознает все неопределенное поведение.

...