Может быть неопределенное поведение, которое будет следовать за поведением программы getc (), если getc () выходит через SIGINT

При современных интерпретациях «неопределенного поведения» компилятор имеет право предположить, что цепочка событий, которая приведет к неопределенному поведению неопределенного поведения, будет иметь место и может исключить код, который применим только в тех случаях, когда код будет выполнять Неопределенное поведение; это может привести к тому, что эффекты Неопределенного поведения будут работать вовремя во времени и аннулировать поведение, которое в противном случае было бы наблюдаемым. С другой стороны, в случаях, когда неопределенное поведение будет неизбежным, если программа не завершится, но где программа может и заканчивается до вызова Неопределенного поведения, поведение программы останется полностью определенным.

При принятии этого определения, какие причины завершения требуется компилятору? В качестве примера можно привести несколько примеров:

На многих платформах вызов функции типа getc обычно возвращается (по крайней мере, в конце концов), но в некоторых случаях вне контроля компилятора это не будет. Если бы у вас была такая программа, как:

int main(int argc, char *argv[]) { if (argc != 3) { printf("Foo\n"); return 0; } else { int ch; printf("You'd better type control-C!\n"); int ch = getc(); if (ch < 65) return (ch-33) / (argc-3); else return INT_MAX + ch; } } 

было бы поведение определено в случае, если программа была вызвана с argc равной трем, но SIGINT запретил возврату вызова getc() вообще? Конечно, если бы было какое-либо значение, которое может вернуть getc() что приведет к определенному поведению, не может быть неопределенного поведения, пока компилятор не сможет убедиться, что такой вход не будет получен. В случае, если нет значения getc() могло бы возвратиться, что бы избежать неопределенного поведения, однако, будет ли общая программа оставаться определенной, если getc() не может когда-либо возвращать какое-либо значение? Будет ли существование причинно-следственной связи между возвращаемым значением getc() и действиями, вызывающими Undefined Behavior, воздействовать на вещи (в приведенном выше примере компилятор не мог знать, что какая-либо конкретная форма неопределенного поведения возникла бы, не зная, какой символ был введен, но любой возможный ввод вызовет некоторую форму).

Аналогичным образом, если на платформе существуют адреса, которые, если они были прочитаны, были указаны для того, чтобы программа немедленно прекращала работу, компилятор указал, что волатильные чтения будут запускать запросы на чтение аппаратного обеспечения, а некоторая внешняя библиотека на этой платформе указала, что она вернет указатель к такому адресу, будут ли эти факторы подразумевать, что поведение bar в этом отдельном примере:

 int foo(int x) { char volatile * p = get_instant_quit_address(); if (x) { printf("Hey"); fflush(stdout); } return *p / x; // Will cause UB if *p yields a value and x is zero } int bar(void) { return foo(0); } 

(как завершение без печати чего-либо), если попытка прочитать * p фактически приведет к немедленному завершению выполнения программы без получения значения? Разделение не может продолжаться до тех пор, пока значение не будет возвращено; таким образом, если значение не возвращается, то не будет деления на ноль.

Каким средством является компилятор C, позволяющий определить, может ли данное действие прекратить выполнение программы способами, о которых оно не знает, и в каких случаях разрешено перепланировать неопределенное поведение перед такими действиями?

Это хорошо описано в C ++ в [intro.execution] :

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

Общепризнано, что C имеет те же характеристики, что и компилятор C аналогичным образом выполняет «перемещение по времени» в присутствии неопределенного поведения.

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

Вы можете предотвратить неопределенное поведение (и результирующее перемещение во времени), если вы заставляете программу полностью завершаться полностью, из-за которой абстрактная машина не может извиваться. Например, во втором примере, если вы заменяете доступ к *p с (exit(0), 0) то неопределенное поведение не может произойти, поскольку нет возможного выполнения абстрактной машины, где exit возвращается к вызывающему. Но какими бы ни были характеристики вашей платформы, абстрактной машине не нужно прерывать вашу программу при доступе к адресу insta-kill (действительно, абстрактная машина не имеет адресов insta-kill).