Вопрос направлен на темы модели памяти C ++ или Java, которая определяет поведение, которое программа может демонстрировать. Простой способ взглянуть на модель памяти — это рассмотреть ее как «фильтр», который определяет набор правил для отклонения выполнения (следов действий) с недопустимым поведением, остальные наборы выполнения являются допустимыми.
Вопрос заключается в следующем: для однопотоковой программы, с учетом фиксированных начальных условий (например, входных параметров, начальных значений переменных) и отсутствия взаимодействия с внешними процессами, существует ли уникальное легальное выполнение (т. Е. Только одно выполнение удовлетворяет модели памяти)?
Дополнительный вопрос: если существует более одного юридического исполнения, что является причиной недетерминизма?
Замечание: Для C ++, давайте посмотрим, как упорядоченный порядок перед тем, чтобы быть полным.
Редактировать: как предложено в комментарии juanchopanza, адрес динамического размещения является одним из источников недетерминированности для однопоточной программы.
Нет, для C ++ не существует ни уникального пути выполнения, ни единого конечного состояния.
Даже при наличии последовательных предварительных гарантий одной из наиболее частых причин являются побочные эффекты при оценке различных аргументов: последовательность оценки аргумента не определяется стандартом и зависит от реализации. Следующий код может дать несколько допустимых выходных данных, например, в зависимости от используемого компилятора:
int display (int i, int j) {
std::cout << i << " " << j << std::endl;
return i<j ? i:j;
}
void my_funny_func (int a, int b, int c) {
std::cout << a << " " << " " << b << " " c << std::endl;
}
...
int i=1, j=1;
my_funny_func(display(i,j), display(++i, j), display(i, ++j));
Стандарт ограничивает гарантии на пути выполнения наблюдаемым поведением (то есть файловыми операциями, операциями с переменными и т. Д.):
1.9 / 1: Скорее, для эмуляции требуются соответствующие реализации (только) наблюдаемое поведение абстрактной машины, как объяснено
ниже.1.9 / 5: Соответствующая реализация, выполняющая правильно сформированную программу, должна производить то же наблюдаемое поведение как один из возможных
казни соответствующего экземпляра абстрактной машины
с той же программой и тем же входом. Однако, если таковые имеются
выполнение содержит неопределенную операцию, этот международный стандарт
не предъявляет никаких требований к реализации, выполняющей эту программу
с этим входом (даже в отношении операций, предшествующих
первая неопределенная операция).
Это сделано специально: это означает, что оптимизатор может максимально упорядочить ненаблюдаемые события. Но это оставляет несколько возможных результатов (особенно для энергонезависимых переменных, которые могут кэшироваться без немедленного сохранения в памяти при каждом отдельном изменении).
Для Java, я думаю, порядок оценки определяется более точно (см. этот другой ответ). Это значительно сократит количество допустимых путей выполнения.
В C++
Существует несколько путей, оба из которых являются законными.
char * a = new char[2000];
char * b = new char[2000];if( ((uintptr_t)a) < ((uintptr_t)b) ) {
// even on the same operating system ALSR may cause different runs to execute in here.
}
Так что в C ++ нет единственного законного пути — есть несколько путей.