英文:
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("Size of struct ABC: %lu\n", 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 = "<6d4H105d14B4d14B4dL2fB2fL2fB2fL2fB2fL2fB2fL2fB2fL2fB2f"
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'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`:
```none
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),
('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
Output (PACK=1):
1114
1
Output (PACK=8):
1136
1
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论