5.1.4. 结构、联合、枚举和位域

本节介绍了结构化数据类型 union、enum 和 struct 实现; 还讨论了结构填充和位域实现。

有关详细信息,请参阅匿名类、结构和联合

联合

使用其他类型的成员访问 union 的成员时,可以从原始类型的表示形式预测产生的值。 不会生成任何错误。

枚举

enum 类型的对象是使用包含 enum 范围的最小整型实现的。 根据 enum 中的枚举器范围,enum 的存储类型为以下内容的前者:

  • unsigned char,如果未使用 --enum_is_int

  • signed char,如果未使用 --enum_is_int

  • unsigned short,如果未使用 --enum_is_int

  • signed short,如果未使用 --enum_is_int

  • signed int

  • unsigned int,C 使用 --strict 时除外

  • signed long long,C 使用 --strict 时除外

  • unsigned long long,C 使用 --strict 时除外

通过按这种方式实现 enum,可以减小数据大小。 命令行选项 --enum_is_int 强制将 enum 的基础类型设置为至少与 int 一样宽。

有关详细信息,请参阅 《ARM 体系结构的过程调用标准》规范中的 C 语言映射说明。

Note

如果要混合已使用和未使用 --enum_is_int 选项编译的转换单元,并且它们使用相同的接口或数据结构,则必须格外小心。

处理超出范围的值

在严格 C 中,必须能够将枚举器值表示为 int,例如,它们必须在 -2147483648 到 +2147483647 范围内(含这两个值)。 在以前版本的 RVCT 中,超出范围的值将转换为 int,而不会发出警告(除非指定了 --strict 选项)。

在 RVCT 2.2 和更高版本中,枚举器值超出范围时,将会发出警告:


#66: enumeration value is out of "int" range

这些值的处理方式与 C++ 相同,即,将它们作为 unsigned intlong longunsigned long long 进行处理。

要确保报告超出范围的警告,请使用以下命令将它们更改为错误:


armcc --diag_error=66 ...

结构

以下内容适用于:

  • 所有 C 结构

  • 所有不使用虚拟函数或基类的 C++ 结构和类。

结构对齐

非压缩结构的对齐边界是其任何字段所需的最大对齐边界。

字段对齐

结构的排列方式为:将第一个已命名组件放在最低地址中。 字段按以下方式进行对齐:

  • char 类型的字段与下一个可用字节对齐。

  • short 类型的字段与下一个偶数地址的字节对齐。

  • 在 RVCT 2.0 和更高版本中,doublelong long 数据类型为 8 字节对齐。 这样,便可有效地使用 ARMv5TE 和更高版本中的 LDRDSTRD 指令。

  • 位域对齐取决于位域的声明方式。 有关详细信息,请参阅压缩结构中的位域

  • 所有其他类型按字边界对齐。

结构可以包含填充以确保正确对齐字段以及结构本身。Figure 5.1 显示了一个常规非压缩结构的示例。 它填充了第 1、第 2 和第 3 个字节以确保正确对齐字段。 它还填充了第 11 和第 12 个字节以确保正确对齐结构。 sizeof() 函数返回结构大小(包括填充)。

Figure 5.1. 常规非压缩结构示例

根据结构的定义方式,编译器使用以下方式之一填充结构:

  • 用零填充定义为 staticextern 的结构。

  • 使用以前存储在堆栈或堆中的任何内容填充这些内存位置上的结构,例如,使用 malloc()auto 定义的结构。 不能使用 memcmp() 比较以这种方式定义的填充结构(请参阅Figure 5.1)。

可以使用 --remarks 选项查看编译器在 struct 中插入填充时生成的消息。

C++ 中允许使用带有空初始值设定项的结构:


struct

{

    int x;

} X = { };

然而,如果使用 -cpp--c90 选项编译 C 或 C++,则会生成错误。

压缩结构

压缩结构是一种结构及其内部字段的对齐边界始终为 1 的结构。

压缩结构是使用 __packed 限定符定义的。 无法使用命令行选项更改结构的缺省压缩方式。

位域

在非压缩结构中,ARM 编译器在容器 中分配位域。 容器是已声明类型的正确对齐的对象。

分配位域时,应确保指定的第一个字段占用字的最低地址位,具体取决于配置:

小端

最低地址为最低有效位。

大端

最低地址为最高有效位。

位域容器可以是任何整型。

Note

在严格 1990 ISO 标准 C 中,允许使用的位域类型仅为 intsigned intunsigned int。对于非 int 位域,编译器将显示错误。

没有使用 signedunsigned 限定符声明的普通位域按 unsigned 处理。 例如,int x:10 分配 10 位无符号整数。

位域将分配给第一个具有足够未分配位数的正确类型的容器,例如:


struct X

{

    int x:10;

    int y:20;

};

第一个声明创建一个整数容器,并为 x 分配 10 位。 在第二个声明中,编译器查找具有足够未分配位数的现有整数容器,并在与 x 相同的容器中分配 y

位域完全包含在其容器中。 如果无法将位域放入某个容器中,则会将其放在下一个相同类型的容器中。 例如,如果为结构声明了其他位域,z 声明将溢出容器:


struct X

{

    int x:10;

    int y:20;

    int z:5;

};

编译器填充第一个容器的剩余两位,并为 z 分配一个新的整数容器。

位域容器可以相互重叠,例如:


struct X

{

    int x:10;

    char y:2;

};

第一个声明创建一个整数容器,并为 x 分配 10 位。 这 10 位占用该整数容器的第 1 个字节以及第 2 个字节的两位。 在第二个声明中,编译器检查 char 类型的容器。 由于没有适合的容器,因此,编译器分配一个正确对齐的新 char 容器。

由于 char 的自然对齐边界为 1,因此,编译器搜索包含足够未分配位数的第一个字节,以便完全包含该位域。 在示例结构中,int 容器的第二个字节为 x 分配了两位,有六位没有分配。 编译器分配一个 char 容器(从前一个 int 容器的第二个字节开始),跳过分配给 x 的前两位,然后为 y 分配两位。

如果将 y 声明为 char y:8,则编译器填充第二个字节,然后为第三个字节分配一个新的 char 容器,因为位域不能溢出其容器。Figure 5.2 显示了以下示例结构的位域分配情况:


struct X

{

    int x:10;

    char y:8;

};

Figure 5.2. 位域分配 1

Note

相同的基本规则适用于具有不同容器类型的位域声明。 例如,要为示例结构添加 int 位域,请使用以下代码:


struct X

{

    int x:10;

    char y:8;

    int z:5;

}

编译器分配一个 int 容器(从与 int x:10 容器相同的位置处开始),并分配一个字节对齐的 char 位域和一个 5 位的位域,请参阅Figure 5.3

Figure 5.3. 位域分配 2

通过声明零大小的未命名位域,可以显式地填充位域容器。 如果容器不为空,则使用零大小的位域填充容器,直至末尾。 后续位域声明开始一个新的空容器。

压缩结构中的位域

压缩结构中的位域容器的对齐边界为 1。 因此,压缩结构中位域的最大位填充是 7 位。 对于非压缩结构,最大填充是 8*sizeof(container-type)-1 位。

Copyright © 2007 ARM Limited. All rights reserved. ARM DUI 0348AC
Non-Confidential