投下负数

时间:2013-04-05 03:28:56

标签: c casting

我正在尝试解释十六进制字节并将它们合并为有用的信息。

void processData(){
  signed char data;
  unsigned long a;//holds four bytes
  unsigned long b;
  unsigned int c;//holds two bytes
  unsigned char d;//holds one byte

  a=readA();//readA,readB,readC,readD return one unsigned byte;
  b=readB();
  c=readC();
  d=readD();

  data=(signed char) ((0xffffffff-((a<<24)|(b<<16)|(c<<8)|(d)))/1000);

}

场景是收到的整个包包含一个四字节信息,例如值0xAABBCCDD。 readA()将返回包中的最高字节,即0xAA。 readD()将返回0xDD。所以我需要在使用“|”之前移动它们将它们放在一起:(a<<24)|(b<<16)|(c<<8)|(d)

所以我的问题是,对于这句话的右侧

data=(signed char) ((0xffffffff-((a<<24)|(b<<16)|(c<<8)|(d)))/1000);

为什么在将结果转换为带符号的char时它不返回char?当我打印出来时,一切都是错的。但是,如果我将data设置为签名长,那么一切都是正确的。我想这是因为签名的char不足以容纳所有信息并导致溢出。

如果我错了,请纠正我,((a<<24)|(b<<16)|(c<<8)|(d)))中的变量都将被隐式转换为无符号长整数,因此该表达式的结果也是如此。 ((a<<24)|(b<<16)|(c<<8)|(d)))/1000也会返回一个无符号长号。然而,结果将显着缩小,因为它除以1000.

四字节信息的范围从0xFFFF5037到0xFFFFFFFF,这也是此表达式((a<<24)|(b<<16)|(c<<8)|(d)))的结果范围。

因此,在分割完成后,表达式的右侧应该在0到45之间。但是当我打印出结果时,它就会四处跳跃。

3 个答案:

答案 0 :(得分:2)

6.3.1.3有符号和无符号整数

  

1当具有整数类型的值转换为另一个整数类型时   除了_Bool之外,如果值可以用新类型表示,那么   没有改变。 2否则,如果新类型是无符号的,则值为   通过重复加或减一个转换而来   在值之前可以在新类型中表示的最大值   是在新类型的范围内。 3否则,新类型已签名   并且价值无法在其中表示;结果是   实现定义或实现定义的信号被引发。

您给我们的代码可能会以不同的方式运行,具体取决于我们使用的实现所做的选择。看到一个实现提出了一个对应于异常条件的实现定义信号,例如SIGSEGV,这并不是不合理的。如果实现决定默认操作是忽略该信号,那么您将处于该实现的未定义行为领域。

在较长的类型中进行计算,然后通过将模数减小到正确的宽度将较长的值转换为较小的值,并在必要时添加到SCHAR_MIN以生成负值。 (阅读评论

void processData(){
    signed char data;
    unsigned long a; // holds sizeof (unsigned long) bytes*
    unsigned long b;
    unsigned int c;  // holds sizeof (int) bytes*
    unsigned char d; // holds one byte
                     // Footnote: int and unsigned long might have padding bits
                     // --------- Attempting to modify those padding bits can 
                     //           result in undefined behavior...

    a = (readA() << 24) + (readB() << 16) + (readC() << 8) + d;
    a /= 1000;

    a %= UCHAR_MAX;
    if (a <= SCHAR_MAX) {
        // the value will fit safely into data as per point 1 of 6.3.1.3
        data = a;
    }
    else if (a >= (unsigned char) SCHAR_MIN) {
        // Since converting negative values to positive values will result 
        // in values greater than or equal to (unsigned char) SCHAR_MIN, 
        // by reducing a modulo
        // (unsigned char) SCHAR_MIN we work out how many to add to SCHAR_MIN.
        data = SCHAR_MIN + (a % (unsigned char) SCHAR_MIN);
    }
    else {
        // The value is out of range. Perhaps it corresponds to a negative zero?
        data = 0;
    }
}

最后一个条件链可以缩短为:

data = a <= SCHAR_MAX                 ? a
     : a >= (unsigned char) SCHAR_MIN? SCHAR_MIN +(a % (unsigned char) SCHAR_MIN)
     : 0;

答案 1 :(得分:1)

这样的结果将是一个负32位数字,不适合签名的字符。你的代码没有任何意义,因此你得到的输出也没有意义。

在实践中,现实世界中的所有系统都是两个补充系统,其行为如下:

无论整个32位值是否已签名,

(signed char)0xAABBCCDD都会尝试将DD存储到已签名的char变量中。原始符号位丢失。此外,二进制补码0xDD中的MSB将被视为符号位。因此,如果您尝试打印包含0xDD的签名字符,您将得到一个负数。

如果它是(signed char)0xAABBCC0D那么你会得到0D并且没有设置符号位,因为没有设置0D的MSB。

答案 2 :(得分:0)

将数据类型更改为long并使用printf("%lx", data)之类的内容将其打印出来。这会给你一个更好的线索。