为什么我不能将位域声明为自动变量?

时间:2010-08-12 19:39:32

标签: c syntax stack bit-fields

我想声明一个使用冒号指定大小的位域(我不记得调用了什么语法)。我想写这个:

void myFunction() 
{   
  unsigned int thing : 12;
  ...
}

但是GCC说这是一个语法错误(它认为我正在尝试编写一个嵌套函数)。我这样做没问题:

struct thingStruct
{
  unsigned int thing : 4;
};

然后将一个这样的结构放在堆栈上

void myFunction() 
{   
  struct thingStruct thing;
  ...
}

这让我相信它是通过语法而不是语义问题来阻止的。

那么为什么第一个例子不起作用呢?我错过了什么?

4 个答案:

答案 0 :(得分:6)

第一个例子不起作用,因为你只能在结构体内声明位域。正如你所说,这是语法,而不是语义,但它就是这样。如果需要位域,请使用结构。

答案 1 :(得分:3)

你为什么要做这样的事情?所有常见架构的12位字段将填充至少16或32位。

如果要确保整数变量的宽度,请使用inttypes.h中的类型,例如int16_tint32_t

答案 2 :(得分:2)

正如其他人所说,必须在struct(或union内声明位域,但这并不是真的有用)。为什么?这有两个原因。

  • 主要是让编译器编写者的工作更轻松。位域往往需要更多的机器指令来从字节中提取位。只有字段可以是位域,而不是变量或其他对象,因此如果没有涉及.->运算符,编译器编写者不必担心它们。

  • 但是,你说,有时语言设计师会让编译器编写者的工作更加努力,以使程序员的生活更轻松。好吧,程序员对struct之外的位域没有太多需求。原因是当程序员要在单个数据结构中塞入几个小整数时,他们几乎只会烦扰位域。否则,他们会使用普通整数类型。

其他语言具有整数范围类型,例如,您可以指定变量范围从17到42.在C中没有太多要求,因为C从不要求执行检查溢出。所以C程序员只需选择一种能够代表所需范围的类型;无论如何,检查边界是他们的工作。

C89(即您可以在任何地方找到的C语言版本)提供了有限的至少n位的类型选择。 8位为unsigned char,16位为unsigned short,32位为unsigned long(加上有符号变体)。 C99提供了更广泛的类型选择,称为uint_least8_tuint_least16_tuint_least32_tuint_least64_t。这些类型保证是至少具有多个值位的最小类型。实现可以为其他位数提供类型,例如uint_least12_t,但大多数不会。这些类型在<stdint.h>中定义,即使标准不要求,也可以在许多C89实现中使用。

答案 3 :(得分:1)

Bitfields提供一致的语法来访问某些与实现相关的功能。该功能的最常见目的是以某种方式将某些数据项放入位中,相对于彼此。如果在结构中将两个项(位字段或非字段)声明为连续项,则保证它们连续存储。无论存储类别或范围如何,单个变量都不存在此类保证。如果结构包含:

struct foo {
  unsigned bar: 1;
  unsigned boz: 1;
};

保证bar和boz将连续存储(很可能在同一存储位置,但我认为实际上并不保证)。相比之下,'bar'和'boz'是单位自动变量,没有人知道它们将被存储在何处,因此将它们作为位域进行处理几乎没有什么好处。如果他们确实与其他变量共享空间,则很难确保在同一字节中读取和写入不同位的不同函数不会相互干扰。

请注意,某些嵌入式系统编译器确实暴露了一个真正的“位”类型,它们打包为8个字节。这样的编译器通常具有存储器区域,其被分配用于仅存储位变量,并且它们生成代码的处理器具有用于测试,设置和清除各个位的原子指令。由于仅使用这些指令访问保存位的存储器位置,因此不存在冲突的危险。