来自过去论文的C指针

时间:2014-12-18 13:45:17

标签: c pointers

我有另一个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}

4 个答案:

答案 0 :(得分:14)

y--中的y + 2*(y+2) = *(y--);之间没有序列点,因此y + 2是指&x[4]还是&x[3]没有具体说明。根据编译器的工作方式,您可以获得0 3 5 5 90 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语言不要求在评估表达式后立即应用副作用,只要在下一个序列点之前应用它(在这种情况下是在声明的结尾)。它也不要求从左到右评估表达式(除了少数例外)。因此,不能保证yy+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