把單一元素的數組放在一個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是不包括\0的,但因爲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);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章