我正在尝试解释十六进制字节并将它们合并为有用的信息。
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之间。但是当我打印出结果时,它就会四处跳跃。
答案 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)
之类的内容将其打印出来。这会给你一个更好的线索。