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

时间:2018-02-06 16:30:08

标签: c

以下计划:

#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");
}

输出以下内容:

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

为什么表达式(max + 1)不等于LONG_MIN?
那等于什么呢? 如果我尝试printf max + 1作为表达式,我会得到8000000000000000

真的对此感到困惑!

- 编辑

如下面的评论和答案中所讨论的,签名溢出是未定义的,编译器可以随心所欲,因此我们没有任何商业质疑它的奇怪之处。

受到以下评论的启发,我检查了我的平台生成的汇编代码(运行ubuntu的linode上的gcc),发生的事情是编译器只是在不执行实际相等的情况下将最后一个等式的结果判定为false检查(否则会产生真正的价值)

2 个答案:

答案 0 :(得分:3)

在C中溢出signed整数类型的行为是 undefined

根据max + 1的评估,您的输出是该未定义行为的表现形式。 (形式上这意味着您的整个程序未定义。)

请注意,LONG_MIN可能会定义为-<value> - 1,其中<value>LONG_MAX的编号相同。这避免了2补体系统中的任何溢出或参数促销效应。

答案 1 :(得分:0)

正如Bathsheba的答案正确指出的那样,有符号整数溢出具有未定义的行为。

话虽如此,在大多数实现中,评估LONG_MAX + 1的结果可能是LONG_MIN。您在打印时没有看到负值的原因是您不正确地打印它。

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);

%lx说明符需要类型为unsigned long int的参数。你给它一个类型(签名)long int的参数。特别是,你给它一个负值。

通常,如果将数字参数传递给期望不同数值类型的函数,则将转换该值。这是可能的,因为编译器知道函数期望的类型。但是,对于printf,期望的类型由格式字符串确定,格式字符串不一定(必要)处理直到运行时。它可能会像{/ em>一样解释long int对象的内容 它是unsigned long int类型的对象,但严格来说,行为是未定义的。

这是一个更简单的示例,其中打印的数字设置为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);
}

我系统的输出是:

num = -9223372036854775808
num = 0x8000000000000000 (undefined behavior)

第二个printf打印的正值并不表示num具有正值,只是表示打印不正确。

可以使用%lx打印已签名的值,如果它在long和{{1}的范围内}}。没有格式说明符可以十六进制打印任意有符号值。