C sizeof(some_structure)返回与Python struct不同的值,calcsize(struct_string)

nukf8bse  于 2023-08-03  发布在  Python
关注(0)|答案(1)|浏览(107)

我有一个C结构。

typedef struct
{
    double cycle_time; 
    double cycle_duty; 
    double state; 
    double servo_mode; 
    double motion_mode; 
    double jcond; 
    
    struct
    {
        uint16_t buff_sz; 
        uint16_t buff_fill; 
        uint16_t cmd_cntr; 
        uint16_t res; 
    } wpi;
    double move_des_q[6]; 
    double move_des_qd[6]; 
    double move_des_x[6]; 
    double move_des_xd[6]; 
    double act_q[6]; 
    double act_qd[6]; 
    double act_x[6]; 
    double act_xd[6]; 
    double act_tq[6]; 
    double frict_tq[6]; 
    double ne_tq[6];
    double act_force_e[6];
    double act_force_0[6]; 
    double des_trq[6]; 
    double des_qd[6]; 
    double temp_m[6]; 
    double temp_e[6]; 
    double arm_current;
    double arm_voltage;
    double psu_voltage;
    struct
    {
        uint8_t dig_in_count; 
        uint8_t an_in_count;
        uint8_t dig_in[8];
        uint8_t an_in_curr_mode[4];
        double an_in_value[4];

        uint8_t dig_out_count; //number of bits
        uint8_t an_out_count;
        uint8_t dig_out[8];
        uint8_t an_out_curr_mode[4];
        double an_out_value[4];
    } io;

    struct
    {
        uint32_t jointState;
        float joint_volt;       
        float joint_amp;        
        uint8_t joint_window;
        float joint_des_iq;
                                 
        float joint_des_vel;
    } jointInfo[6];
} some_structure;

字符串
在运行计算代码后,我发现了C结构的大小(1136字节)(至少我希望它是正确的)。代码如下:

int main()
{
    printf("Size of struct ABC: %lu\n", sizeof(some_structure));
}


但是,在检查python结构大小后,我发现了一些差异(pythonsize is1114 bytes)。下面是Python代码:

struct_string = "<6d4H105d14B4d14B4dL2fB2fL2fB2fL2fB2fL2fB2fL2fB2fL2fB2f"
    struct_byte_size = struct.calcsize(struct_string)
    print(struct_byte_size)


是什么导致了这种规模的变化?如何从socket接收数据并避免这种转换?在创建struct_string的时候可能出错了吗?

UPDHere是struct-module rule-table和calcsize()的描述。

7tofc5zh

7tofc5zh1#

差异是由于C编译器添加了用于对齐的填充字节。如果变量的地址是变量类型大小的倍数,则代码的效率会更高。当使用Python的struct模块时,除了原生(@)对齐(默认)之外,它不使用填充字节。
例如,如果通过cl /Wall test.c启用了所有警告,则Microsoft编译器会指示填充:

test.c(43): warning C4820: '<unnamed-tag>': '2' bytes padding added after data member 'an_in_curr_mode'
test.c(49): warning C4820: '<unnamed-tag>': '2' bytes padding added after data member 'an_out_curr_mode'
test.c(56): warning C4820: '<unnamed-tag>': '3' bytes padding added after data member 'joint_window'

字符串
可以使用#pragma pack禁用此填充。示例如下:

test.c

#include <stdio.h>
#include <stdint.h>

//#pragma pack(push, 1)
typedef struct {
    double cycle_time;
    double cycle_duty;
    double state;
    double servo_mode;
    double motion_mode;
    double jcond;
    struct {
        uint16_t buff_sz;
        uint16_t buff_fill;
        uint16_t cmd_cntr;
        uint16_t res;
    } wpi;
    double move_des_q[6];
    double move_des_qd[6];
    double move_des_x[6];
    double move_des_xd[6];
    double act_q[6];
    double act_qd[6];
    double act_x[6];
    double act_xd[6];
    double act_tq[6];
    double frict_tq[6];
    double ne_tq[6];
    double act_force_e[6];
    double act_force_0[6];
    double des_trq[6];
    double des_qd[6];
    double temp_m[6];
    double temp_e[6];
    double arm_current;
    double arm_voltage;
    double psu_voltage;
    struct {
        uint8_t dig_in_count;
        uint8_t an_in_count;
        uint8_t dig_in[8];
        uint8_t an_in_curr_mode[4];  // 2 bytes padding after
        double an_in_value[4];

        uint8_t dig_out_count; //number of bits
        uint8_t an_out_count;
        uint8_t dig_out[8];
        uint8_t an_out_curr_mode[4];  // 2 bytes padding after
        double an_out_value[4];
    } io;
    struct {
        uint32_t jointState;
        float joint_volt;
        float joint_amp;
        uint8_t joint_window;  // 3 bytes padding after
        float joint_des_iq;

        float joint_des_vel;
    } jointInfo[6];  // 3 * 6 = 18 bytes total padding
} some_structure;  // 18 + 2 + 2 = 22 extra bytes due to padding
//#pragma pack(pop)

int main(void) {
    printf("Size of struct ABC: %zu\n", sizeof(some_structure));
}


使用本机填充的输出:

1136


#pragma pack语句未注解的输出:

1114


从Python代码中删除<以使用本机填充,或者考虑填充:

import struct

struct_string = '6d4H105d14B4d14B4dL2fB2fL2fB2fL2fB2fL2fB2fL2fB2fL2fB2f'
print(struct.calcsize(struct_string)) # native alignment
print(struct.calcsize('<' + struct_string)) # no alignment

struct_string = '<6d4H105d14B2x4d14B2x4dL2fB3x2fL2fB3x2fL2fB3x2fL2fB3x2fL2fB3x2fL2fB3x2f'
print(struct.calcsize(struct_string)) # explicit padding


输出量:

1136
1114
1136


解压缩结构返回一个187元组。很难计算出特定值的正确偏移。
考虑使用ctypes模块,在该模块中可以指定命名字段和嵌套结构,以便更自然地查找特定值。示例如下:

import ctypes as ct

PACK = 1  # use 8 for native or remove the _pack_ lines

class wpi(ct.Structure):
    _pack_ = PACK
    _fields_ = (('buff_sz', ct.c_uint16),
                ('buff_fill', ct.c_uint16),
                ('cmd_cntr', ct.c_uint16),
                ('res', ct.c_uint16))

class io(ct.Structure):
    _pack_ = PACK
    _fields_ = (('dig_in_count', ct.c_uint8),
                ('an_in_count', ct.c_uint8),
                ('dig_in', ct.c_uint8 * 8),
                ('an_in_curr_mode', ct.c_uint8 * 4),
                ('an_in_value', ct.c_double * 4),
                ('dig_out_count', ct.c_uint8),
                ('an_out_count', ct.c_uint8),
                ('dig_out', ct.c_uint8 * 8),
                ('an_out_curr_mode', ct.c_uint8 * 4),
                ('an_out_value', ct.c_double * 4))

class jointInfo(ct.Structure):
    _pack_ = PACK
    _fields_ = (('jointState', ct.c_uint32),
                ('joint_volt', ct.c_float),
                ('joint_amp', ct.c_float),
                ('joint_window', ct.c_uint8),
                ('joint_des_iq', ct.c_float),
                ('joint_des_vel', ct.c_float))

class some_structure(ct.Structure):
    _pack_ = PACK
    _fields_ = (('cycle_time', ct.c_double),
                ('cycle_duty', ct.c_double),
                ('state', ct.c_double),
                ('servo_mode', ct.c_double),
                ('motion_mode', ct.c_double),
                ('jcond', ct.c_double),
                ('wpi', wpi),
                ('move_des_q', ct.c_double * 6),
                ('move_des_qd', ct.c_double * 6),
                ('move_des_x', ct.c_double * 6),
                ('move_des_xd', ct.c_double * 6),
                ('act_q', ct.c_double * 6),
                ('act_qd', ct.c_double * 6),
                ('act_x', ct.c_double * 6),
                ('act_xd', ct.c_double * 6),
                ('act_tq', ct.c_double * 6),
                ('frict_tq', ct.c_double * 6),
                ('ne_tq', ct.c_double * 6),
                ('act_force_e', ct.c_double * 6),
                ('act_force_0', ct.c_double * 6),
                ('des_trq', ct.c_double * 6),
                ('des_qd', ct.c_double * 6),
                ('temp_m', ct.c_double * 6),
                ('temp_e', ct.c_double * 6),
                ('arm_current', ct.c_double),
                ('arm_voltage', ct.c_double),
                ('psu_voltage', ct.c_double),
                ('io', io),
                ('jointInfo', jointInfo * 6))

print(ct.sizeof(some_structure))
# Construct the structure from some byte data
s = some_structure.from_buffer_copy(b'\x01' * ct.sizeof(some_structure))
print(s.jointInfo[2].joint_window)  # example to view a value


输出(PACK=1):

1114
1


输出(PACK=8):

1136
1

相关问题