为什么这个结构大小为3而不是2?

Raf*_*llo 91 c c++ struct

我已经定义了这个结构:

typedef struct
{
    char A:3;
    char B:3;
    char C:3;
    char D:3;
    char E:3;
} col; 
Run Code Online (Sandbox Code Playgroud)

sizeof(col)给我3的输出,但它不应该是2?如果我只评论一个元素,sizeof则为2.我不明白为什么:3位的5个元素等于15位,并且小于2个字节.

在定义像这样的结构时是否存在"内部大小"?我只需要澄清一下,因为从我到目前为止的语言概念来看,我预计大小为2字节,而不是3字节.

did*_*erc 95

因为您使用的char是字段的基础类型,所以编译器会尝试按字节对位进行分组,并且由于它不能在每个字节中放置超过8位,因此每个字节只能存储两个字段.

你的struct使用的总和是15,所以适合那么多数据的理想大小就是a short.

#include <stdio.h>

typedef struct
{
  char A:3;
  char B:3;
  char C:3;
  char D:3;
  char E:3;
} col; 


typedef struct {
  short A:3;
  short B:3;
  short C:3;
  short D:3;
  short E:3;
} col2; 


int main(){

  printf("size of col: %lu\n", sizeof(col));
  printf("size of col2: %lu\n", sizeof(col2));

}
Run Code Online (Sandbox Code Playgroud)

上面的代码(对于像我这样的64位平台)确实会产生2第二个结构.对于大于a的任何东西short,结构将填充不超过所使用类型的一个元素,因此 - 对于同一平台 - 结构最终将为四个int,八个long等等.

  • @ user3629249为什么unsigned short'正确'?如果用户想要从-4到3存储,则short是正确的.如果用户想要存储从0到7,那么无符号短路是正确的.原始问题使用了签名类型,但我不知道这是故意的还是偶然的. (21认同)
  • @BruceDawson:标准允许实现让`char`无符号... (5认同)
  • 为什么`char`和`short`之间存在差异? (2认同)

Jac*_*ack 78

因为你不能有一个跨越最小对齐边界(1字节)的位包字段,所以它们可能会像

byte 1
  A : 3
  B : 3
  padding : 2
byte 2
  C : 3
  D : 3
  padding : 2
byte 3
  E : 3
  padding : 5
Run Code Online (Sandbox Code Playgroud)

(同一字节内的字段/填充顺序不是故意的,只是为了给你一个想法,因为编译器可以放下它喜欢的方式)


250*_*501 16

前两位字段适合单个字段char.第三个不适合那个char并需要一个新的.3 + 3 + 3 = 9,不适合8位字符.

所以第一对取a char,第二对取a char,最后一位取第三对char.


Kos*_*Kos 15

大多数编译器允许您控制填充,例如使用#pragmas.以下是GCC 4.8.1的示例:

#include <stdio.h>

typedef struct
{
    char A:3;
    char B:3;
    char C:3;
    char D:3;
    char E:3;
} col;

#pragma pack(push, 1)
typedef struct {
    char A:3;
    char B:3;
    char C:3;
    char D:3;
    char E:3;
} col2;
#pragma pack(pop)

int main(){
    printf("size of col: %lu\n", sizeof(col));  // 3
    printf("size of col2: %lu\n", sizeof(col2));  // 2
}
Run Code Online (Sandbox Code Playgroud)

请注意,编译器的默认行为是有原因的,可能会为您提供更好的性能.


sup*_*cat 9

尽管ANSI C标准对于如何打包比特字段提供的任何显着优势都没有提供任何明显的优势,但是"编译器允许打包他们认为合适的位域",但在许多情况下,它仍禁止编译器以最有效的方式打包.

特别是,如果结构包含位域,则需要编译器将其存储为包含一个或多个某些"正常"存储类型的匿名字段的结构,然后在逻辑上将每个这样的字段细分为其组成的位域部分.因此,给出:

unsigned char foo1: 3;
unsigned char foo2: 3;
unsigned char foo3: 3;
unsigned char foo4: 3;
unsigned char foo5: 3;
unsigned char foo6: 3;
unsigned char foo7: 3;
Run Code Online (Sandbox Code Playgroud)

如果unsigned char是8位,则编译器将被要求分配该类型的四个字段,并且将除了一个之外的所有字段分配两个位字段(这将在char它自己的字段中).如果所有char声明都被替换short,那么将有两个类型的字段short,其中一个将保存五个位域,另一个将保留剩余的两个.

在没有对齐限制的处理器上,通过使用unsigned short前五个字段可以更有效地布局数据,而unsigned char对于后两个字段,可以在三个字节中存储七个三比特字段.虽然应该可以在三个字节中存储八个三位字段,但是如果存在可以用作"外部字段"类型的三字节数字类型,则编译器只允许这样做.

就个人而言,我认为定义的位域基本上是无用的.如果代码需要使用二进制打包数据,则应明确定义实际类型的存储位置,然后使用宏或其他一些方法来访问其位.如果C支持如下语法将会很有帮助:

unsigned short f1;
unsigned char f2;
union foo1 = f1:0.3;
union foo2 = f1:3.3;
union foo3 = f1:6.3;
union foo4 = f1:9.3;
union foo5 = f1:12.3;
union foo6 = f2:0.3;
union foo7 = f2:3.3;
Run Code Online (Sandbox Code Playgroud)

如果允许,这样的语法将使代码能够以便携方式使用位域,而不考虑字大小或字节顺序(foo0将位于f1的三个最低有效位中,但那些可以存储在更低或更高的地址).然而,如果没有这样的功能,宏可能是唯一可以随身携带的便携式方式.

  • 不同的编译器将以不同的方式布置位域.我写了一些关于Visual C++如何做到这一点的文档.它指出了一些恼人的陷阱:https://randomascii.wordpress.com/2010/06/06/bit-field-packing-with-visual-c/ (2认同)