C 编译器是否假设有符号整数的加法是可交换的?
Does C compiler assume the addition of signed integers is commutative?
我正在尝试检查带符号的加法是否会溢出。通常,检查 if
int a + int b
会溢出(a 和 b 都是正数),我检查是否
if (a > INT_MAX - b)
但现在我想检查是否
int a + int b - int c
会溢出。我知道 a、b 和 c 是正数并且 b >= c,所以我进行以下检查:
if (a > INT_MAX - b + c)
现在我的问题是,编译器可以重写
INT_MAX - b + c to INT_MAX + c - b ?
我担心的是,它会先执行 INT_MAX + c,这可能会溢出并导致未定义的行为。
嗯,我们可以假设不会。
但是"The C language standard doesn't specify operator precedence."
但我会在括号中加上,因为即使有 很小的机会 优先级也会混乱。
引自:
"compilers are built by smart people and do smart things" and thus, can never go wrong.
在胡说八道。
如果这是一个问题,也应该提高可读性。
在推理未定义的行为时考虑 "the compiler" 的作用是一种谬误。编译器是透明的。该行为在您的代码中,而不是在编译器中。你应该问“我的代码INT_MAX - b + c
是什么意思?它会溢出吗?答案永远不会在"the compiler",但在标准中。
标准只要求出现在程序中的个别操作不会溢出。它从不提及任何未明确出现在您的程序中的重写表达式。 INT_MAX - b + c
在您的程序中,相当于 (INT_MAX - b) + c
。所以要求是(INT_MAX - b)
不溢出,然后加在c
上的结果不溢出。 INT_MAX + c - b
没有出现在你的程序中,所以你不用担心。
如果编译器以任何方式重写您的表达式,它必须根据 as-if 规则确保重写的表达式与您的表达式具有相同的可见行为。因此,如果它确实将 INT_MAX - b + c
替换为 INT_MAX + c - b
,它必须确保溢出不会发生或被透明地处理(例如被硬件忽略)。
表达式a - b + c
等价于(a - b) + c
。这是在语言的语法中编码的,这里最相关的子句是 6.5.6:1(当然你必须查看整个语法才能理解这个子句)。
additive-expression:
multiplicative-expression
additive-expression + multiplicative-expression
additive-expression - multiplicative-expression
当遇到a - b + c
时,编译器只能将其解析为加法表达式a - b
和乘法表达式c
的和。没有其他规则可以适用。所以 a - b + c
是 a - b
和 c
.
的总和
编译器可以自由生成它认为最适合您提供的源代码的汇编代码,但必须保留程序的含义。如果您编写了为 a = INT_MAX
、b = 2
和 c = 1
定义的源代码,则汇编代码必须为这些值提供正确的答案。如果编译器选择重新排序操作,它只会以保留含义的方式这样做,例如因为它知道目标体系结构的汇编指令会产生二进制补码结果并且可以重新排序以达到相同的结果。
我正在尝试检查带符号的加法是否会溢出。通常,检查 if
int a + int b
会溢出(a 和 b 都是正数),我检查是否
if (a > INT_MAX - b)
但现在我想检查是否
int a + int b - int c
会溢出。我知道 a、b 和 c 是正数并且 b >= c,所以我进行以下检查:
if (a > INT_MAX - b + c)
现在我的问题是,编译器可以重写
INT_MAX - b + c to INT_MAX + c - b ?
我担心的是,它会先执行 INT_MAX + c,这可能会溢出并导致未定义的行为。
嗯,我们可以假设不会。
但是"The C language standard doesn't specify operator precedence."
但我会在括号中加上,因为即使有 很小的机会 优先级也会混乱。
引自:
"compilers are built by smart people and do smart things" and thus, can never go wrong.
在胡说八道。
如果这是一个问题,也应该提高可读性。
在推理未定义的行为时考虑 "the compiler" 的作用是一种谬误。编译器是透明的。该行为在您的代码中,而不是在编译器中。你应该问“我的代码INT_MAX - b + c
是什么意思?它会溢出吗?答案永远不会在"the compiler",但在标准中。
标准只要求出现在程序中的个别操作不会溢出。它从不提及任何未明确出现在您的程序中的重写表达式。 INT_MAX - b + c
在您的程序中,相当于 (INT_MAX - b) + c
。所以要求是(INT_MAX - b)
不溢出,然后加在c
上的结果不溢出。 INT_MAX + c - b
没有出现在你的程序中,所以你不用担心。
如果编译器以任何方式重写您的表达式,它必须根据 as-if 规则确保重写的表达式与您的表达式具有相同的可见行为。因此,如果它确实将 INT_MAX - b + c
替换为 INT_MAX + c - b
,它必须确保溢出不会发生或被透明地处理(例如被硬件忽略)。
表达式a - b + c
等价于(a - b) + c
。这是在语言的语法中编码的,这里最相关的子句是 6.5.6:1(当然你必须查看整个语法才能理解这个子句)。
additive-expression:
multiplicative-expression
additive-expression + multiplicative-expression
additive-expression - multiplicative-expression
当遇到a - b + c
时,编译器只能将其解析为加法表达式a - b
和乘法表达式c
的和。没有其他规则可以适用。所以 a - b + c
是 a - b
和 c
.
编译器可以自由生成它认为最适合您提供的源代码的汇编代码,但必须保留程序的含义。如果您编写了为 a = INT_MAX
、b = 2
和 c = 1
定义的源代码,则汇编代码必须为这些值提供正确的答案。如果编译器选择重新排序操作,它只会以保留含义的方式这样做,例如因为它知道目标体系结构的汇编指令会产生二进制补码结果并且可以重新排序以达到相同的结果。