C sizeof(some_structure)返回的值与python struct.calcsize(struct_string)的值不同。

huangapple go评论79阅读模式
英文:

C sizeof(some_structure) returns different value in compare with python struct.calcsize(struct_string)

问题

I have a C structure.

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;

After running calculating code, I've found out the size (1136 bytes) of the C structure (at least I hope it's correct). Here is the code:

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

But, after checking the Python structure size, I've found some difference (Python size is 1114 bytes). Here is the Python code:

import struct

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

What causes this size shift? How can I receive the data from the socket and avoid this shift? May I have made a mistake while I was creating the struct_string?

UPD: Here is the struct-module rule-table and calcsize() description.

英文:

I have a C structure.

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;

After running calculating code, i've found out the size (1136 bytes) of the C structure (at least i hope it's correct). Here is the code:

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

But, after checkout the python structure size i've found some difference (python size is 1114 bytes). Here is python code:

    struct_string = &quot;&lt;6d4H105d14B4d14B4dL2fB2fL2fB2fL2fB2fL2fB2fL2fB2fL2fB2f&quot;
    struct_byte_size = struct.calcsize(struct_string)
    print(struct_byte_size)

What cause this size shift?
How can i receive the data from socket and avoid this shift ?
May i've a mistake while i was creating the struct_string?

UPD: Here is the struct-module rule-table and calcsize() description.

答案1

得分: 3

The difference is due to the C compiler adding padding bytes for alignment. Code is more efficient if variables are at addresses that are multiples of the size of the variable's type. When using Python's struct module with anything but native(@) alignment (the default), it doesn't use padding bytes.

As an example the Microsoft compiler indicates padding if all warnings are enabled via cl /Wall test.c:

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'

This padding can be disabled with #pragma pack. Example below:

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));
}

Output with native padding:

1136

Output with the #pragma pack statements uncommented:

1114

Remove the < from the Python code to use native padding, or account for the padding:

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

Output:

1136
1114
1136

Unpacking your structure returns a 187-tuple. It can be hard to figure out the correct offset for a particular value.

Consider using the ctypes module where you can specify named fields and nested structures to more naturally look up a particular value. Example:

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),
                ('

<details>
<summary>英文:</summary>

The difference is due to the C compiler adding padding bytes for alignment.  Code is more efficient if variables are at addresses that are multiples of the size of the variable&#39;s type.  When using Python&#39;s `struct` module with anything but native(@) alignment (the default), it doesn&#39;t use padding bytes.

As an example the Microsoft compiler indicates padding if all warnings are enabled via `cl /Wall test.c`:
```none
test.c(43): warning C4820: &#39;&lt;unnamed-tag&gt;&#39;: &#39;2&#39; bytes padding added after data member &#39;an_in_curr_mode&#39;
test.c(49): warning C4820: &#39;&lt;unnamed-tag&gt;&#39;: &#39;2&#39; bytes padding added after data member &#39;an_out_curr_mode&#39;
test.c(56): warning C4820: &#39;&lt;unnamed-tag&gt;&#39;: &#39;3&#39; bytes padding added after data member &#39;joint_window&#39;

This padding can be disabled with #pragma pack. Example below:

test.c

#include &lt;stdio.h&gt;
#include &lt;stdint.h&gt;

//#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(&quot;Size of struct ABC: %zu\n&quot;, sizeof(some_structure));
}

Output with native padding:

1136

Output with the #pragma pack statements uncommented:

1114

Remove the &lt; from the Python code to use native padding, or account for the padding:

import struct

struct_string = &#39;6d4H105d14B4d14B4dL2fB2fL2fB2fL2fB2fL2fB2fL2fB2fL2fB2f&#39;
print(struct.calcsize(struct_string)) # native alignment
print(struct.calcsize(&#39;&lt;&#39; + struct_string)) # no alignment

struct_string = &#39;&lt;6d4H105d14B2x4d14B2x4dL2fB3x2fL2fB3x2fL2fB3x2fL2fB3x2fL2fB3x2fL2fB3x2f&#39;
print(struct.calcsize(struct_string)) # explicit padding

Output:

1136
1114
1136

Unpacking your structure returns a 187-tuple. It can be hard to figure out the correct offset for a particular value.

Consider using the ctypes module where you can specify named fields and nested structures to more naturally look up a particular value. Example:

import ctypes as ct

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

class wpi(ct.Structure):
    _pack_ = PACK
    _fields_ = ((&#39;buff_sz&#39;, ct.c_uint16),
                (&#39;buff_fill&#39;, ct.c_uint16),
                (&#39;cmd_cntr&#39;, ct.c_uint16),
                (&#39;res&#39;, ct.c_uint16))

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

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

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

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

Output (PACK=1):

1114
1

Output (PACK=8):

1136
1

huangapple
  • 本文由 发表于 2023年7月20日 22:09:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/76730751.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定