Является ли статус выхода наблюдаемым поведением?

C 2018 5.1.2.3 6 говорит:

Минимальные требования к соответствующей реализации:

  • Доступ к изменчивым объектам оценивается строго по правилам абстрактной машины.

  • При завершении программы все данные, записанные в файлы, должны быть идентичны результату, который произойдет при выполнении программы в соответствии с абстрактной семантикой.

  • Динамика ввода и вывода интерактивных устройств должна осуществляться в соответствии с 7.21.3. Цель этих требований заключается в том, чтобы небуферизованный или линейно-буферизованный вывод появлялся как можно быстрее, чтобы гарантировать, что сообщения-подсказки действительно появляются до того, как программа ожидает ввода.

Это наблюдаемое поведение программы.

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

Что касается exit(status), 7.22.4.4 5 говорит:

Наконец, управление возвращается в среду хоста. Если значение status равно нулю или EXIT_SUCCESS, возвращается определяемая реализацией форма успешного завершения статуса. Если значением status является EXIT_FAILURE, возвращается определяемая реализацией форма неудачного завершения статуса. В противном случае возвращаемый статус определяется реализацией.

Стандарт не говорит нам, что это является частью наблюдаемого поведения. Конечно, не имеет смысла, чтобы это поведение exit было описанием чисто абстрактной машины Cs; возвращение значения в окружающую среду не имеет смысла, если оно не наблюдается в окружающей среде. Поэтому мой вопрос не столько в том, является ли наблюдаемый статус выхода, сколько в том, является ли это дефектом в определении стандартов наблюдаемого поведения в стандартах C. Или в стандарте есть где-то еще текст, который применяется?

Ответ 1

Я думаю, что можно собрать это воедино, чтобы увидеть, что ответ подпадает под первый пункт в п. 5.1.2.3.6:

Доступ к изменчивым объектам оценивается строго по правилам абстрактной машины.

В дальнейшем, § 3.1 определяет "доступ" как:

читать или изменять значение объекта

и § 3.15 определяет "объект" как:

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

Как ни странно, стандарт не содержит определения "летучего объекта". Он содержит определение "объекта, который имеет тип volatile -qualified" в § 6.7.3.6:

Объект, имеющий изменчивый тип -qualified, может быть изменен способами, неизвестными для реализации, или иметь другие неизвестные побочные эффекты. Поэтому любое выражение, относящееся к такому объекту, должно оцениваться строго в соответствии с правилами абстрактной машины, как описано в 5.1.2.3.

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

§ 5.1.2.3.2 определяет "побочный эффект" следующим образом:

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

Поэтому я думаю, что мы можем собрать это вместе следующим образом:

  1. Возвращение состояния выхода, которое должно быть возвращено в среду хоста, является явно изменением состояния среды выполнения, поскольку среда выполнения после получения, например, EXIT_SUCCESS, обязательно находится в другом состоянии, в котором она была бы получена, например, EXIT_FAILURE. Поэтому возврат статуса выхода является побочным эффектом согласно § 5.1.2.3.2.

  2. exit() - это функция, которая делает это, поэтому вызов exit() сам по себе также является побочным эффектом согласно § 5.1.2.3.2.

  3. Стандарт, очевидно, не дает нам никаких подробностей о внутренней работе exit() или о том, какой механизм exit() будет использовать для возврата этого значения в среду хоста, но бессмысленно предполагать, что доступ к объекту не будет задействован, поскольку объекты области хранения данных в среде исполнения, содержимое которых может представлять значения, а состояние выхода - значение.

  4. Поскольку мы не знаем, что, во всяком случае, будет делать среда хоста в ответ на это изменение состояния (не в последнюю очередь потому, что наша программа выйдет до того, как это произойдет), этот доступ имеет неизвестный побочный эффект, и поэтому доступ к объекту является изменчивый объект.

  5. Поскольку вызов exit() обращается к энергозависимому объекту, это наблюдаемое поведение согласно § 5.1.2.3.6.

Это согласуется с нормальным пониманием объектов, которые имеют изменчивые типы -qualified, а именно то, что мы не можем оптимизировать удаленный доступ к таким объектам, если не можем определить, что никакие необходимые побочные эффекты не будут опущены, поскольку наблюдаемое поведение (в обычном повседневном смысле) может пострадать, если мы сделаем. Конечно, в этом случае нет видимого объекта типа volatile -qualified, поскольку к volatile объекту обращаются изнутри с помощью exit(), и exit() очевидно, даже не нужно писать в C. Но, несомненно, быть изменчивым объектом, и в п. 5.1.2.3 конкретно (трижды) содержится ссылка на летучие объекты, а не на объекты типа -qualified изменчивого (и не сноски к п. 6.2.4.2, это единственное место в стандарте упоминаются летучие объекты.)

Наконец, это, кажется, единственное прочтение, которое делает п. 5.1.2.3.6 понятным, поскольку интуитивно мы ожидаем, что "наблюдаемое поведение" программ на C, использующих только средства, описанные в стандарте, будет таким, что:

  • Изменяет память таким образом, который виден вне самой программы;
  • Изменяет содержимое файлов (которые по определению видны вне самой программы); а также
  • Влияет на взаимодействие с интерактивными устройствами

что, по сути, является тем, к чему стремится п. 5.1.2.3.6.

редактировать

Кажется, что в комментариях есть небольшая полемика, по-видимому, связанная с идеями о том, что статус выхода может передаваться в регистрах и что регистры не могут быть объектами. Это возражение (без каламбура) тривиально опровергается:

  1. Объекты могут быть объявлены с помощью спецификатора класса хранилища register, и такие объекты могут быть обозначены lvalues;

  2. Регистры с отображением в памяти, довольно вездесущие во встроенном программировании, дают такую же ясную демонстрацию, как и любые регистры, которые могут быть объектами, могут иметь адреса и могут быть обозначены lvalues. Действительно, регистры с отображением в памяти являются одним из наиболее распространенных видов использования volatile типов -qualified;

  3. mmap() показывает, что даже содержимое файла может иногда иметь адреса и быть объектами.

В целом, ошибочно полагать, что объекты могут находиться только в или что "адреса" могут относиться только к местам в памяти ядра или банкам микросхем DRAM, или ко всему прочему, что можно условно назвать "памятью" или "БАРАН". Любой компонент среды выполнения, который способен хранить значение, включая регистры, может быть объектом, потенциально может иметь адрес и потенциально может быть обозначен lvalue, и именно поэтому определение "объекта" в стандарте в таких нарочито широких сроках.

Кроме того, такие разделы, как п. 5.3.2.1.9, идут на некоторую длину, чтобы провести различие между "значениями реальных объектов" и "[значениями], указанными в абстрактной семантике", что указывает на то, что фактические объекты являются реальными вещами, существующими в исполнении. окружение, отличное от абстрактной машины, - это вещи, с которыми действительно тесно связана спецификация, что ясно дает определение "объекта" в п. 3.15. Кажется неприемлемым сохранять позицию, в которой стандарт касается таких реальных объектов вплоть до и только до того момента, когда вызывается стандартная библиотечная функция, и в этот момент все такие проблемы испаряются, и такие вопросы внезапно становятся "вне С".

Ответ 2

Я прочитал документацию по system функциям (7.22.4.8 Системная функция). Это содержит:

Возвращает

Если аргумент является нулевым указателем, системная функция возвращает ненулевое значение, только если доступен командный процессор. Если аргумент не является нулевым указателем, и системная функция возвращает, она возвращает значение, определенное реализацией.

Похоже, что это стандартное условие для системы, в которой программа на C (или, в более общем случае, пользовательская команда) не может запустить другую команду, и/или где команда не возвращает ничего своему вызывающему. В этом последнем случае выходное значение не будет наблюдаемым (в обычном смысле).

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

Я могу вспомнить старую систему (Solar 16) из 70, где команды запускались с call стандартных команд или run для пользовательских команд, и когда параметры могли передаваться только в подкомандах после определенного запроса от программы. Компилятора C там не было, но если бы кому-то удалось его реализовать, возвращаемое значение не было бы заметным.