在 C 中禁用结构填充而不使用 pragma

发布于 2024-10-05 02:46:41 字数 37 浏览 2 评论 0原文

如何在不使用 pragma 的情况下禁用 C 中的结构填充?

How can I disable structure padding in C without using pragma?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

二货你真萌 2024-10-12 02:46:41

没有标准方法可以做到这一点。该标准规定,可以根据实现自行决定进行填充。来自 C99 6.7.2.1 结构和联合说明符,第 12 段:

结构或联合对象的每个非位域成员都以适合其类型的实现定义方式对齐。

话虽如此,您可以尝试以下几件事。


第一个您已经打折了,使用 #pragma 尝试说服编译器不要打包。无论如何,这都是不可移植的。也不是任何其他特定于实现的方法,但您应该检查它们,因为如果您确实需要此功能,可能有必要这样做。


第二种是以从大到小的顺序对字段进行排序,例如所有 long long 类型,后跟 long 类型,然后是所有 intshort 和最后的 char 类型。这通常会起作用,因为通常较大的类型具有更严格的对齐要求。再说一次,不便携。


第三,您可以将类型定义为 char 数组并转换地址以确保没有填充。但请记住,如果变量未正确对齐,某些架构会减慢速度,而另一些架构则会严重失败(例如引发总线错误并终止进程)。

最后一个需要进一步的解释。假设您有一个结构,其字段按以下顺序排列:

char C; // one byte
int  I; // two bytes
long L; // four bytes

使用填充,您可能最终得到以下字节:

CxxxIIxxLLLL

其中 x 是填充。

但是,如果您将结构定义为:

typedef struct { char c[7]; } myType;
myType n;

您会得到:

CCCCCCC

然后您可以执行类似的操作:

int *pInt = &(n.c[1]);
int *pLng = &(n.c[3]);
int myInt = *pInt;
int myLong = *pLng;

给您:

CIILLLL

再次不幸的是,不可移植。


所有这些“解决方案”都依赖于您对编译器和底层数据类型的深入了解。

There is no standard way of doing this. The standard states that padding may be done at the discretion of the implementation. From C99 6.7.2.1 Structure and union specifiers, paragraph 12:

Each non-bit-field member of a structure or union object is aligned in an implementation-defined manner appropriate to its type.

Having said that, there's a couple of things you can try.


The first you've already discounted, using #pragma to try and convince the compiler not to pack. In any case, this is not portable. Nor are any other implementation-specific ways but you should check into them as it may be necessary to do it if you really need this capability.


The second is to order your fields in largest to smallest order such as all the long long types followed by the long ones, then all the int, short and finally char types. This will usually work since it's most often the larger types that have the more stringent alignment requirements. Again, not portable.


Thirdly, you can define your types as char arrays and cast the addresses to ensure there's no padding. But keep in mind that some architectures will slow down if the variables aren't aligned properly and still others will fail miserably (such as raising a BUS error and terminating your process, for example).

That last one bears some further explanation. Say you have a structure with the fields in the following order:

char C; // one byte
int  I; // two bytes
long L; // four bytes

With padding, you may end up with the following bytes:

CxxxIIxxLLLL

where x is the padding.

However, if you define your structure as:

typedef struct { char c[7]; } myType;
myType n;

you get:

CCCCCCC

You can then do something like:

int *pInt = &(n.c[1]);
int *pLng = &(n.c[3]);
int myInt = *pInt;
int myLong = *pLng;

to give you:

CIILLLL

Again, unfortunately, not portable.


All these "solutions" rely on you having intimate knowledge of your compiler and the underlying data types.

述情 2024-10-12 02:46:41

除了 pragma pack 之类的编译器选项之外,您不能这样做,填充是在 C 标准中的。

您始终可以尝试通过在结构的最后声明最小类型来减少填充,如下所示:

struct _foo {
     int a;  /* No padding between a & b */
     short b;
} foo;

struct _bar {
     short b; /* 2 bytes of padding between a & b */
     int a;
} bar;

注意 对于具有 4 字节边界的实现

Other than compiler options like pragma pack, you cannot, padding is in the C Standard.

You can always attempt to reduce padding by declaring the smallest types last in the structure as in:

struct _foo {
     int a;  /* No padding between a & b */
     short b;
} foo;

struct _bar {
     short b; /* 2 bytes of padding between a & b */
     int a;
} bar;

Note for implementations which have 4 byte boundaries

勿忘心安 2024-10-12 02:46:41

在某些架构上,如果要求处理未对齐的数据,CPU 本身会反对。为了解决这个问题,编译器可以生成多个对齐的读或写指令,移位并拆分或合并各个位。您可以合理地预期它比对齐数据处理慢 5 到 10 倍。但是,该标准并不要求编译器做好这样做的准备……考虑到性能成本,它的需求还不够。支持显式控制填充的编译器提供了自己的编译指示,因为编译指示是为非标准功能保留的。

如果必须使用未填充的数据,请考虑编写自己的访问例程。您可能想要尝试需要较少对齐的类型(例如使用 char/int8_t),但仍然有可能例如结构的大小将四舍五入为 4 的倍数,这会紧密地挫败包装结构,在这种情况下您'您需要对整个内存区域实现您自己的访问。

On some architectures, the CPU itself will object if asked to work on misaligned data. To work around this, the compiler could generate multiple aligned read or write instructions, shift and split or merge the various bits. You could reasonably expect it to be 5 or 10 times slower than aligned data handling. But, the Standard doesn't require compilers to be prepared to do that... given the performance cost, it's just not in enough demand. The compilers that support explicit control over padding provide their own pragmas precisely because pragmas are reserved for non-Standard functionality.

If you must work with unpadded data, consider writing your own access routines. You might want to experimenting with types that require less alignment (e.g. use char/int8_t), but it's still possible that e.g. the size of structs will be rounded up to multiples of 4, which would frustrate packing structures tightly, in which case you'll need to implement your own access for the entire memory region.

寂寞清仓 2024-10-12 02:46:41

要么让编译器进行填充,要么告诉它不要使用 #pragma 进行填充,要么只使用一些字节(例如 char 数组),然后自己构建所有数据(移动和添加字节)。这确实效率低下,但您可以精确控制字节的布局。有时我会手动准备网络数据包,但在大多数情况下这是一个坏主意,即使它是标准的。

Either you let compiler do padding, or tell it not to do using #pragma, either you just use some bunch of bytes like a char array, and you build all your data by yourself (shifting and adding bytes). This is really inefficient but you'll exactly control the layout of the bytes. I did that sometimes preparing network packets by hand, but in most case it's a bad idea, even if it's standard.

清晨说晚安 2024-10-12 02:46:41

如果您确实想要没有填充的结构:使用仅由 8 位字节组成的结构或类定义短、整型、长整型等的替换数据类型。然后使用替换数据类型组成更高级别的结构。

C++ 的运算符重载非常方便,但在 C 中使用结构而不是类也可以达到相同的效果。下面的转换和赋值实现假设 CPU 可以处理未对齐的 32 位整数,但其他实现可以适应更严格的 CPU。

这是示例代码:

#include <stdint.h>
#include <stdio.h>

class packable_int { public:

  int8_t b[4];

  operator int32_t () const       { return *(int32_t*) b; }
  void operator =  ( int32_t n )  { *(int32_t*) b = n; }

};

struct SA {
  int8_t   c;
  int32_t  n;
} sa;

struct SB {
  int8_t        c;
  packable_int  n;
} sb;

int main () {
  printf ( "sizeof sa  %d\n", sizeof sa );    // sizeof sa  8               
  printf ( "sizeof sb  %d\n", sizeof sb );    // sizeof sb  5               
  return 0;
}

If you really want structs without padding: Define replacement datatypes for short, int, long, etc., using structs or classes that are composed only of 8 bit bytes. Then compose your higher level structs using the replacement datatypes.

C++'s operator overloading is very convenient, but you could achieve the same effect in C using structs instead of classes. The below cast and assignment implementations assume the CPU can handle misaligned 32bit integers, but other implementations could accommodate stricter CPUs.

Here is sample code:

#include <stdint.h>
#include <stdio.h>

class packable_int { public:

  int8_t b[4];

  operator int32_t () const       { return *(int32_t*) b; }
  void operator =  ( int32_t n )  { *(int32_t*) b = n; }

};

struct SA {
  int8_t   c;
  int32_t  n;
} sa;

struct SB {
  int8_t        c;
  packable_int  n;
} sb;

int main () {
  printf ( "sizeof sa  %d\n", sizeof sa );    // sizeof sa  8               
  printf ( "sizeof sb  %d\n", sizeof sb );    // sizeof sb  5               
  return 0;
}
魂ガ小子 2024-10-12 02:46:41

我们可以使用以下任一方法在 C 程序中禁用结构填充。

->在结构定义后面使用 __attribute__((packed)) 。例如。

struct node {
    char x;
    short y;
    int z;
} __attribute__((packed));

->编译 C 代码时使用 -fpack-struct 标志。例如。

$ gcc -fpack-struct -o tmp tmp.c

希望这有帮助。
谢谢。

We can disable structure padding in c program using any one of the following methods.

-> use __attribute__((packed)) behind definition of structure. for eg.

struct node {
    char x;
    short y;
    int z;
} __attribute__((packed));

-> use -fpack-struct flag while compiling c code. for eg.

$ gcc -fpack-struct -o tmp tmp.c

Hope this helps.
Thanks.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文