把单一元素的数组放在一个struct的尾端

1. 出现本文的原因

2. 介绍 这种技巧

3. 修改书中的错误

1. 出现本文的原因

在《深度探究c++对象模型》看到这样一段代码:

1 struct mumble {
2   /* stuff */
3   char pc[ 1 ];
4 };
5 // grab a string from file or standard input
6 // allocate memory both for struct & string
7 struct mumble *pmumb1 = ( struct mumble* ) malloc(sizeof(struct mumble)+strlen(string) + 1);  
8 strcpy( &pmumb1.pc, string );

在那本书中并不是为了介绍 "把单一元素的数组放在一个struct的尾端问题" 的这个trick的。但在看项目中服务器发包给客户端的时候,也用到了这种格式,为什么要这么写,分析记录一下。

2. 介绍 这种技巧

简化项目中例子:

定义一个可变包结构&&如何往可变包结构中填充数据。

#pragma pack(1)
typedef unsigned char BYTE;
// 1. 定义一个 送花排行信息 包结构 。结构体的最后是 只含一个元素的一维数组。
struct FlowerRank
{
    // 2. 定义 排行信息单元,玩家名字和被送花数目 也就是 可变单元里 的元素的结构。 
    struct RankUnit 
    {
        char szName[32];
        int iNum;
        RankUnit()
        {
            memset(szName, 0, 32 * sizeof(char));
            iNum = 0;
        }
    };
    BYTE packetType;     // 指明此消息的类型 方便接收方解析消息。
    RankUnit rankData[1]; // 排行信息单元 从这里开始存。注:此变量也可成 char rankData[1];
};

// 
int main()
{
// 获取下结构体的大小
int a = sizeof(FlowerRank); // 37 size //注:如果 char rankData[1] ,那么 sizeof(FlowerRank) = 2 size; a = sizeof(FlowerRank::RankUnit); // 36 size   
  
   // 3. 根据需要塞入的 排行信息单元的 数目多少,申请对应的内存空间。
const int iCounts = 10; // 10个单元 // 占了一段内存,在此上对应结构体成员。 pData 大小 361 char pData[sizeof(FlowerRank::RankUnit) * (iCounts -1) + sizeof(FlowerRank)] = { 0 }; // 注:如果 char rankData[1] ,那么 // char pData[sizeof(FlowerRank::RankUnit) * iCounts + sizeof(FlowerRank) - 1] = { 0 }; a = sizeof(pData); // 361 size

   // 4. 在申请的空间上 定义变量类型 如何读取内存 FlowerRank* pPacket = new (pData) FlowerRank; FlowerRank::RankUnit* pRankList = new (pPacket->rankData) FlowerRank::RankUnit[iCounts]; // 5. 塞入实际的信息。为了方便书写10个都填入一样的数据了 for (int i = 0; i < iCounts; ++i) { strncpy_s(pRankList[i].szName, "Goddess", 32); pRankList[i].iNum = 10; } // 6. 发送包
pPacket
 return 0; }

可以看到定义可变包结构时候,结构中没有可变包的大小,而是只要在结构里最后加一个元素的字节数组就可以。(也可以加一个元素的 信息单元结构的数组,这样方便理解,但是结构体大小会大点)。

这个技巧利用了两点:

1) struct在内存中的布局。

2) 指针之间的相互转换,内存地址可以按照不同类型的数据来解释。

 这里有一个问题:把

 RankUnit rankData[1] ----》RankUnit* rankData; 会怎么样?
char rankData[1] ----》char* rankData; 会怎么样?

这样整个结构体的数据是不连续的的。改变成指针后指向了另外一块内存,而不是和结构体的其他数据成员连续存放了。

3. 修改书中的错误

struct mumble {
   /* stuff */
   char pc[ 1 ];

};
// 0. 注意下面的 string不是类型,是实际字符串。
// 1. 去掉 + 1, 因为尽管strlen是不包括的,但因为pc中已经有一个字节了,所以这里不需要再加1了。当然加1也不会有问题,只是没有必要而已。
struct mumble *pmumb1 = ( struct mumble* ) malloc(sizeof(struct mumble)+strlen(string) + 1);
struct mumble *pmumb1 = ( struct mumble* ) malloc(sizeof(struct mumble)+strlen(string));
// 2. pmumbl是指针,应该用->;而且pc是指针了,也不能再取地址了。
strcpy( &pmumb1.pc, string );
strcpy_s(pmumb1->pc,strlen(string) + 1, string);
原文地址:https://www.cnblogs.com/fulina/p/6003872.html