Moe*_*673 70 c c++ increment prefix
我一直在愚弄一些代码,看到一些我不理解"为什么"的东西.
int i = 6;
int j;
int *ptr = &i;
int *ptr1 = &j
j = i++;
//now j == 6 and i == 7. Straightforward.
Run Code Online (Sandbox Code Playgroud)
如果您将操作员放在等号的左侧怎么办?
++ptr = ptr1;
Run Code Online (Sandbox Code Playgroud)
相当于
(ptr = ptr + 1) = ptr1;
Run Code Online (Sandbox Code Playgroud)
而
ptr++ = ptr1;
Run Code Online (Sandbox Code Playgroud)
相当于
ptr = ptr + 1 = ptr1;
Run Code Online (Sandbox Code Playgroud)
后缀运行编译错误,我得到它.你在赋值运算符的左侧有一个常量"ptr + 1".很公平.
一个编译的前缀和C++中的WORKS.是的,我理解它很乱,你正在处理未分配的内存,但它可以工作和编译.在C中,这不会编译,返回与后缀"左值作为赋值的左操作数所需的左值"相同的错误.无论如何编写,使用两个"="运算符或"++ ptr"语法扩展,都会发生这种情况.
C如何处理这样的赋值和C++如何处理它有什么区别?
T.C*_*.C. 73
在C和C++中,结果x++都是rvalue,因此您无法分配它.
在C中,++x相当于x += 1(C标准§6.5.3.1/ p2;所有C标准引用均为WG14 N1570).在C++中,++x等同于x += 1if x不是bool(C++标准§5.3.2[expr.pre.incr]/p1;所有C++标准引用都是WG21 N3936).
在C中,赋值表达式的结果是rvalue(C标准§6.5.16/ p3):
赋值运算符将值存储在左操作数指定的对象中.赋值表达式在赋值后具有左操作数的值,但不是左值.
因为它不是左值,你不能赋值给它:(C标准§6.5.16/ p2 - 注意这是一个约束)
赋值运算符应具有可修改的左值作为其左操作数.
在C++中,赋值表达式的结果是左值(C++标准§5.17[expr.ass]/p1):
赋值运算符(=)和复合赋值运算符都是从右到左分组.所有都需要一个可修改的左值作为左操作数,并返回一个左值操作数的左值.
因此++ptr = ptr1;,C中的可诊断约束违规也是如此,但不违反C++中的任何可诊断规则.
但是,pre-C++ 11 ++ptr = ptr1;具有未定义的行为,因为它ptr在两个相邻序列点之间修改两次.
在C++ 11中,行为++ptr = ptr1变得很明确.如果我们将其重写为更清楚的话
(ptr += 1) = ptr1;
Run Code Online (Sandbox Code Playgroud)
从C++ 11开始,C++标准提供了(§5.17[expr.ass]/p1)
在所有情况下,在右和左操作数的值计算之后,以及在赋值表达式的值计算之前,对赋值进行排序.对于不确定序列的函数调用,复合赋值的操作是单个评估.
因此,由执行分配=的值计算后测序ptr += 1和ptr1.由+=值执行之前对其执行的赋值进行排序ptr += 1,并且+=在该赋值之前必须对所需的所有值计算进行排序.因此,这里的排序是明确定义的,并且没有未定义的行为.
Sha*_*our 17
在C中,前后增量的结果是rvalues,我们不能赋值给rvalue,我们需要一个左值(另见:理解C和C++中的左值和右值).我们可以看到草案C11标准部分6.5.2.4 Postfix增量和减量运算符(强调我的前进):
postfix ++运算符的结果是操作数的值.[...]有关约束,类型和转换以及操作对指针的影响的信息,请参阅加法运算符和复合赋值的讨论.[...]
所以递增后的结果是一个值,它是同义词右值,我们可以通过将部分确认此6.5.16 赋值运算符,其上段为我们指出了约束和结果的进一步理解,它说:
[...]赋值表达式在赋值后具有左操作数的值,但不是左值.[...]
这进一步证实了后增量的结果不是左值.
对于预增量,我们可以从章节6.5.3.1 前缀增量和减量运算符中看到:
[...]有关约束,类型,副作用和转换以及操作对指针的影响的信息,请参阅加法运算符和复合赋值的讨论.
也指回6.5.16后增量,因此C中预增量的结果也不是左值.
在C++中,后递增也是一个rvalue,更具体地说是一个prvalue,我们可以通过转到5.2.6 增量和减量部分来证实这一点:
[...] 结果是一个prvalue.结果的类型是操作数类型的cv-nonqualified版本[...]
关于预增量C和C++的不同.在C中,结果是一个rvalue,而在C++中,结果是一个左值,它解释了为什么++ptr = ptr1;在C++中工作而不是C.
对于C++,这将在5.3.2 增量和减量部分中介绍,其中说:
[...]结果是更新的操作数; 它是一个左值,如果操作数是一个位域,它是一个位域.[...]
要了解是否:
++ptr = ptr1;
Run Code Online (Sandbox Code Playgroud)
在C++中我们需要两种不同的方法,一种用于预C++ 11,另一种用于C++ 11.
Pre C++ 11此表达式调用未定义的行为,因为它在同一序列点中多次修改对象.我们可以通过转到Pre C++ 11草案标准部分5 表达式来看到这一点:
除非另有说明,否则单个运算符的操作数和各个表达式的子表达式的评估顺序以及副作用的发生顺序是未指定的.57)在前一个和下一个序列点之间,标量对象的存储值应该被修改最多一次通过表达式的评价. 此外,只能访问先前值以确定要存储的值.对于完整表达式的子表达式的每个允许排序,应满足本段的要求; 否则行为未定义.[例如:
Run Code Online (Sandbox Code Playgroud)i = v[i ++]; / / the behavior is undefined i = 7 , i++ , i ++; / / i becomes 9 i = ++ i + 1; / / the behavior is undefined i = i + 1; / / the value of i is incremented- 末端的例子]
我们正在递增ptr然后分配给它,这是两个修改,在这种情况下,序列点出现在表达式之后的表达式的末尾;.
对于C + 11,我们应该去缺陷报告637:排序规则和示例不同意,这是导致的缺陷报告:
i = ++i + 1;
Run Code Online (Sandbox Code Playgroud)
在C++ 11中成为明确定义的行为,而在C++ 11之前,这是未定义的行为.本报告中的解释是我见过的最好的解释之一,多次读它是有启发性的,并帮助我以新的眼光理解了许多概念.
导致此表达式变为明确定义的行为的逻辑如下:
在LHS和RHS(5.17 [expr.ass]第1段)的值计算之后,需要对分配副作用进行排序.
LHS(i)是左值,因此其值计算涉及计算i的地址.
为了计算RHS(++ i + 1)的值,有必要首先对左值表达式++ i进行值计算,然后对结果进行左值到右值的转换.这保证了在计算加法运算之前对递增副作用进行排序,加法运算又在赋值副作用之前进行排序.换句话说,它为此表达式生成明确定义的顺序和最终值.
逻辑有点类似于:
++ptr = ptr1;
Run Code Online (Sandbox Code Playgroud)
在分配副作用之前,对LHS和RHS的值计算进行排序.
RHS是左值,因此其值计算涉及计算ptr1的地址.
为了计算LHS(++ ptr)的值,有必要首先对左值表达式++ ptr进行值计算,然后对结果进行左值到右值的转换.这保证了在赋值副作用之前对递增副作用进行排序.换句话说,它为此表达式生成明确定义的顺序和最终值.
注意
OP说:
是的,我理解它很乱,你正在处理未分配的内存,但它可以工作和编译.
指向非数组对象的指针被认为是加法运算符的大小为1的数组,我将引用C++标准草案,但C11几乎具有完全相同的文本.从部分5.7 添加剂运算符:
出于这些运算符的目的,指向非阵列对象的指针与指向长度为1的数组的第一个元素的指针的行为相同,其中对象的类型为其元素类型.
并且进一步告诉我们,只要你不取消引用指针,指向一个数组的结尾就是有效的:
[...]如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则评估不应产生溢出; 否则,行为未定义.
所以:
++ptr ;
Run Code Online (Sandbox Code Playgroud)
仍然是一个有效的指针.