如何使用Python'struct'模块获得精确的结构大小

时间:2013-08-29 18:50:30

标签: python c python-2.7 struct

我正在尝试通过TCP从C发送消息(实际上是Obj-C,但是对象方向不在此处)客户端到python服务器。现在我首先发送一个unsigned short消息大小,然后发送消息,这是一个C结构。我想在包的末尾添加一个动态字符串,所以我决定使用struct size将包拆分为两个,但问题就开始了。

问题是,我做错了什么,或者Python的结构库大小+填充计算存在错误。

Python struct似乎正确地解析了填充。例如,对于这个结构:

struct.Struct("H I").size == 8

与此结构的sizeof返回值匹配:

#include <stdio.h>

typedef struct {
        unsigned short a;
        unsigned int b;
} test;

int main() {
        printf("%ld\n", sizeof(test));
        return 0;
}

$ gcc test.c 
$ ./a.out
8

但是对于某些结构定义,我并没有得到相同的结果。例如,在这种情况下:

struct.Struct("H 5s").size == 7

typedef struct {
    unsigned short a;
    char b[5];
} test;
sizeof(test) == 8

我已经读过某个地方,编译器可能会填充一个结构,以确保在数组中使用结构时正确的内存访问。我不确定是否是这种情况(似乎是),但如果是这样,我无法理解为什么这个结构没有填充到8个字节(假设打包4个字节):

struct.Struct("H 4s").size == 6

typedef struct {
    unsigned short a;
    char b[4];
} test;
sizeof(test) == 6

所以,澄清一下,我的问题是如何在Python中获得给定结构的精确大小,因为它没有应用最终的填充。


我尝试了什么:

手动添加最终填充大小:

real_struct_size = self._struct.size + self._struct.size % 4

当然,这不起作用,因为单个成员结构不添加填充,正如您在最后一种情况中所看到的那样,它既不适用于小结构(unsigned short + char [4])。 (也许我在这里过度简化了问题。也许这不是关于小结构,而是与我无法识别的另一个因素有关。)

然后我打开了Python的结构库,看看如何找出预期的参数数量,这样我可以问它是否为1,然后避免最后的填充,但是无法访问s_len属性PyStructObject的参见(参见Python-2.7.5 / Modules / _struct.c:48),这是存储打包参数的数量的地方。

因此,作为一种解决方法,我在数据包的开头放置一个偏移值,以了解额外/动态字符串的开始位置。

但我觉得这里有一个错误(我的或者来自Python的结构库)。无论哪种方式,如果是我,我真的需要知道我做错了什么,或者如果它是Python的库我想报告问题。如果有人能帮助我深究这一点,我将非常感激。

所以,提前谢谢!对不起,很长的帖子:))

2 个答案:

答案 0 :(得分:1)

简短的回答:你做不到。结构模块仅通过重用一些基本类型的符号来与C类型相关,以便程序员方便。所有与填充相关的修补程序都会破坏您将代码移动到另一个平台,由另一个编译器编译的代码或其他任何平台。

获取struct(c-struct)大小的唯一方法是从C引用它并使用编译器编译该代码。您可以使用像

这样的单线程
return PyInt_FromLong(sizeof(mystruct));

长答案:实现一些#includes适当类型的包装代码,将它们写入内存并传递它们(作为不透明对象)。您可以实现bufferview协议,以便将其直接传递给socket.send()

答案 1 :(得分:0)

要将结构的末端与对齐要求对齐,我们只需要找到最大的整数类型即可。像这样:

def c_sizeof(s):
    # Types sorted in size order
    size_map = "cbB?hHiIlLqQfd"
    # Filter out chars in s that not in size_map.
    # The default align char ("c") in case filtered list is empty.
    chars = filter(lambda x: x in size_map, s) + "c"
    # Largest index and its char in size_map gives the align char
    align_char = size_map[max([size_map.index(x) for x in chars])]
    # Using native prefix to calculate alignment between fields
    return struct.calcsize("@{0}0{1}".format(s, align_char))

并运行一些测试

print c_sizeof("cci"), c_sizeof("cic"), c_sizeof("H5s")

产生

8 12 8