整数算术产生一个奇怪的结果(除法后舍入?)

lau*_*une 12 c

在Linux上使用gcc版本4.8.4,short是16位,int是32位.

#include "stdio.h"
int main( void ){
  unsigned short u = 0xaabb;
  unsigned int   v = 0xaabb;
  printf ("%08x %08x\n", u, (unsigned short)((u*0x10001)/0x100));
  printf ("%08x %08x\n", v, (unsigned short)((v*0x10001)/0x100));
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

结果:

0000aabb 0000bbab
0000aabb 0000bbaa
Run Code Online (Sandbox Code Playgroud)

这可以改变,例如,通过除以0x10,其为第一种情况产生类似的结果(+1).如果截断的字节/0x100小于0x80 ,则不会发生这种效应.第一种情况(short u)的机器代码看起来好像是要进行一些舍入(添加0xFF).

  1. 结果是什么原因还是错误?
  2. 其他编译器的结果是什么?

Lun*_*din 14

类似的文字0x10001将是类型int(如果它可以适合int,在这种情况下是正确的).int是签名类型.

由于变量u是一个小整数类型,因此int无论何时在表达式中使用,它都会被提升为整数.

0xaabb * 0x10001应该会给出结果0xAABBAABB.但是,这个结果太大了,无法容纳在int32位二进制补码系统中,其中a的最大数字int0x7FFFFFFF.所以你得到一个有符号整数的溢出,因此调用未定义的行为 - 任何事情都可能发生.

在进行任何形式的二进制算术时,切勿使用有符号整数!

此外,最终的强制转换(unsigned short)是徒劳的,因为printf参数int无论如何都会提升传递的值.严格说来也是不正确的,因为预期的%x意思printfunsigned int.

为了避免C中不可预测和有限的默认整数类型的所有问题,请stdint.h改为使用.此外,使用unsigned int literals解决了许多隐式类型提升错误.

例:

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

int main( void ){
  uint16_t u = 0xaabb;
  uint16_t v = 0xaabb;
  printf ("%08" PRIx16 " %08" PRIx16 "\n", u, (uint16_t)(u*0x10001u/0x100u));
  printf ("%08" PRIx16 " %08" PRIx16 "\n", v, (uint16_t)(v*0x10001u/0x100u));
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

(此代码也将具有参数提升,但是通过使用PRIx16格式说明符,您可以告诉printf现在编译器的业务是使代码工作,而不管函数调用中可能存在哪种类型的促销.)