разрешает только определенное поведение в C ?

#c #visual-c #gcc #g

#c #visual-c #gcc #g

Вопрос:

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

 func(a  , a,   a)
  

Комментарии:

1. Я компилирую, используя g -Wall -ansi -pedantic или иногда g -Wall -Weffc -ansi -pedantic .

2. Я также считаю -Wnon-virtual-dtor важным.

3. @Mark: Это не приведет к обнаружению какого-либо неопределенного поведения.

4. Я поддерживаю вопрос из-за великолепных ответов. OT: легенда гласит, что когда-то был компилятор, который сообщал «Компилятор не смог обнаружить ни одной из ваших ошибок» вместо «0 ошибок, 0 предупреждений»

5. @Mark B В некоторых случаях это совершенно разумно; защищенные деструкторы — очевидный случай.

Ответ №1:

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

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

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

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

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

Ответ №2:

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

Комментарии:

1. 1. Вы также можете использовать -Werror=sequence-point , поэтому g откажется его компилировать.

Ответ №3:

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

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

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

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

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

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

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

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

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

Ответ №4:

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

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

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

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

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

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

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

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

Комментарии:

1. Вы что-то упускаете -Wconversion !

2. Вау, ты прав. Был получен один случай преобразования size_t -> int . Должен любить C за педантичность :)

Ответ №5:

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

 #include <stdio.h>

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

int main()
{
    int a=0;
    printf("%dn", 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 . Итак, в какой-то степени это было реализовано. Но, очевидно, мы не можем ожидать, что компилятор распознает все неопределенное поведение.