[英]C signed arithmetic: difference in representation between literals and variables
The following program: 以下程序:
#include <limits.h>
#include <stdio.h>
int main() {
long int max = LONG_MAX;
long int max_plus_one = max + 1;
printf(" max \t\t %lx\n LONG_MAX \t %lx\n", max, LONG_MAX);
printf(" max_plus_one \t %lx\n max + 1 \t %lx\n LONG_MIN \t %lx\n\n",\
max_plus_one, max + 1, LONG_MIN);
printf(" max == LONG_MAX? \t\t %s\n", max == LONG_MAX ? "true" : "false");
printf(" max_plus_one == LONG_MIN ? \t %s\n",\
max_plus_one == LONG_MIN ? "true" : "false");
printf(" max + 1 == max_plus_one? \t %s\n",\
max + 1 == max_plus_one ? "true" : "false");
printf(" max + 1 == LONG_MIN? \t\t %s\n", max + 1 == LONG_MIN ? "true" : "false");
}
Outputs the following: 输出以下内容:
max 7fffffffffffffff
LONG_MAX 7fffffffffffffff
max_plus_one 8000000000000000
max + 1 8000000000000000
LONG_MIN 8000000000000000
max == LONG_MAX? true
max_plus_one == LONG_MIN ? true
max + 1 == max_plus_one? true
max + 1 == LONG_MIN? false
Why is the expression (max + 1) not equal to LONG_MIN? 为什么表达式(max + 1)不等于LONG_MIN?
What is it equal to then? 那等于什么呢?
If I try to printf max + 1 as an expression I do get 8000000000000000 如果我尝试printf max + 1作为表达式,我会得到8000000000000000
Really baffled about this! 真是让人感到困惑!
--EDIT - 编辑
As discussed in the comments and answers below signed overflow is undefined and the compiler can do as it pleases, so we have no business questioning its oddities. 正如下面的评论和答案中所讨论的那样,签名溢出是未定义的,编译器可以随心所欲,因此我们没有任何商业质疑它的奇怪之处。
Inspired by the comments below I checked the assembly code generated by my platform (gcc on a linode running ubuntu) and what is happening is that the compiler is simply deciding the result of the last equality to be false without performing the actual equality check (which would otherwise result in a true value) 受到下面评论的启发,我检查了我的平台生成的汇编代码(运行ubuntu的linode上的gcc),发生的事情是编译器只是在没有执行实际的相等性检查的情况下将最后一次相等的结果判定为false(否则会产生真正的价值)
The behaviour on overflowing a signed
integral type is undefined in C. 溢出有signed
整数类型的行为在C中是未定义的。
Your outputs that are contingent on the evaluation of max + 1
are manifestations of that undefined behaviour. 您的输出取决于对max + 1
的评估,是该未定义行为的表现形式。 (Formally this means that your entire program is undefined.) (形式上这意味着您的整个程序未定义。)
Note that LONG_MIN
will probably be defined as -<value> - 1
where <value>
is the same number as LONG_MAX
. 请注意, LONG_MIN
可能定义为-<value> - 1
,其中<value>
与LONG_MAX
数字相同。 This obviates any overflow or argument promotion effects in a 2's complement system. 这避免了2的补码系统中的任何溢出或参数促销效应。
As Bathsheba's answer correctly points out, signed integer overflow has undefined behavior. 正如Bathsheba的答案正确指出的那样,有符号整数溢出具有未定义的行为。
Having said that, the result of evaluating LONG_MAX + 1
is likely to be LONG_MIN
on most implementations. 话虽如此,在大多数实现中,评估LONG_MAX + 1
的结果可能是LONG_MIN
。 The reason you're not seeing a negative value when you print it is that you're printing it incorrectly. 打印时没有看到负值的原因是您打印错误。
long int max = LONG_MAX;
long int max_plus_one = max + 1;
printf(" max \t\t %lx\n LONG_MAX \t %lx\n", max, LONG_MAX);
printf(" max_plus_one \t %lx\n max + 1 \t %lx\n LONG_MIN \t %lx\n\n",\
max_plus_one, max + 1, LONG_MIN);
The %lx
specifier requires an argument of type unsigned long int
. %lx
说明符需要unsigned long int
类型的参数。 You're giving it an argument of type (signed) long int
. 你给它一个类型(signed) long int
。 In particular, you're giving it a negative value. 特别是,你给它一个负值。
Usually if you pass a numeric argument to a function expecting a different numeric type, the value will be converted. 通常,如果将数字参数传递给期望不同数值类型的函数,则将转换该值。 That's possible because the compiler knows what type the function expects. 这是可能的,因为编译器知道函数期望的类型。 For printf
, however, the expected type is determined by the format string, which isn't (necessarily) processed until run time. 但是,对于printf
,期望的类型由格式字符串确定,格式字符串在运行时之前不一定(必须)处理。 It's likely to interpret the contents of the long int
object as if it were an object of type unsigned long int
, but strictly speaking the behavior is undefined. 它可能会解释long int
对象的内容, 就好像它是unsigned long int
类型的对象,但严格来说,行为是未定义的。
Here's a simpler example where the number being printed was set to LONG_MIN
without any overflow: 这是一个更简单的示例,其中打印的数字设置为LONG_MIN
而没有任何溢出:
#include <stdio.h>
#include <limits.h>
int main(void) {
unsigned long num = LONG_MIN;
printf("num = %ld\n", num);
printf("num = 0x%lx (undefined behavior)\n", num);
}
The output on my system is: 我系统的输出是:
num = -9223372036854775808
num = 0x8000000000000000 (undefined behavior)
The positive value printed by the second printf
does not indicate that num
has a positive value, just that it's being printed incorrectly. 第二个printf
打印的正值并不表示num
具有正值,只是它打印错误。
You can use %lx
to print a signed value if it's within the range of both long
and unsigned long
. 如果%lx
在long
和unsigned long
的范围内, 则 可以使用%lx
打印有符号值。 There is no format specifier that will print an arbitrary signed value in hexadecimal. 没有格式说明符将以十六进制格式打印任意签名值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.