gpt4 book ai didi

c++ - 该程序中的 16 位数学是否调用了未定义的行为?

转载 作者:可可西里 更新时间:2023-11-01 17:44:11 25 4
gpt4 key购买 nike

前几天,我将我的 Windows 构建环境从 MSVC2013 升级到 MSVC2017,你瞧,我的程序中一个多年来一直运行良好(并且在 g++/clang 下仍然运行良好)的函数突然开始给出不正确的结果使用 MSVC2017 编译。

我能够重写函数以再次给出正确的结果,但这段经历让我感到好奇——我的函数是调用了未定义的行为(直到现在恰好给出了正确的结果),还是代码定义明确并且MSVC2017 有问题吗?

下面是一个简单的程序,显示了我重写前后的函数玩具版本。特别是,函数 maybe_invokes_undefined_behavior()(如下所示)在使用值为 -32762 的参数调用时是否会调用未定义的行为?

#include <stdio.h>

enum {ciFirstToken = -32768};

// This function sometimes gives unexpected results under MSVC2017
void maybe_invokes_undefined_behavior(short token)
{
if (token >= 0) return;

token -= ciFirstToken; // does this invoke undefined behavior if (token==-32762) and (ciFirstToken==-32768)?
if (token == 6)
{
printf("Token is 6, as expected (unexpected behavior not reproduced)\n");
}
else
{
printf("Token should now be 6, but it's actually %i\n", (int) token); // under MSVC2017 this prints -65530 !?
}
}

// This function is rewritten to use int-math instead of short-math and always gives the expected result
void allgood(short token16)
{
if (token16 >= 0) return;

int token = token16;
token -= ciFirstToken;
if (token == 6)
{
printf("Token is 6, as expected (odd behavior not reproduced)\n");
}
else
{
printf("Token should now be 6, but it's actually %i\n", (int) token);
}
}

int main(int, char **)
{
maybe_invokes_undefined_behavior(-32762);
allgood(-32762);
return 0;
}

最佳答案

does this invoke undefined behavior if (token==-32762) and (ciFirstToken==-32768)?

token -= ciFirstToken;

否(简短回答)

现在让我们逐个分解。

1) 根据 expr.ass对于复合赋值,-=:

The behavior of an expression of the form E1 op= E2 is equivalent to E1 = E1 op E2 except that E1 is evaluated only once.

表达式:

token -= ciFirstToken;

相当于:

token = token - ciFirstToken;
// ^ binary (not unary)

2) additive operator (-) 执行 usual arithmetic conversion对于算术类型的操作数。

根据 expr.arith.conv/1

Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. This pattern is called the usual arithmetic conversions, which are defined as follows:

(1.5) Otherwise, the integral promotions shall be performed on both operands.

3) 然后将两个操作数提升为 int

根据 conv.prom/1 :

A prvalue of an integer type other than bool, char16_­t, char32_­t, or wchar_­t whose integer conversion rank is less than the rank of int can be converted to a prvalue of type int if int can represent all the values of the source type;

4) 整数提升后,不需要进一步转换。

根据 expr.arith.conv/1.5.1

If both operands have the same type, no further conversion is needed.

5) 最后,表达式的未定义行为按照expr.pre 定义:

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined


结论

现在替换值:

token = -32762 - (-32768);

在所有整数提升之后,两个操作数都在INT_MIN[1]INT_MAX[2] 的有效范围内.

并且在评估之后,数学结果 (6) 然后被隐式转换为 short,这在 short 的有效范围内。

因此,表达式是良构的

非常感谢@MSalters、@n.m 和@Arne Vogel 帮助回答这个问题。


Visual Studio 2015 MSVC14 Integer LimitsMS Integer Limits定义:

[1] INT_MIN -2147483648
[2] INT_MAX +2147483647

SHRT_MIN –32768
SHRT_MAX +32767

关于c++ - 该程序中的 16 位数学是否调用了未定义的行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51033539/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com