繁体   English   中英

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

[英]C signed arithmetic: difference in representation between literals and variables

以下程序:

#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(否则会产生真正的价值)

溢出有signed整数类型的行为在C中是未定义的。

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

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

正如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类型的参数。 你给它一个类型(signed) long int 特别是,你给它一个负值。

通常,如果将数字参数传递给期望不同数值类型的函数,则将转换该值。 这是可能的,因为编译器知道函数期望的类型。 但是,对于printf ,期望的类型由格式字符串确定,格式字符串在运行时之前不一定(必须)处理。 它可能会解释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具有正值,只是它打印错误。

如果%lxlongunsigned long的范围内, 可以使用%lx打印有符号值。 没有格式说明符将以十六进制格式打印任意签名值。

暂无
暂无

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

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