Вопрос направлен на темы модели памяти C ++ или Java, которая определяет поведение, которое программа может демонстрировать. Простой способ взглянуть на модель памяти состоит в том, чтобы рассматривать ее как «фильтр», который определяет набор правил для отклонения выполнений (следов действий) с недопустимым поведением, остальные наборы выполнений являются законными.

Вопрос заключается в следующем: для однопотоковой программы, с учетом фиксированных начальных условий (например, входных параметров, начальных значений переменных) и отсутствия взаимодействия с внешними процессами, существует ли уникальное легальное выполнение (т. Е. Только одно выполнение удовлетворяет модели памяти)?

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

Примечание: Для C ++ давайте посмотрим, как упорядоченный порядок до того, чтобы быть полным.

Редактировать: как предложено в комментарии juanchopanza, адрес динамического размещения является одним из источников недетерминированности для однопоточной программы.

1
314314314 28 Май 2017 в 14:27

2 ответа

Лучший ответ

Нет, для 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, я думаю, что порядок оценки определяется более точно (см. этот другой ответ). Это значительно сократит количество допустимых путей выполнения.

1
Christophe 28 Май 2017 в 14:54

В 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 ++ нет единственного законного пути - есть несколько путей.

0
mksteve 28 Май 2017 в 12:50