4

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

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

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

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

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

因此,除法完成后,表达式的右侧应该在 0 到 45 之间。但是当我打印结果时,一切都在跳跃。

4

3 回答 3

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;
于 2013-04-05T06:23:38.647 回答
1

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

在实践中,现实世界中的所有系统都是二进制补码系统,其行为如下:

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

如果是(signed char)0xAABBCC0D这样,您将获得 0D 并且没有设置符号位,因为未设置 0D 的 MSB。

于 2013-04-05T06:28:00.963 回答
0

Change the type of data to long and print it out with something like printf("%lx", data). That will give you a better clue.

于 2013-04-05T04:00:14.010 回答