这是 C 中未定义的行为吗?

s96*_*s96 1 c side-effects undefined-behavior logical-or logical-and

该表达式b = 5 << sizeof(a++);是未定义的行为。但这是c = a && b << --a;c = a || b << a++;也是一种未定义的行为吗?我认为这是未定义的行为,但我不确定。

Eri*_*hil 7

他说“我们不应该在 sizeof 运算符的操作数中产生任何副作用”[根据评论,显然原因b = 5 << sizeof(a++);据说有未定义的行为。]

如果老师这么说是因为这种副作用可能会导致未定义的行为,那么他们就错了。除非是可变长度数组类型 (C 2018 6.5.3.4 2),否则不会计算 的操作数sizeof,因此 不会产生副作用sizeof(a++)。此外,即使对表达式进行了求值,也没有规则表明操作数中仅仅存在副作用就会sizeof导致未定义的行为。

显示的语句本身都没有未定义的行为。( 、和a的一些定义和值bc可能会导致未定义的行为。)

在 中, , ,c = a && b << --a;的左操作数被指定为在右操作数 之前求值,并且它们之间有一个序列点 (C 2018 6.5.13 4),因此修改和也用在左操作数中的事实确实如此不触发规则(C 2018 6.5 2):如果标量对象的副作用相对于其值的使用是无序的,则行为是未定义的。&&ab << --a--aaa

同样,在 中,左操作数和右操作数c = a || b << a++;的求值之间存在一个序列点(C 2018 6.5.14 4)。ab << a++


Vla*_*cow 5

这个说法

b = 5 << sizeof(a++);
Run Code Online (Sandbox Code Playgroud)

是一个有效的表达式语句。不存在未定义的行为。请注意,运算符中使用的表达式sizeof不会被求值。

在一种情况下,此语句中可能会出现未定义的行为(C 标准。6.5.7 位移位运算符)

如果右操作数的值为负数或大于或等于提升的左操作数的宽度,则行为未定义。

size_t但带有运算符的表达式的类型sizeof 是无符号整数类型,其值不能为负数。

在另外两个声明中

c = a && b << --a; 
Run Code Online (Sandbox Code Playgroud)

c = a || b << a++;
Run Code Online (Sandbox Code Playgroud)

相当于

c = ( a ) && ( b << --a ); 
Run Code Online (Sandbox Code Playgroud)

c = ( a ) || ( b << a++ );
Run Code Online (Sandbox Code Playgroud)

两者都没有未定义的行为,因为在逻辑运算符 AND 和 OR 的第一个操作数求值之后存在一个序列点。

来自 C 标准(6.5.13 逻辑 AND 运算符)

4 与按位二元 & 运算符不同,&& 运算符保证从左到右求值;如果对第二操作数求值,则在第一操作数和第二操作数的求值之间存在序列点。如果第一个操作数比较等于 0,则不计算第二个操作数。

和(6.5.14 逻辑或运算符)

4 与按位 | 不同 运算符,|| 运算符保证从左到右评估;如果计算第二个操作数,则在第一个和第二个操作数的计算之间存在序列点。如果第一个操作数比较后不等于 0,则不计算第二个操作数。

未定义的行为只能发生在带有移位运算符的子表达式中,前提是当表达式--a或的值为a++负或太大时将计算这些操作数。请参阅答案开头的 C 标准引用。

这是正确的演示程序

#include <stdio.h>

int main( void ) 
{
    int a = 1;
    int b = 10;

    int c = a && b << --a;

    printf ( "a = %d, b = %d, c = %d\n", a, b, c );

    c = a || b << a++;

    printf ( "a = %d, b = %d, c = %d\n", a, b, c );
}
Run Code Online (Sandbox Code Playgroud)

程序输出是

a = 0, b = 10, c = 1
a = 1, b = 10, c = 1
Run Code Online (Sandbox Code Playgroud)

在这两个表达式中

b << --a
Run Code Online (Sandbox Code Playgroud)

b << a++
Run Code Online (Sandbox Code Playgroud)

左移运算符右操作数的值等于0。因此表达式的值等于,10因为没有进行移位。