提问人:Mika 提问时间:7/20/2023 最后编辑:Mika 更新时间:7/21/2023 访问量:120
C sizeof(some_structure) 返回的值与 python struct.calcsize(struct_string) 不同
C sizeof(some_structure) returns different value in compare with python struct.calcsize(struct_string)
问:
我有一个 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 结构大小后,我发现了一些差异(python 大小为 1114 字节)。这是 python 代码:
struct_string = "<6d4H105d14B4d14B4dL2fB2fL2fB2fL2fB2fL2fB2fL2fB2fL2fB2f"
struct_byte_size = struct.calcsize(struct_string)
print(struct_byte_size)
是什么导致了这种尺寸变化? 我怎样才能从套接字接收数据并避免这种偏移? 我在创建struct_string时可能会出错吗?
UPD:这里是 struct-module 规则表和 calcsize() 描述。
答:
3赞
Mark Tolonen
7/20/2023
#1
这种差异是由于 C 编译器添加了填充字节以进行对齐。如果变量位于变量类型大小的倍数的地址,则代码效率更高。当使用 Python 的模块时,除了 native(@) 对齐(默认值)之外,它不使用填充字节。struct
例如,如果通过以下方式启用所有警告,则 Microsoft 编译器会指示填充: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'
可以使用 禁用此填充。示例如下:#pragma pack
测试.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
评论
0赞
Mika
7/21/2023
谢谢你的回答非常好。我现在正在尝试使用它。但我这里有一些问题。在我的 Ubuntu 23.04 系统中,使用第 11 代英特尔(R)酷睿™ i5-1135G7 2.40GHz,我有以下输出(用于比较 3 个计算大小的变体的代码部分):和 .在谈论 Ctypes 时,所有输出都与您的输出相同。native alignment 1180
no alignment 1114
explicit padding 1136
0赞
Mika
7/21/2023
此外,为了澄清这个话题(对我来说),我们应该在哪里以及为什么要使用填充?我非常感谢我能获得的关于这个主题的每一个值得信赖的信息线索。
0赞
Mark Tolonen
7/21/2023
@Mika 我的第二句话回答了这个问题。“如果变量位于变量类型大小的倍数的地址上,则代码效率更高。”从奇数地址读取 4 字节整数所需的 CPU 周期比可被 4 整除的地址需要更多的 CPU 周期,因此填充用于将变量与适当的地址对齐。
0赞
Mark Tolonen
7/21/2023
@Mika我不知道为什么你会在本机模式下出现如此大的差异。查看编译器是否有可以启用的填充警告,例如 Microsoft。
评论
%lu
%zu
struct.calcsize
sizeof
uint8_t
uint8_t
double an_in_value[4];
double an_out_value[4];
float joint_des_iq;