我在阅读一些代码时遇到了一些理解Perl的unpack函数的困难,特别是在使用S*
模板时。
$data = "FF";
print "$data - ", unpack("S*", $data), "\n";
# > FF - 17990
- 在C语言中,这个与之等价的是什么?
- 为什么?
非常感谢您的帮助。
unpack 'S'
表示将两个字节转化为一个 uint16_t
。
#include <stdint.h>
const char *data = "\x46\x41";
uint16_t n;
memcpy(&n, data, sizeof(n)); // n = 0x4146 or 0x4641
在执行此操作之前,请不要忘记检查 data
中的字节数!
请注意,它可以根据系统给出两个不同的结果。
在小端系统(例如 x86、x64)上,unpack 'S'
也等同于
uint16_t n = (data[1] << 8) | data[0]; // 0x4146
unpack 'S'
也等同于uint16_t n = (data[0] << 8) | data[1]; // 0x4641
uint16_t n = *((const uint16_t *)data);
memcpy
本身没问题,但现在内存中有一些随机的字节位模式,而编译器却期望一个有效的uint16_t
。你怎么知道这个位模式作为该类型时是有效的呢?
当你让编译器通过数值位移操作构造uint16_t
时,就不会有这种危险,因为它知道那时它将是有效的。 - LeoNerduint16_t
的本机表示。因此,您实际上是在争论memcpy
是唯一正确的解决方案。 - ikegami您在C中的代码(大致)如下:
const char *data = "FA";
unsigned short s;
memcpy( &s, data, strlen(data) );
printf("%s = %d\n", data, s);
这仅处理了两个字符的情况,而unpack('S*',...)将返回与其输入对应的短整数列表。
为什么?pack和unpack的主要动机之一是使二进制数据与C结构更轻松地进行交换。
perlpacktut 是一个很好的起点。
我在回答自己的问题,所以可能有些地方不正确,但我会把这个留在这里供将来需要的人参考。
首先,让我们将我的例子改为
$data = "FA";
print "$data - ", unpack("S*", $data), "\n";
# > FA - 16710
由于拥有“FF”并没有什么特别的作用。
问题是:我们是如何从“FA”到16710的?
首先,字符“F”被转换为其ASCII值-70。以二进制表示为0100 0110
(请注意,我填充了一个前导零,以便清楚地表示它是一个完整的字节)。
然后,我们需要“A”的ASCII值-65。以二进制表示为0100 0001
。
因此,我们有F
对应于0100 0110
和A
对应于0100 0001
。
然后我们将这两个二进制值粘在一起,除了我们将A
放在第一位:
0100 0001 0100 0110
将0100 0001 0100 0110
转换为十进制得到16,710
。
perldoc -f unpack
可以获取有关unpack函数的信息,并指向perldoc -f pack
以了解不同格式的解释。另外,需要注意的是,如果你期望的是十六进制值0xFF,那么"FF"并不是它的表示方式。 - Andy Lester