表达式的定义行为

bit*_*ask 11 c standards-compliance undefined-behavior language-lawyer

C99标准以6.5.2美元计价.

在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算来修改一次.此外,先前的值应该只读以确定要存储的值.

(我强调)

它继续指出,以下示例是有效的(一开始看起来很明显)

a[i] = i;
Run Code Online (Sandbox Code Playgroud)

虽然它没有明确说明是什么ai是什么.

虽然我相信它没有,但我想知道这个例子是否涵盖以下情况:

int i = 0, *a = &i;
a[i] = i;
Run Code Online (Sandbox Code Playgroud)

不会改变值i,而是访问值i来确定放置值的地址.或者我们分配一个i已存储的值是无关紧要的i?请说清楚.


奖金问题; 怎么样a[i]++还是a[i] = 1

Kei*_*son 15

第一句话:

在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算来修改一次.

很清楚.该语言不会对子表达式施加评估顺序,除非它们之间存在序列点,而不是要求一些未指定的评估顺序,它表示修改对象两次会产生未定义的行为.这允许积极优化,同时仍然可以编写遵循规则的代码.

下一句话:

此外,先前的值应该只读以确定要存储的值

在第一眼(和第二眼)看来确实看起来不直观; 为什么读取值的目的是否会影响表达式是否已定义行为?

但它反映的是,如果子表达式B依赖于子表达式A的结果,则必须在评估B 之前评估A. C90和C99标准没有明确说明.

在脚注的一个例子中,更明确地违反了该句,是:

a[i++] = i; /* undefined behavior */
Run Code Online (Sandbox Code Playgroud)

假设它a是一个声明的数组对象并且i是一个声明的整数对象(没有指针或宏技巧),则不会多次修改任何对象,因此它不会违反第一个句子.但是i++对LHS 的评估确定了要修改的对象,并且i对RHS 的评估确定了要存储在该对象中的值 - 以及RHS上的读取操作和写入操作的相对顺序. LHS未定义.同样,该语言可能需要以某种未指定的顺序评估子表达式,而是将整个行为保留为未定义,以允许更积极的优化.

在你的例子中:

int i = 0, *a = &i;
a[i] = i; /* undefined behavior (I think) */
Run Code Online (Sandbox Code Playgroud)

i读取前一个值以确定要存储的值确定要存储的对象.由于a[i]引用i(但仅仅因为i==0),修改值i将改变左值a[i]引用的对象.在这种情况下,存储i的值与已存储在那里的值(0)相同,但标准不会对碰巧存储相同值的存储造成异常.我认为这种行为是不确定的.(当然,标准中的示例并不打算涵盖这种情况;它隐含地假设这a是一个与之无关的声明的数组对象i.)

至于标准所说的例子是允许的:

int a[10], i = 0; /* implicit, not stated in standard */
a[i] = i;
Run Code Online (Sandbox Code Playgroud)

人们可以解释标准,说它是未定义的.但我认为第二句,指的是"先前值",仅适用于由表达式修改的对象的值. i永远不会被表达式修改,所以没有冲突.值i既用于确定要通过赋值修改的对象,也用于存储在那里的值,但这没关系,因为i它本身的值永远不会改变.价值i不是"先前价值",只是价值.

C11标准有一种新的模型用于这种表达式评估 - 或者更确切地说,它用不同的词表达相同的模型.它不是"序列点",而是讨论在彼此之前或之后进行排序的副作用,或相对于彼此不顺序的副作用.它明确表示如果子表达式B依赖于子表达式A的结果,则必须在评估B 之前评估A.

N1570草案中,第6.5节说:

1 表达式是一系列运算符和操作数,用于指定值的计算,或指定对象或函数,或生成副作用,或执行其组合.在运算符的结果的值计算之前,对运算符的操作数的值计算进行排序.

2如果对标量对象的副作用相对于同一标量对象的不同副作用或使用相同标量对象的值进行的值计算未进行排序,则行为未定义.如果表达式的子表达式有多个允许的排序,则如果在任何排序中发生这种未测序的副作用,则行为是不确定的.

3语法指示运算符和操作数的分组.除非后面指出,否则子表达的副作用和值计算是不确定的.