简体   繁体   English

C签名算术:文字和变量之间的表示差异

[英]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 . 如果%lxlongunsigned long的范围内, 可以使用%lx打印有符号值。 There is no format specifier that will print an arbitrary signed value in hexadecimal. 没有格式说明符将以十六进制格式打印任意签名值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM