C 2018 5.1.2.3 6说:
符合实施的最低要求是:
- 根据抽象机器的规则严格评估对易失性对象的访问。
- 在程序终止时,写入文件的所有数据应与根据抽象语义执行程序的结果相同。
- 交互设备的输入和输出动态应按照7.21.3的规定进行。这些要求的目的是尽快出现无缓冲或行缓冲输出,以确保在程序等待输入之前实际出现提示消息。
这是该程序的可观察行为。
从表面上看,这不包括程序的退出状态。
关于exit(status)
,7.22.4.4 5说:
最后,控制权返回给主机环境。如果
status
的值为零或EXIT_SUCCESS
,则返回状态成功终止的实现定义形式。如果status
的值是EXIT_FAILURE
,则返回状态不成功终止的实现定义形式。否则返回的状态是实现定义的。
标准没有告诉我们这是可观察行为的一部分。当然,这种exit
行为纯粹是C的抽象机器的描述是没有意义的;除非在环境中可观察到,否则向环境返回值没有任何意义。所以我的问题不在于退出状态是否可观察到这是否是C标准对可观察行为的定义中的缺陷。或者标准中的其他地方是否有适用的文字?
我认为将这一点拼凑在一起可以看到答案属于第5.1.2.3.6节的第一个要点:
根据抽象机器的规则严格评估对易失性对象的访问
进一步看,第3.1节将“访问”定义为:
读取或修改对象的值
和§3.15将“对象”定义为:
执行环境中的数据存储区域,其内容可以表示值
奇怪的是,该标准不包含“易失性对象”的定义。它确实包含第6.7.3.6节中“具有volatile限定类型的对象”的定义:
具有volatile限定类型的对象可能以实现未知的方式进行修改,或者具有其他未知的副作用。因此,任何涉及这种对象的表达都应严格按照抽象机的规则进行评估,如5.1.2.3所述。
推断具有volatile限定类型的对象具有准确性以通知编译器它实际上是一个易失性对象似乎并不合理,所以我认为这不会过于宽泛而无法使用这些措辞“volatile对象”本身的定义的基础,并将volatile对象定义为可以以实现未知的方式修改或具有其他未知副作用的对象。
§5.1.2.3.2将“副作用”定义如下:
访问易失性对象,修改对象,修改文件或调用执行任何这些操作的函数都是副作用,这些都是执行环境状态的变化。
所以我认为我们可以把它拼凑成如下:
EXIT_SUCCESS
之后的执行环境必然处于与其接收的状态不同的状态,例如,EXIT_FAILURE
。因此,返回退出状态是第5.1.2.3.2节的副作用exit()
是一个执行此操作的函数,因此调用exit()
本身也是根据§5.1.2.3.2的副作用。exit()
的内部工作原理或exit()
将用于将该值返回到主机环境的机制的详细信息,但是假设访问对象不会涉及是没有意义的,因为对象是数据存储的区域在执行环境中,其内容可以表示值,退出状态是值。exit()
访问volatile对象,因此根据§5.1.2.3.6,它是可观察的行为。这与具有volatile限定类型的对象的正常理解是一致的,即如果我们无法确定由于可观察行为(在正常的日常意义上)将不会省略所需的副作用,则我们无法优化对这些对象的访问。 )如果我们这样做可能会受到影响。当然,在这种情况下没有volatile限定类型的可见对象,因为exit()
内部访问volatile对象是问题,而exit()
显然甚至不需要用C语言编写。但毫无疑问它似乎是一个易变的对象, §5.1.2.3具体(三次)指易失性对象,而不是指挥发性合格类型的对象(除了第6.2.4.2节的脚注外,这是标准易变物体中唯一被引用的地方)。 )
最后,这似乎是使§5.1.2.3.6易于理解的唯一阅读,直观地说,我们期望C程序的“可观察行为”仅使用标准描述的设施松散地:
这似乎基本上是§5.1.2.3.6试图达到的目的。
编辑
在评论中似乎存在一些争议,显然集中在退出状态可能在寄存器中传递的想法,并且寄存器不能是对象。这个异议(没有双关语意)可以轻易地反驳:
register
存储类说明符声明对象,并且这些对象可以由lvalues指定;volatile
限定类型的最常见用途之一;mmap()
表明,即使文件内容有时也可以包含地址和对象。一般来说,认为对象只能驻留在,或者“地址”只能指代核心存储器中的位置,或DRAM芯片组,或者通常可称为“存储器”的任何其他东西, “内存”。能够存储值的执行环境的任何组件(包括寄存器)都可以是一个对象,可能有一个地址,并且可能由左值指定,这就是为什么标准中“对象”的定义是故意宽泛地演绎。
此外,诸如第5.3.2.1.9节之类的部分会花一些时间来区分“实际对象的值”和“抽象语义指定的[值]”,表明实际对象是执行中存在的真实对象环境与抽象机器不同,并且是规范确实与其密切相关的事物,因为第3.15节中“对象”的定义清楚地表明了这一点。似乎难以维持一个标准,即标准关注这些实际对象的位置,直到达到标准库函数被调用的点,此时所有这些问题都会消失,这些问题突然变成“C外”。
我已经阅读了system
函数文档(7.22.4.8系统函数)。它包含:
返回
如果参数是空指针,则仅当命令处理器可用时,系统函数才返回非零值。如果参数不是空指针,并且系统函数确实返回,则返回实现定义的值。
它看起来像是一个系统的标准规定,其中C程序(或更一般地说是用户定义的命令)无法启动另一个命令,和/或命令不会向其调用者返回任何内容的地方。在后一种情况下,退出值将不可观察(在常识中)。
在这种解释中,退出值的可观察性只是实现定义的。并且它与在程序的可观察行为中没有被明确引用的情况一致。
我记得70年代的一个旧系统(Solar 16),其中命令是用call
为标准命令启动的,或run
用于用户命令,其中参数只能在来自程序的特定请求之后传递给子命令。那里没有C编译器,但是如果有人设法实现了一个,那么返回值将不会被观察到。