作为程序员,我们是否必须关心结构体指针上下文中的结构体填充?

问题描述 投票:0回答:3

我在 C 中使用指针已经有一段时间了,它们总是按预期工作。然而现在我读到了一本书,它在 C 结构体填充和指针的上下文中断言了这一点:“与这种额外内存分配相关的几个含义:

  • 指针运算必须谨慎使用
  • 结构数组的元素之间可能有额外的内存”

考虑所有以各种方式使用结构(带填充)和指针的代码。它工作得很好:


//struct Test is 8 Bytes in size ( 2 extra Bytes padding ) 

struct Test{

    int a;
    char b;

};

struct Test t1;
t1.a = 20;
t1.b = 'D';

struct Test t2;
t1.a = 40;
t1.b = 'H';

//creating a pointer to a struct Test variable

struct Test *p = &t1;
(*p).a = 50;

//creating an array of struct Test objects

struct Test arr[] = {t1, t2};
p = arr;

//using pointer arithmetic with p :

(*(p+1)).a = 100;

//creating an array of struct Test pointers

struct Test* pt1 = &t1;
struct Test* pt2 = &t2;
struct Test* arr2[] = {pt1, pt2};

(*(*(arr2 + 1))).a = 300;

//creating an array of struct Test pointers pointing to struct Test pointers :

struct Test t3;
t3.a = 56;
t3.b = 'J';
struct Test t4;
t4.a = 221;
t4.b = 'L';

struct Test* pt3 = &t3;
struct Test* pt4 = &t4;
struct Test* arr3[] = {pt3, pt4};
struct Test** arr4[] = {arr2, arr3};

(*(*(*(arr4 + 1)))).a = 400;

即使有填充,一切也能正常工作。我假设作为程序员,我们不必担心结构和填充......所以我的问题是:

  1. 这本书试图警告我们注意什么?

  2. 我们程序员是否必须关心指针上下文中以及其他上下文中的结构和填充?

c pointers struct padding
3个回答
1
投票

然而现在我遇到了一本书……

引用这本书。

…在 C 结构体填充和指针的上下文中断言了这一点:“与这种额外内存分配相关的几个含义:

  • 指针运算必须谨慎使用
  • 结构数组的元素之间可能有额外的内存”

后者在技术上是不正确的。结构的成员之间和末端可能有填充,这被视为结构的一部分。创建数组时,数组元素之间没有额外的填充,只有结构内部的填充,包括其末端的填充。

前者含糊不清且无用。

您显示的示例代码仅使用指向结构的指针。对与填充相关的内存布局的关注适用于访问结构成员和表示结构的字节。由于结构体的成员之间可能有填充,因此您不能假设,如果一个成员距离结构体开头的偏移量 O 且大小为 S,则下一个成员将从偏移量 O+S 处开始。它可能从某个偏移量 O+S+P 开始,其中 P 是 C 实现在它们之间使用的填充字节数。

典型的 C 实现将使用足够的填充来使 O+S+P 成为下一个成员的对齐要求的倍数。例如,如果 O 为 0(对于结构的第一个成员),S 为 1(因为该成员可能是

char
),下一个成员是
int
,且对齐要求为 4 个字节,则 P 将为 3,以便 O+S+P 为 4,即所需对齐的倍数。末尾的填充也将被计算,以使总结构大小是成员最严格对齐的倍数,以便在数组中使用该结构时所有成员保持对齐。

程序员可能对结构中的填充感兴趣的一个问题是结构使用了多少空间。如果成员的布局效率低下,则结构可能会具有比必要的更多的填充,并且如果程序使用该结构的大量实例,则它可能会使用比必要的更多的内存。为了减少内存使用,在结构体的定义中,首先对对齐要求最严格的成员进行排序。

当以其他方式访问表示结构的内存时,例如在联合中使用多个不同的结构或传输或存储字节以供其他软件重新解释时,也可能会出现有关填充布局的问题。

前者使用相同的内存作为不同类型的结构(或通常不同的类型),应该避免,并且当不可避免时,必须明智地使用,并了解 C 的规则和所使用的 C 实现。

后者也需要特殊的知识和关怀。在不同软件(包括为不同目标平台编译的相同源代码)之间交换数据的一种方法是定义一种特定的格式来表示数据,并将数据从 C 类型转换为写入时的格式,并在编写时反向转换。它已被读取。这可以通过与 C 中的类型表示无关的方式来完成,但需要足够的谨慎和知识,但这可能很麻烦。程序员可能会尝试采取捷径并更直接地使用结构的内存内容。在这些情况下,您需要关心填充以及如何表示数据的问题,例如字节顺序。


0
投票

简短的回答:不,您不需要了解结构填充。 C 添加一些填充的原因是出于性能原因,尽管如果您的内存严格,您可以通过在文件顶部添加

#pragma pack(1)
来设置一个字节边界来禁用它(不推荐)。


-1
投票

考虑一下:
从网络的一侧,有一个在 raspberrypi 32 位上的发送者,编译了

struct data { char c; long num;};
(sizeof(struct data)是 8 字节)并发送
{ .c = 'a', .num = 1 }
, 另一侧 64 位 PC 上的接收器具有相同的解压结构(即 sizeof(struct data) 为 16 字节)。 接收者在 .num 中会有什么?

© www.soinside.com 2019 - 2024. All rights reserved.