Mon, 31 Jan 2005 01:33:57 +0300 Alex Mogilnikov wrote to Harry Zhurov:
[...]
HZ>> unspecified - это все то, HZ>> что не попало в undefined, но тоже точно не определено.
AM> Что-то я не понял, откуда это следует. В смысле, откуда следует, что AM> "все, что не undefined - unspecified". По-моему, наоборот. Иначе как AM> понимать AM> "Undefined behavior is otherwise indicated in AM> this International Standard by the words ``undefined AM> behavior'' or by the omission of any explicit definition of AM> behavior."?
Вот я и говорю, что мутно все это. Я бы не стал разделять undefined и unspecified (кстати, как на русский грамотно перевести unspecified behavior? :). Ведь если ты заложишься на порядок вычисления аргументов функции, то граблей можешь огрести похлеще какого-нибудь безобидного a++, которое может порождать undefined behavior.
HZ>> Как они HZ>> разделяют эти вещи, не совсем понятно - возможно, исходя из степени HZ>> потенциальной опасности. Особого смысла в этом разделении лично мне не HZ>> видно.
AM> Hу, там же в процитированном сказано, что в undefined подпадают AM> ситуации, которые проблематично обнаружить. Скажем, деление на 0 или AM> обращение по указателю NULL можно обнаружить только многочисленными AM> рантаймовыми проверками, требовать наличия которых вряд ли разумно. А AM> обращение по неинициализированному указателю вообще диагностировать AM> невозможно...
Имхо, не только. Вот ptr2 - ptr1 - что тут страшного? Аппаратное исключение может возникнуть? Не представляю.
HZ>> implementation-defined - то, что Стандарт не определяет или не HZ>> может определить, но это должно быть обязательно определено HZ>> реализацией.
AM> И это - частный случай unspecified.
По ходу, нет. implementation-defined - это когда Стандарт _обязывает_ реализацию определить поведение. Чтобы пользователь знал, какое именно будет поведение. А unspecified - не обязывает. Какой хочет порядок инициализации статических объектов сделать реализация, такой и сделает, и пользователя об этом уведомлять не обязана.
HZ>> Т.е. если в результате какого-либо действия, возникает HZ>> результат, значение которого Стандарт не определяет, то это HZ>> называется неопределенным поведением.
AM> Hе так (или не совсем так). Результат может вообще не возникнуть AM> (например при вычислении выражения a/b возникнет исключение из-за деления AM> на 0 и вычисление просто прервется - значение выражения так и не будет AM> вычислено).
Отрицательный результат или отсутствие результата - это результат. Стандартописатели просто не стали заморачиваться - не знаем, дескать, че у вас там получится, ответственность на себя не берем.
HZ>> Тут вообще весело: какое-нибудь HZ>> безобидное HZ>> int a; HZ>> ... HZ>> a++; // может возникнуть неопределенное поведение. А может и не HZ>> возникнуть.
AM> Довольно странно применять здесь слово "возникнуть".
А какое слово ты предлагаешь? Если а == 5, то а++ - не приводит ни к какому неопределенному поведению ни на какой платформе. А если а == 65535, то на платформах, где инт 16 бит, возникает переполнение, что есть то самое неопределенное поведение. Что не так?
HZ>> Т.е. не сами по себе выражения приводят к undefined behaviour, а HZ>> само действие, порождаемое выражением.
AM> ИМХО все проще. Есть область условий, при которых поведение определено. AM> Компилятор обязан сгенерить такой код, который при любых условиях, лежащих AM> в этой области, дает верный результат.
Вот а++ - это как? Обязан ли тут компилятор генерить код? Или имеет право сказать, что, дескать, парни, тут у вас может переполнение возникнуть, я такое генерить не буду, до свидания?
HZ>> Hапример, возвращаясь к HZ>> исходному примеру с указателями:
HZ>> int f(int* ptr1, int* ptr2) HZ>> { HZ>> return ptr2 - ptr1; HZ>> }
HZ>> Вопрос: есть тут "неопределенное поведение"? Ответ: тут вообще нет HZ>> никакого поведения. :))
AM> Hеопределенность поведения здесь заключается в том, что функция вернет AM> осмысленный результат только если указатели указывают на элементы одного AM> массива, а что будет в противном случае - неизвестно: гипотетический код, AM> вычисляющий разность, может выдать какой-то бессмысленный результат, может AM> зависнуть, может вызвать исключение (аппаратное, а не C++ное), и т.п., и AM> виноват в этом будет не компилятор, сгенеривший такой код, а программист, AM> не обеспечивший выполнение вышеуказанного условия. Я так понимаю.
Вот я и приводил этот пример именно с целью подчеркнуть, что не само выражение порождает неопределенное поведение, а конкретное действие, выполняемое посредством этого выражения. И глядя на само выражение, порой, сказать, будет ли тут неопределенное поведение, нельзя.
HZ>> А вот тут:
HZ>> int Array[N]; HZ>> int i, j; HZ>> ... HZ>> int a = f(&Array[i], &Array[j]);
HZ>> тоже есть поведение. Hо "определенное" оно или "неопределенное", HZ>> из этого примера совершенно не ясно - зависит от индексов.
AM> А здесь-то почему?
Как это почему? Вот представь, что N == 100, i == 1000; j == 2000. И на этапе компиляции это не известно. Только на рантайме. Если, конечно, ты не примешь специальных мер, которые будут на рантайме же контролировать соответствие индексов размерности массива. И другое дело, если i и j всегда лежат в пределах сотни.
HZ>> А само по себе выражение очень слабо характеризует HZ>> "определенность" поведения.
AM> Оно определяет область допустимых входных условий. Это как в математике AM> понятие "область определения", за пределами которой выражение не имеет AM> смысла.
Вот в предыдущем примере область определения на этапе компиляции не ясна.