С++. Какое раннее поведение undefined может проявиться?

Я знаю, что поведение undefined может потенциально вызвать что-либо, что делает любую программу, содержащую UB, бессмысленной. Мне было интересно, есть ли способ идентифицировать самый ранний момент в программе, поведение undefined может вызвать проблемы. Вот пример, чтобы проиллюстрировать мой вопрос.

void causeUndefinedBehavior()
{
   //any code that causes undefined behavior
   //every time it is run
   char* a = nullptr;
   *a;
}


int main()
{
 //code before call
 //...
 causeUndefinedBehavior();
 //code after call
 //...
}

По моему мнению, возможное поведение undefined может быть вызвано (не обязательно проявляется):

  • Когда causeUndefinedBehavior() скомпилирован.
  • Когда main() скомпилирован.
  • Во время запуска программы.
  • В момент выполнения causeUndefinedBehavior().

Или точка, в которой поведение undefined вызвано совершенно другим для каждого случая и каждой реализации?

Кроме того, если я прокомментировал строку, в которой вызывается causeUndefinedBehavior(), это устранит UB, или он все еще будет в программе, поскольку скомпилирован код, содержащий UB?

Ответ 1

Как ваш код несколько демонстрирует, поведение undefined почти всегда является условием состояния выполнения во время попытки поведения. Небольшая модификация вашего кода может сделать это болезненно очевидным:

void causeUndefinedBehavior()
{
   //any code that causes undefined behavior
   //every time it is run
   char* a = nullptr;
   *a;
}


int main()
{
 srand(time(NULL));
 //code before call
 //...
 if (rand() % 973 == 0)
    causeUndefinedBehavior();
 //code after call
 //...
}

Вы можете выполнить это тысячу раз или больше и никогда не запускать условие выполнения UB. что не изменяет тот факт, что сама функция явно UB, но обнаружение ее во время компиляции в контексте invoker не является тривиальным.

Ответ 2

Я думаю, что это зависит от типа поведения undefined. Вещи, которые повлияли бы на что-то вроде смещения структуры, могут привести к поведению undefined, которое будет отображать любой временной код, который затрагивает эту структуру.

Однако в большинстве случаев поведение undefined происходит в время выполнения, что означает, что только если этот код будет выполнен, произойдет поведение undefined.

Например,, попытка изменить строковый литерал имеет поведение undefined:

char* str = "StackOverflow";
memcpy(str+5, "Exchange", 8);    // undefined behavior

Это "undefined поведение" не будет выполняться до выполнения memcpy. Он по-прежнему будет компилироваться в совершенно нормальный код.

Другим примером является исключение возврата из функции с невоидным возвращаемым типом:

int foo() {
    // no return statement -> undefined behavior.
}

Здесь он находится в точке, где foo возвращает, что происходит поведение undefined. (В этом случае на x86 все, что попадало в регистр eax, - это возвращаемое возвращаемое значение функции.)

Многие из этих сценариев можно идентифицировать, включив более высокий уровень сообщений об ошибках компилятора (например, -Wall в GCC.)

Ответ 3

"Undefined поведение" означает, что определение языка не говорит вам, что будет делать ваша программа. Это очень простое утверждение: никакой информации. Вы можете расспрашивать все, что вам нравится, о том, что ваша реализация может или не может сделать, но если ваша реализация не документирует то, что она делает, вы только догадываетесь. Программирование не связано с угадыванием; это о знании. Если поведение вашей программы undefined, исправьте ее.

Ответ 4

в то время как это "поведение w90 > ", учитывая конкретный компилятор, он будет иметь предсказуемое поведение. Но поскольку это undefined, на разных компиляторах, это может привести к тому, что поведение происходит в любой точке процесса компиляции/выполнения

Ответ 5

что делает любую программу, содержащую UB, потенциально бессмысленной

Не совсем верно. Программа не может "содержать" UB; когда мы говорим "UB", что коротко: поведение программы undefined. Все это!

Таким образом, программа не просто потенциально, а фактически бессмысленна с самого начала.

[intro.execution]/5: Соответствующая реализация, выполняющая хорошо сформированную программу, должна обеспечивать такое же наблюдаемое поведение, как и одно из возможных исполнений соответствующего экземпляра абстрактной машины с той же программой и тем же входом. Однако, если какое-либо такое исполнение содержит операцию undefined, , этот международный стандарт не устанавливает требования к реализации, выполняющей эту программу с этим вводом (даже в отношении операций, предшествующих первой операции undefined),.