我理解这段代码有一个问题。我所知道的是,我们已经把一段代码传递给一个汇编程序,它把代码转换成“字节码”。现在我有一个虚拟机,它应该读这段代码。这个函数应该读第一个字节码指令。我不明白这段代码中发生了什么。我猜我们正在试图读这段字节码,但不明白它是如何完成的。
static int32_t bytecode_to_int32(const uint8_t *bytecode, size_t size)
{
int32_t result;
t_bool sign;
int i;
result = 0;
sign = (t_bool)(bytecode[0] & 0x80);
i = 0;
while (size)
{
if (sign)
result += ((bytecode[size - 1] ^ 0xFF) << (i++ * 8));
else
result += bytecode[size - 1] << (i++ * 8);
size--;
}
if (sign)
result = ~(result);
return (result);
}
4条答案
按热度按时间ws51t4hk1#
这段代码写得有点糟糕,一行代码中包含了很多操作,因此包含了各种潜在的bug,看起来很脆弱。
bytecode[0] & 0x80
简单地读取MSB符号位,假设它是2的补码或类似的,然后将其转换为布尔值。int
。i * 8
位向左移位。数据总是隐式地提升到int
,因此如果i * 8
恰好给予大于INT_MAX
的结果,则这里存在一个未定义的行为错误。在移位之前将uint32_t
转换为uint32_t
,执行移位,然后转换为有符号类型。int
被转换为int32_t
--根据系统的不同,它们可以是相同的类型或不同的类型。int32_t
被反转为符号扩展的某个2的补码负数,并且所有数据位再次被反转。除了通过左移移入的所有零也被替换为1。无论这是有意还是无意,我不知道。例如,如果你开始使用像0x0081
这样的格式,你现在就有了像0xFFFF01FF
这样的格式。我不知道这种格式有什么意义。我认为
bytecode[size - 1] ^ 0xFF
(相当于~
)是用来切换数据位的,这样当以后调用~
时,它们就会切换回原来的值。如果程序员有能力的话,他们必须用注解来记录这些技巧。无论如何,不要使用这段代码。如果目的仅仅是交换一个4字节整数的字节顺序(endianess),那么这段代码必须从头开始重写。
正确的做法是:
任何比上面更复杂的代码都是非常有问题的。我们不必担心符号是一种特殊情况,上面的代码保留了原始的签名格式。
x6h2sr282#
所以
A^0xFF
切换A中设置的位,所以如果你有10101100与11111111异或..它将变成01010011。我不知道为什么他们没有在这里使用~。^是一个异或运算符,所以你是与0xFF异或。<<
是一个向上或向左的位移。换句话说,A〈〈1相当于A乘以2。>>
向下移动,因此等效于右移位或除以2。~
反转字节中的位。注意,最好在声明时初始化变量,这样做不需要额外的处理。
sign =(t_bool)(字节码[0] & 0x 80);数字中的符号存储在第8位(或从0开始计数的位置7),这是0x 80的来源。因此,它实际上检查是否在字节码的第一个字节中设置了有符号位,如果是,则将其存储在sign变量中。
本质上,如果它是无符号的,那么它是从字节码中一次一个字节地复制字节到结果中。
如果数据是有符号的,那么它会翻转位,然后复制字节,然后当它完成复制时,它会将位翻转回来。
就我个人而言,这种事情我更喜欢得到的数据,坚持在htons()格式(网络字节顺序),然后将其存储到已分配的数组中,以端序不可知的方式存储它,然后当我检索数据时我使用ntohs()将其转换回计算机使用的格式。htons()和ntohs()是标准的C函数,一直用于网络和平台无关的数据格式化/存储/通信。
u7up0aaq3#
这个函数是一个非常简单的版本的函数,它将大端字节序转换为小端字节序。
不需要参数大小,因为它仅适用于4字节数据。
它可以更容易地通过union punning归档(并且它允许编译器优化它-在这种情况下,简单的指令):
https://godbolt.org/z/rb6Na5
k2arahey4#
如果代码的目的是将网络/big-endian字节顺序中的1、2、3或4字节序列符号扩展为有符号的32位
int
值,则它将以困难的方式执行任务,并在此过程沿着执行reimplementing the whee l。这可以分为三个步骤:将适当的字节数转换为32位整数值,将字节进行符号扩展为32位,然后将该32位值从big-endian转换为主机的字节顺序。
本例中重新实现的“wheel”是POSIX标准的
ntohl()
函数,它将big-endian/network字节顺序的32位无符号整数值转换为本地主机的本机字节顺序。第一步是将1、2、3或4个字节转换为
uint32_t
:然后,对它进行符号扩展(borrowing from this answer):
把它们放在一起:
请注意,上述代码中的
return
语句隐式执行的从uint32_t
到int32_t
的转换可能会导致实现定义的行为,因为可能存在无法Map到int32_t
值的uint32_t
值。请参见this answer。任何像样的编译器都应该将其优化为内联函数。
我个人认为这还需要更好的错误处理/输入验证。