我的代码的结构类型定义如下:
typedef struct
{
Structure_2 a[4];
UCHAR b;
UCHAR c;
}Structure_1;
其中Structure_2的定义如下:
typedef struct
{
ULONG x;
USHORT y;
UCHAR z;
}Structure_2;
代码中还有两个功能。第一个(名为setter)声明一个类型为“Structure_1”的结构,并用数据填充它:
void setter (void)
{
Structure_1 data_to_send ;
data_to_send.a[0].x = 0x12345678;
data_to_send.a[0].y = 0x1234;
data_to_send.a[0].z = 0x12;
data_to_send.a[1].x = 0x12345678;
data_to_send.a[1].y = 0x1234;
data_to_send.a[1].z = 0x12;
data_to_send.a[2].x = 0x12345678;
data_to_send.a[2].y = 0x1234;
data_to_send.a[2].z = 0x12;
data_to_send.a[3].x = 0x12345678;
data_to_send.a[3].y = 0xAABB;
data_to_send.a[3].z = 0x12;
data_to_send.b =0;
data_to_send.c = 0;
getter(&data_to_send);
}
编译器将data_to_send保存在内存中:
第二个名为getter:
void getter (Structure_1 * ptr_to_data)
{
UCHAR R_1 = ptr_to_data -> b;
UCHAR R_2 = ptr_to_data -> c;
/* The remaining bytes are received */
}
我希望R_1的值为“00”,R_2的值为“00”。
但是,编译器会发生以下两行的转换:
/* Get the data at the address ptr_to_data -> b,
which equals the start address of structure + 28 which contains the
value “AA”, and hence R_1 will have “AA” */
UCHAR R_1 = ptr_to_data -> b;
/* Get the data at the address ptr_to_data -> c,
which equals the start *address of structure + 29 which contains the
value “BB”, and hence R_2 will *have “BB” */
UCHAR R_2 = ptr_to_data -> c;
编译器在将结构保存到堆栈时添加了填充b / yte但是当它开始读取它时,它会忘记它做了什么(并且在读取时包含了填充字节)。
如何通知编译器在读取结构元素时应跳过填充字节?
我不想解决这个问题,我很想知道编译器为什么会这样?
我的编译器是GreenHills,我的目标是32位
答案 0 :(得分:2)
如何通知编译器在读取结构元素时应跳过填充字节?
简短回答:你不能。
编译器 不会忽略结构中包含的内容 。但是,可以控制它将如何处理结构中的内容。
我很想知道编译器为什么会这样?
简短回答:数据对齐 需要考虑两个问题:数据对齐边界和数据结构填充。你可以控制每个: 数据对齐 是编译器看到它看到的原因。数据对齐意味着将数据放入存储器地址等于字大小的某个倍数(32位环境为4个字节)即使您不使用显式填充,也会存储数据以便观察这些边界,并且大小struct的结尾将指示使用的总字节空间中的填充。
结构填充 - 放置在结构中的无意义字节,以帮助将大小对齐为字大小的倍数。您在示例代码中有这个。
您可以使用 pragma macros 导致编译器以某种方式预处理(在编译之前解析)包装结构:example #pragma pack( n)简单地设置新的对齐方式。或者,#pragma pack()将对齐设置为编译开始时生效的对齐方式。
Example :
#pragma pack(push) /* push current alignment to stack */
#pragma pack(1) /* set alignment to 1 byte boundary */
struct MyPackedData
{
char Data1;
long Data2;
char Data3;
};
#pragma pack(pop) /* restore original alignment from stack */
<强> 注意:的强> pack(n) 的n单位是 byte 。 n的值是编译器特定的,例如对于MSVC,通常是1,2,4,8和16.
问题: 如果您使用的是prama包宏,它们是否在getter()/ setter()函数之间使用一致的包值? (归功于@alain)
但同样,这不会导致编译器忽略结构的内容,只会以不同的方式处理它。
答案 1 :(得分:1)
我对@ryykers的评论的较长版本答案很好:
您在问题中显示的代码完全有效,在with open(csv_file, 'rb') as f:
reader = csv.reader(f)
for row in reader:
# Assuming the columns in the CSV file we want are the first two // Changed
filename = row[0]
filepath = row[1] #Changed
'''Changed: I skipped over this next part, didn't need it, but should be
easy enough to figure out
if filename.startswith(existing_path_prefix):
filename = filename[len(existing_path_prefix):]
'''
new_filename = os.path.join(new_path_prefix, filename)
print ('Copying %s to %s...' % filepath, new_filename) #Changed
shutil.copy(filepath, new_filename) #Changed
print 'done.'
print 'All done!'
中读取结构成员时,绝对没有理由得到错误的值,前提是
否则您使用的编译器将 严重 损坏。
设置打包规则的方式因编译器而异,它们不是标准化的,所以它可能没有命名为getter
。
“正常”,没有理由干扰结构打包,但一个原因是通过网络或文件发送数据。当结构体根本没有填充时,您可以将它们转换为#pragma pack
或void *
并将结构直接传递给“发送”功能,例如:
char *
您问题中的变量名send((void *)&data_to_send, sizeof(data_to_send));
暗示这可能是此代码中发生的情况。我不是说这是一种很好的做法,但它很常见,因为你不必编写序列化代码。