Требуется ли согласованное поведение, определяемое реализацией, между запусками в C ++?

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

Например, разрешено ли реализации говорить «это поведение по выходным, а в противном случае» и реализовывать поведение в соответствии с таким утверждением?


person sharptooth    schedule 21.07.2010    source источник
comment
Вы имеете в виду конкретный пример? в качестве альтернативы я бы взял реализацию, определенную как означающую именно это, и если разработчик гарантирует согласованность, тогда ожидайте согласованности, в противном случае все ставки отключены   -  person msw    schedule 21.07.2010
comment
Раньше GCC был гораздо круче с неопределенным поведением: en.wikipedia.org/wiki/Undefined_behavior#Compiler_easter_easter   -  person sarnold    schedule 21.07.2010


Ответы (6)


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

Семантические описания в этом международном стандарте определяют параметризованную недетерминированную абстрактную машину.

Некоторые аспекты и операции абстрактной машины описаны в этом международном стандарте как определяемые реализацией (например, sizeof (int)). Они составляют параметры абстрактной машины. Каждая реализация должна включать документацию, описывающую ее характеристики и поведение в этих отношениях. Такая документация должна определять экземпляр абстрактной машины, который соответствует этой реализации (далее именуется «соответствующий экземпляр»).

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

Некоторые другие аспекты и операции абстрактной машины описаны в этом международном стандарте как неуказанные (например, порядок оценки аргументов функции). По возможности, этот международный стандарт определяет набор допустимого поведения. Они определяют недетерминированные аспекты абстрактной машины. Таким образом, экземпляр абстрактной машины может иметь более одной возможной последовательности выполнения для данной программы и данного ввода.

person Johannes Schaub - litb    schedule 21.07.2010
comment
...define a parameterized nondeterministic abstract machine. Поскольку абстрактная машина недетерминирована, как необходимо, чтобы поведение было согласованным при одном запуске компилятора? - person Prasoon Saurav; 21.07.2010
comment
@Prasoon, только некоторые пути - те, где поведение не определено - вводят недетерминизм. Например, регулярное выражение z(a|b) может быть выражено недетерминированным конечным автоматом (NFA), который соответствует либо a, либо b в конце ввода. Но, тем не менее, он постоянно соответствует z в начале ввода :) Теперь для поведения, определяемого реализацией, если бы параметры (скажем, z в начале) изменились, вы бы получили другую соответствующую абстрактную машину. У вас больше не будет одной реализации для перевода исходного кода, а будет две. :) - person Johannes Schaub - litb; 21.07.2010
comment
Ах !! И вопрос Sharptooth упоминает, что входные данные должны быть _1 _.... Фу! спасибо Йоханнесу :) - person Prasoon Saurav; 21.07.2010

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

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

Точно так же, я думаю, вы могли бы написать компилятор, который производил бы как 32-, так и 64-разрядные компиляции одной и той же программы - и режим, который он выполнял, можно было бы определить во время выполнения. Опять же, в документации должно быть указано, что ints были 32-битными или 64-битными, в зависимости от того, как вы его запускали.

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

person jcoder    schedule 21.07.2010
comment
+1, универсальные двоичные файлы MacOSX содержат как 32-разрядную, так и 64-разрядную версию (в последних версиях MacOS X, поддерживающих 64-разрядную версию), и вы можете выбрать во время запуска, какую версию вы хотите запустить. Бинарные файлы могут даже содержать код для совершенно другой архитектуры, такой как PowerPC. И если вам интересно, это полезно. Есть некоторые плагины сафари, которые не работают в 64-битной версии, поэтому вы можете запустить сеанс сафари в 32-битной версии, когда они вам понадобятся. - person David Rodríguez - dribeas; 21.07.2010
comment
Ах хорошо, я не знал, что MaxOSX сделал это :) - person jcoder; 21.07.2010

Средства поведения, определенные реализацией

Неопределенное поведение, при котором каждая реализация документирует, как был сделан выбор.

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

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

Нету!

Например, разрешено ли реализации говорить «это поведение по выходным, а в противном случае» и реализовывать поведение в соответствии с таким утверждением?

Я не уверен, но думаю, что ответ - нет.

person Prasoon Saurav    schedule 21.07.2010
comment
Я не понимаю, почему это запрещено. В стандарте говорится, как делается выбор, а не о том, какой именно выбор сделан. - person sharptooth; 21.07.2010
comment
На мой взгляд, данное определение поведения, определяемого реализацией, не исключает возможности различий между запусками, при условии, что оно точно документирует, когда происходит каждый случай и что именно происходит для каждого из них. - person j_random_hacker; 21.07.2010
comment
@sharptooth: Я отредактировал свой ответ, но почти уверен, что ответ на ваш первый вопрос - НЕТ. Например, учитывая sizeof(int), гарантировано (поскольку реализация задокументировала поведение) остается исправление для конкретной реализации. .... Меня немного смущает and implement behavior according to such statement? - person Prasoon Saurav; 21.07.2010
comment
Соответствующая реализация может довольно легко заявить, что sizeof(int) равно 4 по будням и 2 по выходным. Это не отличается от утверждения, что будет 2, если вы определите INT_IS_2_BYTES, или 4 в противном случае. Пока это задокументировано, все в порядке. И 4 в будние дни, 2 в противном случае утверждение полностью согласовано - числа не обязательно должны быть согласованными, только спецификация. Конечно, сказав это, я бы держался подальше от компилятора, который действительно делал это :-) - person paxdiablo; 21.07.2010
comment
Я думаю, вам нужно уточнить, когда вы подозреваете изменение. Поведение не изменится между двумя запусками скомпилированной программы. Однако это может измениться, если вы скомпилируете программу дважды (например, с другими настройками компилятора). - person Tobias Langner; 21.07.2010
comment
@paxdiablo: А! Поэтому я сказал, что не уверен в ответе на его второй вопрос :). Но все же я не понимаю ответа Йоханнеса на первый вопрос. Смотрите мой комментарий под его постом. - person Prasoon Saurav; 21.07.2010
comment
@paxdiablo: Я бы сказал, что это соответствует формулировке, но не цели стандарта. - person peterchen; 21.07.2010
comment
@paxdiablo: Может ли компилятор на законных основаниях указать, что значение unsigned int в памяти составляет 16 бит, но при определенных обстоятельствах при отсутствии приведений типов промежуточные вычисления с unsigned int не будут длиннее? Например. может ли компилятор указать, что (ui1 + ui2) ›› 1 даст арифметически правильное округленное среднее значение? Я думаю, что это может быть для int, но может ли это для unsigned int? - person supercat; 01.10.2010

IIRC, system () должен существовать, но с учетом поведения, определенного реализацией. Что-то вроде system ("ls | grep foo"), естественно, будет иметь разные эффекты в зависимости от того, может ли ваша система выполнять что-то, называемое ls, которое может различаться между запусками. И даже на довольно нормальной машине UNIX, где ls и grep делают то, что вы ожидаете, и не удаляются, результат по-прежнему будет зависеть от существования файла с foo в имени, который, безусловно, будет иметь возможность варьироваться время, и откуда программа выполняется и т. д. Это просто зависит от того, где вы проводите линию «одинаковых входных данных». Если машина находится в абсолютно идентичном состоянии, вы можете ожидать идентичного поведения, но никакие два прогона не приведут к тому, что машина действительно будет в педантически идентичном состоянии. (Даже температура процессора на совершенно идентичных машинах может привести к некоторому дросселированию, которое изменяет победителя некоторого состояния гонки, что явно приводит к другому поведению вашей программы.)

person wrosecrans    schedule 21.07.2010

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

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

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

person David Rodríguez - dribeas    schedule 21.07.2010

rand(3) в <stdlib.h> можно вызывать из C ++, не знаю, какая часть библиотеки C включена в «стандартный C ++», но, безусловно, есть некоторый соответствующий стандартам механизм для генерации случайных чисел.

time(3) в <time.h> возвращает текущее время; та же история с C ++ и вызовом библиотеки C.

person sarnold    schedule 21.07.2010
comment
какое отношение это имеет к вопросу? - person Naveen; 21.07.2010
comment
rand() или time() помечены как реализация? Я могу только предположить, что это так, и если так, то они действительно были бы контрпримером. Что ж, если вы не считаете текущее время и / или состояние системы ГСЧ частью входных данных. - person j_random_hacker; 21.07.2010
comment
@naveen, я предположил, что rand() указан в стандарте, но детали определяются реализацией. Мне сложно проверить это предположение. - person sarnold; 21.07.2010