在C和C++语言中,该arr[i] = i++;语句调用未定义的行为.为什么语句i = i + 1;不会调用未定义的行为?
对于打包f0并f1放入同一字节的实现,下面的程序是定义的吗?
struct S0 {
unsigned f0:4;
signed f1:4;
} l_62;
int main (void) {
(l_62.f0 = 0) + (l_62.f1 = 0);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我对C99和C11的答案感兴趣,如果有理由认为它在那里是不同的.
在C99中,我发现的只有6.5:2:
在前一个和下一个序列点之间,一个对象的存储值最多只能通过表达式的评估来修改一次.[...]
我不清楚这一段对上述计划有何影响.
基于大量随机测试,大多数编译器似乎生成代码,其中两个分配不会干扰.
维基百科说在C++ 11中不推荐使用序列点.那是什么意思?这是否意味着由序列点引起的未定义行为没有影响?
很抱歉再次打开这个话题,但是考虑这个话题本身已经开始给我一个未定义的行为.想要进入定义明确的行为区域.
特定
int i = 0;
int v[10];
i = ++i; //Expr1
i = i++; //Expr2
++ ++i; //Expr3
i = v[i++]; //Expr4
Run Code Online (Sandbox Code Playgroud)
我认为上面的表达式(按此顺序)为
operator=(i, operator++(i)) ; //Expr1 equivalent
operator=(i, operator++(i, 0)) ; //Expr2 equivalent
operator++(operator++(i)) ; //Expr3 equivalent
operator=(i, operator[](operator++(i, 0)); //Expr4 equivalent
Run Code Online (Sandbox Code Playgroud)
现在来到这里的行为是来自C++ 0x的重要引用.
$ 1.9/12-"表达式(或子表达式)的评估通常包括值计算(包括确定用于左值评估的对象的身份以及获取先前分配给对象以进行右值评估的值)和启动副作用".
$ 1.9/15-"如果标量对象的副作用相对于同一标量对象的另一个副作用或使用相同标量对象的值计算的值没有排序,则行为未定义."
[注意:与不同参数表达式相关的值计算和副作用未被排序. - 尾注]
$ 3.9/9-"算术类型(3.9.1),枚举类型,指针类型,指向成员类型的指针(3.9.2),std :: nullptr_t和这些类型的cv限定版本(3.9.3)统称为标量类型."
在Expr1中,对表达式i(第一个参数)的评估在对评估operator++(i)(具有副作用)方面没有考虑.
因此,Expr1具有未定义的行为.
在Expr2中,对表达式i(第一个参数)的评估在对评估operator++(i, 0)(具有副作用)的评估方面没有考虑.
因此,Expr2具有未定义的行为.
在Expr3中,在调用operator++(i)外部之前,必须完成对单个参数的评估operator++.
因此,Expr3具有良好定义的行为. …
假设一个具有以下原型的功能
template<typename T>
std::unique_ptr<T> process_object(std::unique_ptr<T> ptr);
Run Code Online (Sandbox Code Playgroud)
该函数可以返回传递给它的对象(移动版本)或完全不同的对象.
使用此函数是合法的C++如下吗?
std::unique_ptr<Widget> pw(new Widget());
pw = process_object(std::move(pw));
Run Code Online (Sandbox Code Playgroud)
如果我没记错的话,有一个C/C++规则禁止在一个完整的表达式中多次修改一个对象.这条规则适用于此吗?如果是的话,有没有办法在一行中以不同的方式表达这个习语?
如果一个人std::unique_ptr被鄙视取代std::auto_ptr怎么办?
如果我写的话f(x)->g(args, ...)可以f(x)在评估之前依赖序列点args, ...吗?我可以通过两种方式看到论点:
this,好像我写的g(f(x), args, ...)那样表明它就像一个参数,因此未指定.该->操作是不正常的二元运算符,因为显然g(...) 不能被评估之前f(x),如果我写的是它可以f(x) + g(...).我很惊讶我找不到一些具体的说法.
简单描述问题,请看下面的代码:
int main()
{
int a=123;
({if (a) a=0;});
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我收到了[-Wsequence-point]的警告
Line 4: warning: operation on 'a' may be undefined
Run Code Online (Sandbox Code Playgroud)
我的g ++版本是4.4.5
无论谁解释这个简单的问题,我都会感激不尽.
顺便说一句,你可以在这个中文网站的#7中找到我的原始程序和原始问题(不是必需的)
UPD1:
虽然改变代码({if(a) a=0; a;})可以避免警告,但我认识到问题的真正原因可能不是The last thing in the compound statement should be an expression followed by a semicolon.
因为纪录片还说If you use some other kind of statement last within the braces, the construct has type void, and thus effectively no value.
一个例子可以显示它:
int main()
{
int a=123, …Run Code Online (Sandbox Code Playgroud) C99§6.5 表达式
(1)表达式是操作符和操作数的序列,其指定值的计算,或指定对象或函数,或者生成副作用,或执行其组合.
(2)在前一个和下一个序列点之间,一个对象的存储值最多只能通过表达式的评估来修改一次.72)此外,先前值应只读以确定要存储的值.73)
用脚注
72)浮点状态标志不是对象,可以在表达式中多次设置.
73)此段落呈现未定义的语句表达式,如
Run Code Online (Sandbox Code Playgroud)i = ++i + 1; a[i++] = i;允许的同时
Run Code Online (Sandbox Code Playgroud)i = i + 1; a[i] = i;
C11§6.5改为((1)的文本有附录):
(1)[...]运算符的操作数的值计算在运算符的结果的值计算之前被排序.
(2)如果对标量对象的副作用相对于对同一标量对象的不同副作用或使用相同标量对象的值进行的值计算未被排序,则行为未定义.如果表达式的子表达式有多个允许的排序,则如果在任何排序中发生这种未测序的副作用,则行为是不确定的.84)
C11中的脚注84与C99中的73相同.
我有点困惑......我把C11(2)读作"[...](对同一标量对象的不同副作用)或(使用相同标量对象的值进行值计算)[...]"似乎甚至不允许foo = ++i(有副作用,我们根据更改的对象使用值).不过,我不是母语人士,所以如果能告诉我这句话应该如何"解析"会更好.我理解C99,但我不太明白C11的措辞.
无论如何,实际问题:这是从C99到C11的变化,还是这些措辞相当?如果是这样,为什么它会被改变?如果没有,有人可以给出一个表达式的例子,这个表达式在C99中是UB而在C11中不是,反之亦然?
据称"巧妙"(但实际上效率低下)交换两个整数变量而不是使用临时存储的方式通常涉及这一行:
int a = 10;
int b = 42;
a ^= b ^= a ^= b; /*Here*/
printf("a=%d, b=%d\n", a, b);
Run Code Online (Sandbox Code Playgroud)
但我想知道,复合赋值运算符^=不是序列点,是吗?这是否意味着它实际上是未定义的行为?
sequence-points ×10
c++ ×6
c ×5
c++11 ×2
c11 ×2
c99 ×2
bit-fields ×1
c++14 ×1
deprecated ×1
expression ×1
side-effects ×1