C++ 有没有办法防止组合类的填充

C++ 有没有办法防止组合类的填充,c++,padding,C++,Padding,假设是64位机器: 如果我从a类开始: struct A { int* a1; //8 int* a2; //8 uint16_t a3; //2 uint16_t a4; //2 uint32_t a5; //4 uint32_t a6; //4 uint32_t a7; //4 }; 现在所有单个成员都已正确对齐,A的大小为32字节,从a5到a1的偏移量为20字节 现在,如果我尝试如下重构: struct A_part1 {

假设是64位机器:

如果我从a类开始:

struct A
{
    int* a1; //8
    int* a2; //8 
    uint16_t a3; //2
    uint16_t a4; //2

    uint32_t a5; //4
    uint32_t a6; //4
    uint32_t a7; //4
};
现在所有单个成员都已正确对齐,
A
的大小为32字节,从
a5
a1
的偏移量为20字节

现在,如果我尝试如下重构:

struct A_part1
{
    int* a1; //8
    int* a2; //8 
    uint16_t a3; //2
    uint16_t a4; //2
};

struct A_new
{
    A_part1 a1; 
    uint32_t a5; //4
    uint32_t a6; //4
    uint32_t a7; //4
};
现在
A_new
的大小是40个字节,因为
A_part1
被填充到24个字节,而
A_new
随后从36个字节填充到40个字节

可能编译器正在尝试确保相邻的
部分1
对齐


如果我知道我只会在
A
中使用
A\u part1
,在代码< > AXPARTHOR> <代码> >中,我必须选择的唯一选项是:<代码> AyNeX/<代码>将具有与原代码相同的特性> A< <代码> < /p> < p>您是正确的:标准C++中没有办法指定“代码<结构> <代码> s的填充布局,因此您会被编译器特定的代码所困扰。控制机制,如
#pragma pack

如果我知道我只会在A中使用A_part1,这是我唯一的选择 必须在此处的零件1上使用pragma包,以确保新零件 是否具有与原A相同的特征

不可以。pragma不是指定布局和填充的唯一方法

在我曾经开发过的一个嵌入式软件中,我们找到了解决pragma问题的几种方法


< C和C++都没有提供内存布局语义。 但是一种很容易理解的技术,至少在逐字段的基础上,是显式地编码字节驻留在类或结构中的位置。通过重构和添加小字节移动方法,可以大大减少工作量

注意:七个字段很小,因此“容易”。五十块地太累了。一百个字段是“让我们写一些代码来写一些代码”。您的容忍度会有所不同

这种技术在需要的地方将字节显式地逐字节移动到类数据中。优点:1)无填充(除非您需要)。2) 对编译器选项更改不敏感。3) 便携式

小示例(编译,但未测试)(注意:任意endian选择,我们使用传统的endian转换)

A类
{
//int*a1;//8 0..7
//int*a2;//8..15
//uint16\u t a3;//2 16..17
//uint16_t a4;//2 18..19
//
//uint32\u t a5;//4等
//uint32\u t a6;//4
//uint32\u t a7;//4
// ...
//修改字段a3
无效a3(uint16_t val){
//如果需要,请更正目标端的val------vvv
数据[Oa3+0]=static_cast((val>>0)&0xff);//LSB
数据[Oa3+1]=静态_cast((val>>8)&0xff);//MSB
}
//访问字段a3
uint16_t a3(){
//如果需要,请更正尾数的val-----------------vvv

uint16_t val=static_cast((数据[Oa3+0]注意a)pragma通常不被认为是可移植的。2)不同的编译器可能具有类似的pragma,但它们的名称不一定相同。3)编译器选项(即优化级别)可能会改变与以前的设置相比的布局,并且您可能需要测试PrabMa仍然在做您想要的。另一方面,我在C++中用PrimaMeX的经验通常是这样的。@ Skimon有几种方法来指定内存结构/类的布局和填充。我没有评估编译选项更改时pragma的行为。下面我的回答是我在生产代码中使用的3种“标准C++”技术之一(即可移植和不带pragma)。
class A
{
   //   int* a1;     //8  0..7
   //   int* a2;     //8  8..15
   //   uint16_t a3; //2  16..17
   //   uint16_t a4; //2  18..19
   //
   //   uint32_t a5; //4  etc
   //   uint32_t a6; //4
   //   uint32_t a7; //4
   // ...

   // modify field a3
   void a3(uint16_t val) {
      // if needed, correct val for destination endianess ------vvv
      data[Oa3+0] = static_cast<uint8_t>((val >> 0) & 0xff); // LSB
      data[Oa3+1] = static_cast<uint8_t>((val >> 8) & 0xff); // MSB
   }

   // access field a3
   uint16_t  a3() {
      // if needed, correct val for endianess ---------------------vvv
      uint16_t val = static_cast<uint16_t>((data[Oa3+0] << 0) + // LSB
                                           (data[Oa3+8] << 8)); // MSB
      return (val);
   }

   // continue for each field

private:
   enum {
      Oa1 = 0, // Offset a1
      Oa2 = 8,
      Oa3 = 16
      //... etc
   };

   uint8_t data[(8+8+2+2+(3*4))]; 
};