Ошибка, которая не является синтаксической или семантической?

У меня был этот вопрос по заданию домашней работы (не беспокойтесь, уже сделано):

[Используя ваш любимый императивный язык, дайте пример каждый из...] Ошибка, которую компилятор не может ни поймать, ни легко сгенерировать код для (это должно быть нарушение определения языка, а не только ошибка программы)

Из "Прагматики программирования языка" (3-е изд.) Майкл Л. Скотт

Мой ответ, вызовите main из main, передав те же аргументы (на C и Java), вдохновленные этим. Но я лично чувствовал, что это будет просто семантическая ошибка.

Мне этот вопрос задает вопрос о том, как создать ошибку, которая не является ни синтаксической, ни семантической, и, честно говоря, я не могу думать о ситуации, когда она тоже не попадет.

Будет ли это код, который восприимчив к эксплуатации, например переполнение буфера (и, возможно, другая эксплуатация, о которой я никогда не слышал)? Какой-то вид ямы выпадает из структуры языка (IDK, но ленивая оценка/проверка слабого типа)? Я бы хотел сделать простой пример в Java/С++/C, но другие примеры приветствуются.

Ответ 1

Undefined поведение. Оператор, вызывающий UB, не является ни синтаксически, ни семантически некорректным, но результат не может быть предсказан и считается ошибочным.

Примером этого может быть (со страницы Википедии) попытка изменить константу строки:

char * str = "Hello world!";
str[0] = 'h'; // undefined-behaviour here

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

// get number from user
char input[100];
fgets(input, sizeof input, stdin);
int number = strtol(input, NULL, 10);
// print its square: possible integer-overflow if number * number > INT_MAX
printf("%i^2 = %i\n", number, number * number);

Здесь может не обязательно быть переполненным целым числом. И невозможно обнаружить его при компиляции или времени ссылки, поскольку он включает пользовательский ввод.

Ответ 2

Заявления, вызывающие поведение undefined 1 семантически, а также синтаксически корректны, но делают программы неуправляемыми.

a[i++] = i;   // Syntax (symbolic representation) and semantic (meaning) both are correct. But invokes UB.   

Другим примером является использование указателя без его инициализации.
Логические ошибки также не являются семантическими и синтаксическими.


<суб > 1. undefined поведение: все может произойти; Стандарт не предъявляет никаких требований. Программа может не скомпилироваться, или она может выполняться некорректно (либо сбой, либо беззвучно генерирование неверных результатов), либо она может выполнить точно то, что планировал программист.

Ответ 3

Вот пример для С++. Предположим, что у нас есть функция:

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

Тогда следующий код имеет поведение undefined, поскольку он дважды изменяет объект без промежуточной точки последовательности:

int i = 0;
incsum(i, i);

Если вызов incsum находится в другом TU из определения функции, тогда невозможно поймать ошибку во время компиляции, потому что ни один из них не является само по себе неправильным. Он может быть обнаружен во время соединения с помощью достаточно интеллектуального компоновщика.

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

Вы можете утверждать, как легко генерировать код, чтобы поймать это - я бы не сказал, что это очень легко, но компилятор заметил, что ++a + ++b недействителен, если a и b псевдоним одного и того же объекта, и добавьте в эту строку эквивалент assert (&a != &b);. Таким образом, код обнаружения может быть создан локальным анализом.