我有另一个C指针问题。
考虑执行以下程序:
int x[5] = {0,3,5,7,9}; int* y = &x[2]; *(y+2) = *(y--);
数组
x
之后有什么值?
y--
到底发生了什么事?我知道*(y+2)
如何运作,并了解其余部分,但不知道y--
如何与其他人联系。
另外,给出的答案是{0, 3, 5, 5, 9}
。
答案 0 :(得分:14)
y--
中的y + 2
和*(y+2) = *(y--);
之间没有序列点,因此y + 2
是指&x[4]
还是&x[3]
没有具体说明。根据编译器的工作方式,您可以获得0 3 5 5 9
或0 3 5 7 5
。
这意味着两个表达式之间没有序列点,简而言之,没有指定一个操作(在这种情况下为--y
)的副作用是否已被应用另一个(y - 2
)被评估。您可以阅读有关序列点here的更多信息。
ISO / IEC 9899:201x
6.5表达式
p2:如果相对于不同的副作用,对标量对象的副作用未被排序 在相同的标量对象上或使用相同标量的值进行值计算 对象,行为未定义。如果有多个允许的排序 表达式的子表达式,如果这样一个未经测序的一方,则行为是不确定的 效果发生在任何排序中。
答案 1 :(得分:10)
在这种情况下,你不应该相信你的教授给出的答案。
扩展Wintermute的答案......
问题在于声明
*(y+2) = *(y--);
表达式y--
计算为y
的当前值,并且副作用递减变量。例如:
int a = 10;
int b;
b = a--;
评估上述表达式后,b
的值为10,a
的值为9.
但是,C语言不要求在评估表达式后立即应用副作用,只要在下一个序列点之前应用它(在这种情况下是在声明的结尾)。它也不要求从左到右评估表达式(除了少数例外)。因此,不能保证y
中y+2
的值表示递减操作之前或之后的y
的值。
C语言标准显式地将此类操作调用为未定义行为,这意味着编译器可以以任何方式自由处理该情况。结果将根据编译器,编译器设置甚至周围的代码而有所不同,就语言定义而言,任何答案将同样正确。
为了使这个定义明确并给出相同的结果,你需要在赋值语句之前递减y
:
y--;
*(y+2) = *y;
这始终是C语言中最容易被误解和误解的方面之一。如果你的教授希望这个特定的结果能够被明确定义,那么他就不会像他认为的那样知道语言。再说一次,他在这方面并不是独一无二的。
重复并展开Wintermute发布的C 2011 draft standard代码段
6.5表达
...
2 如果相对于不同的副作用,对标量物体的副作用是无效的 在相同的标量对象上或使用相同标量的值进行值计算 对象,行为未定义。如果有多个允许的排序 表达式的子表达式,如果这样一个未经测序的一方,则行为是不确定的 效果发生在任何排序中。 84)
3运算符和操作数的分组由语法指示。 85) 除非另有说明 后来,子表达的副作用和价值计算没有排序。 86)
84)此段落呈现未定义的语句表达式,如允许的同时i = ++i + 1; a[i++] = i;
i = i + 1; a[i] = i;
85)语法指定运算符在表达式求值中的优先级,它是相同的 作为本条款主要子条款的顺序,首先是最高优先级。因此,例如, 表达式允许二进制+
运算符(6.5.6)的操作数是在中定义的表达式 6.5.1至6.5.6。例外情况是强制转换表达式(6.5.4)作为一元运算符的操作数 (6.5.3),以及以下任何一对运算符之间包含的操作数:分组 括号()
(6.5.1),下标括号[]
(6.5.2.1),函数调用括号()
(6.5.2.2),以及 条件运算符? :
(6.5.15)。 在每个主要子条款中,运算符具有相同的优先级。左或右相关性是 在每个子条款中通过其中讨论的表达式的语法表示。
86)在程序执行期间被多次评估的表达式中,未经过排序和 对其子表达式的不确定顺序评估不需要一致地执行 不同的评价。
重点补充。请注意,自C89标准以来,情况确实如此,尽管从那时起措辞有所改变。
“Unsequenced”仅表示不能保证一个操作在另一个操作之前完成。赋值运算符不会引入序列点,因此无法保证在RHS之前计算表达式的LHS。
现在对于困难的一点 - 你的教授显然希望这些表达的具体行为。如果他给出测试或测验询问a[i] = i--;
之类的结果会是什么,他可能不会接受“行为未定义”的回答,至少不会在其自己的。您可能想要讨论Wintermute和我给他的答案,以及上面引用的标准部分。
答案 2 :(得分:0)
问题出在本声明中。
*(y+2) = *(y--);
因为在C中,在表达式中读取变量两次(在其中被修改)具有未定义的行为。
另一个例子是:
i = 5;
v[i] = i++;
在这种情况下,最可能发生的事情(AFAIK)是编译器首先评估RHS或LHS,如果首先评估LHS,那么我们将v[5] = 5;
并且在分配i
之后等于6,如果不是首先评估RHS,那么我们将得到右侧的评估将等于5,但是当我们开始评估时,左侧i
将等于6,所以我们最终得到v[6] = 5;
,但是,如果引用“未定义的行为允许编译器做任何它选择的事情,甚至让恶魔飞出你的鼻子”你应该不要指望其中一个选项,而不是你应该期待什么,因为它取决于编译器会发生什么。
答案 3 :(得分:0)
首先int x[5] = {0, 3, 5, 7, 9}
表示
x[0] = 0, x[1] = 3, x[2] = 5, x[3] = 7, x[4] = 9
下一页int *y = &x[2]
您在此尝试使用指针y
指向x[2]
的地址
现在请注意您的混淆*(y + 2)
表示您指的是x[4]
的地址
和*(y--)
,这里y--
是一个后递减运算符,因此首先必须使用*y
的值x[2] = 5
,所以现在分配的值是{{ 1}}。
最终输出为x[4] = 5