如何使用Python';结构';模块

如何使用Python';结构';模块,python,c,python-2.7,struct,Python,C,Python 2.7,Struct,我试图通过TCP将一条消息从C(实际上是Obj-C,但在这里面向对象是不可能的)客户端发送到python服务器。现在我发送一个无符号短消息,首先是消息大小,然后是消息,它是一个C结构。我想在包的末尾附加一个动态字符串,所以我决定使用struct size将包一分为二,但是问题开始了 问题是,要么我做错了什么,要么Python的结构库大小+填充计算有错误 Python结构似乎能够正确地解析填充。例如,对于此结构: struct.Struct("H I").size == 8 #include &

我试图通过TCP将一条消息从C(实际上是Obj-C,但在这里面向对象是不可能的)客户端发送到python服务器。现在我发送一个
无符号短消息
,首先是消息大小,然后是消息,它是一个C结构。我想在包的末尾附加一个动态字符串,所以我决定使用struct size将包一分为二,但是问题开始了

问题是,要么我做错了什么,要么Python的结构库大小+填充计算有错误

Python结构似乎能够正确地解析填充。例如,对于此结构:

struct.Struct("H I").size == 8
#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 I").size == 8
#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
我在某个地方读到,编译器可能会填充结构,以确保在数组中使用结构时正确访问内存。我不确定是否是这样(似乎是这样),但如果是这样,我无法理解为什么这个结构没有填充到8字节(假设4字节的打包):

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


我所尝试的:

手动添加最终填充大小:

real_struct_size = self._struct.size + self._struct.size % 4
当然,这不起作用,因为单成员结构不添加填充,正如您在上一个例子中看到的,它也不适用于小结构(unsignedshort+char[4])。(也许我过于简化了这个问题。也许不是关于小结构,而是与另一个我无法确定的因素有关。)

然后,我打开了Python的结构库,看看如何找出预期的参数数量,这样我就可以询问它是否为1,然后避免最后的填充,但是没有办法访问
PyStructObject
s_len
属性(参见Python-2.7.5/Modules/\u struct.c:48)这是存储打包参数数量的位置

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

但是我认为这里有一个bug(我的或者来自Python的结构库)。不管怎样,如果是我,我真的需要知道我做错了什么,或者如果是Python库,我想报告这个问题。如果有人能帮我弄清真相,我将非常感激


所以,提前谢谢你!抱歉发了这么长的帖子:)

简短回答:你不能。为了方便程序员,struct模块仅通过重用一些基本类型的符号与C类型相关。所有与填充相关的修复都会在您将代码移动到其他平台(由其他编译器编译的代码)时中断

获取结构(c-struct)大小的唯一方法是从c中引用它并使用编译器编译代码。你可以使用一个像这样的内衬

return PyInt_FromLong(sizeof(mystruct));

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

要将结构的结尾与对齐要求对齐,我们只需要找到最大的整数类型。大概是这样的:

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

您是否尝试过使用该函数?@martineau它给出的结果与
Struct.size
(在我的机器上)相同。实际上,
Struct.size
的docstring似乎是
calcsize
的docstring的副本。我不是C语言的专家,但我相信该标准不能保证
struct
s的大小,因此python在任何情况下都无法可靠地计算它。不同的编译器将添加不同的填充(我相信主要取决于架构)。文档还说“要将结构的结尾与特定类型的对齐要求对齐,请使用重复计数为零的该类型的代码来结束格式”,因此您可以这样做。另外,请确保在格式字符串前面加上两种本机类型(
@
=
)中的一种,否则将不会添加任何填充。嗯,这不是自动的,但是一种更好的解决方法,我将尝试一下,谢谢!关于前缀,例如“如果第一个字符不是这些字符中的一个,”@“。我将尝试在这里更新:)@martineau遗憾的是,添加0不会影响大小计算。无论如何谢谢你!不同意。模块本身已经依赖于编译器,编译器根据编译时的配置定义填充行为:--因此,即使使用当前应用的类型填充,您也依赖于编译器配置来打包或解包结构。无论如何,实现bufferview协议听起来是个好主意,我将尝试一下。谢谢