在不使用pragma的情况下禁用C中的结构填充
如何在不使用pragma的情况下禁用C中的结构填充?除了编译器选项(如pragma pack)之外,您不能,填充是C标准的 始终可以通过声明结构中最后一个最小的类型来减少填充,如中所示:在不使用pragma的情况下禁用C中的结构填充,c,pragma,C,Pragma,如何在不使用pragma的情况下禁用C中的结构填充?除了编译器选项(如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 */
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字节边界的实现没有标准的方法。该标准规定,填充可由实施方自行决定。C99
6.7.2.1结构和联合规范第12段:
结构或联合对象的每个非位字段成员以适合其类型的实现定义的方式对齐
话虽如此,有几件事你可以试试
第一个已经打折了,使用#pragma
尝试说服编译器不要打包。无论如何,这是不可移植的。也没有任何其他特定于实现的方法,但您应该检查它们,因为如果您确实需要此功能,可能需要这样做
第二种方法是按从大到小的顺序排列字段,例如所有long
类型,然后是long
类型,然后是所有int
、short
类型,最后是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
同样不幸的是,它不是便携式的
所有这些“解决方案”都依赖于您对编译器和底层数据类型的深入了解。在某些体系结构上,如果要求CPU处理未对齐的数据,CPU本身会反对。为了解决这个问题,编译器可以生成多个对齐的读或写指令,移位、拆分或合并不同的位。您可以合理地预期它比对齐数据处理慢5到10倍。但是,该标准并不要求编译器准备这样做。。。考虑到性能成本,它的需求量还不够。支持对填充进行显式控制的编译器提供了自己的杂注,这正是因为杂注是为非标准功能保留的
如果您必须使用未填充的数据,请考虑编写自己的访问例程。您可能希望尝试使用需要更少对齐的类型(例如,使用char/int8_t),但也有可能结构的大小将向上舍入到4的倍数,这将阻碍紧密打包结构,在这种情况下,您需要实现自己对整个内存区域的访问。
要么让编译器进行填充,要么告诉它不要使用#pragma,要么只使用一些字节,比如字符数组,然后自己构建所有数据(移动和添加字节)。这是非常低效的,但是您可以精确地控制字节的布局。有时我会手工准备网络数据包,但在大多数情况下,这是一个坏主意,即使它是标准的。如果您真的想要不带填充的结构:使用仅由8位字节组成的结构或类,为short、int、long等定义替换数据类型。然后使用替换数据类型组合更高级别的结构
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;
}
#包括
#包括
类可打包_int{public:
int8_t b[4];
运算符int32_t()常量{return*(int32_t*)b;}
void运算符=(int32_t n){*(int32_t*)b=n;}
};
结构SA{
国际贸易委员会;
int32_t n;
}sa;
构造某人{
国际贸易委员会;
可包装的;
}某人;
int main(){
printf(“sizeof sa%d\n”,sizeof sa);//sizeof sa 8
printf(“sizeof sb%d\n”,sizeof sb);//sizeof sb 5
返回0;
}
我们可以使用以下任何一种方法禁用c程序中的结构填充
->在结构定义后面使用\uuuuuu属性((打包))
。例如
struct node {
char x;
short y;
int z;
} __attribute__((packed));
->编译c代码时使用-fpack struct
标志。例如
struct node {
char x;
short y;
int z;
} __attribute__((packed));
$gcc-fpack结构-o tmp tmp.c
希望这有帮助。
谢谢。这是非标准的。这是否可能(以及如何可能)完全取决于您使用的是哪种C实现。我认为没有其他方法可以做到这一点。可能有一个特定于编译器的编译器命令行选项。“尽可能接近不是一个真正的问题”?认真地有些人似乎对什么构成真正的问题有一个相当奇怪的想法,IMNSHO:-/也许他们认为OP很难,应该使用#pragma
。但是,你猜怎么着?单个pragma不是标准的一部分(STDC除外)。可能是OP使用的编译器没有#pragma pack
,他们正在从一个有的平台移植代码。@Matthew Flaschen:“在标准C中不能这样做”似乎是对这个问题的合理完整回答。注意,在某些系统上访问未对齐的地址(int*pLng=&