簡體   English   中英

在Linux(gcc)和Windows(mingw32 gcc)之間具有不同位域的結構的大小

[英]Size of struct with bitfields different between Linux (gcc) and Windows (mingw32 gcc)

類似的問題,但具體到打包結構: 為什么在使用gcc時Linux和Windows上打包結構的大小會有所不同?


我正在為Linux和Windows構建一個共享庫,需要通過網絡連接處理結構良好的數據。 我在Linux上使用gcc 4.8.2,並使用i686-pc-mingw32-gcc 4.8.1對Windows目標進行交叉編譯。

我已經制作了這個小程序來演示這個問題(請注意GCC屬性已被注釋掉,留待參考):

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

typedef uint16_t word_t;

typedef enum //__attribute__((__packed__))
{
  PRIO_0 = 0,
  PRIO_1,
  PRIO_2,
  PRIO_3,
  PRIO_4,
  PRIO_5,
  PRIO_6,
  PRIO_7,
}
prio_t;

typedef enum //__attribute__((__packed__))
{
  FLAG_A = 0,
  FLAG_B,
}
flag_t;

typedef struct //__attribute__((__packed__))
{
  word_t id     : 8;
  prio_t prio   : 3;
  flag_t flag_1 : 1;
  flag_t flag_2 : 1;
  flag_t flag_3 : 1;
  flag_t flag_4 : 1;
  word_t spare  : 1;
}
recd_t;


int main(int argc, char *argv[])
{
#define NAME_WIDTH 32

  printf("%-*s = %lu\n", NAME_WIDTH, "sizeof(prio_t)", (unsigned long)sizeof(prio_t));
  printf("%-*s = %lu\n", NAME_WIDTH, "sizeof(flag_t)", (unsigned long)sizeof(flag_t));
  printf("%-*s = %lu\n", NAME_WIDTH, "sizeof(recd_t)", (unsigned long)sizeof(recd_t));

  return 0;
}

我正在使用以下命令編譯Linux: gcc -g -Wall test.c -o ./test

和Windows: i686-pc-mingw32-gcc -g -Wall test.c -o ./test.exe

我覺得很直白。 在Linux上運行時,輸出就是我所期望的:

sizeof(prio_t)                   = 4
sizeof(flag_t)                   = 4
sizeof(recd_t)                   = 4

但在Windows上:

sizeof(prio_t)                   = 4
sizeof(flag_t)                   = 4
sizeof(recd_t)                   = 12

那么Windows尺寸的處理是什么? 在這種情況下,為什么它們與Linux不同?


我最終需要打包這些枚舉和結構,但這個問題出現在任何包裝完成之前。 啟用時,結果類似:

Linux的:

sizeof(prio_t)                   = 1
sizeof(flag_t)                   = 1
sizeof(recd_t)                   = 2

視窗:

sizeof(prio_t)                   = 1
sizeof(flag_t)                   = 1
sizeof(recd_t)                   = 6

C規范有一個信息性附件(附件J),它總結了未指定的行為,未定義的行為和實現定義的行為。 這是關於位域的說法。

J.3實現定義的行為

需要符合要求的實施方案來記錄本子條款中列出的每個領域的行為選擇。 以下是實現定義:

J.3.9結構,聯合,枚舉和位域

  • 是否將“plain”int位字段視為signed int位字段或無符號int位字段(6.7.2,6.7.2.1)。

  • 除_Bool,signed int和unsigned int(6.7.2.1)以外的允許位字段類型。

  • 是否允許原子類型用於位域(6.7.2.1)。
  • 位域是否可以跨越存儲單元邊界(6.7.2.1)。
  • 單元內位域分配的順序(6.7.2.1)。
  • 結構的非位域成員的對齊(6.7.2.1)。 除非一個實現寫入的二進制數據被另一個實現讀取,否則這應該沒有問題。
  • 與每個枚舉類型兼容的整數類型(6.7.2.2)。

您可以得出自己的結論,但我不會在可移植的代碼中使用位字段。


似乎在Windows上,每次類型更改時,編譯器都會啟動一個新的“單元”。 因此在解壓縮的情況下,你有一個word_t(2個字節),后跟一個prio_t(4個字節),一個flag_t(4個字節),另一個word_t(2個字節),總共12個字節。 打包時它是2,1,1,2,總共6個。如果你將所有字段聲明為uint16_t,你可能會在windows上獲得正確的大小,但你仍然有“按位分配順序”的問題- 單元內的字段“是實現定義的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM