编译器在读取结构时会考虑结构的填充字节

时间:2015-08-05 18:36:01

标签: c structure padding greenhills

我的代码的结构类型定义如下:

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保存在内存中:

enter image description here

第二个名为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位

2 个答案:

答案 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)

但同样,这不会导致编译器忽略结构的内容,只会以不同的方式处理它。

有关根本原因的详细信息,请参阅 here here 信息你的观察结果。

答案 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 packvoid *并将结构直接传递给“发送”功能,例如:

char *

您问题中的变量名send((void *)&data_to_send, sizeof(data_to_send)); 暗示这可能是此代码中发生的情况。我不是说这是一种很好的做法,但它很常见,因为你不必编写序列化代码。

相关问题