结构和联合
结构 包含数据对象的有序组。 与数组的元素不同,结构中的数据对象可以具有不同的数据类型。 结构中的每个数据对象都是 成员 或 字段。
union 是一个类似于结构的对象,只是它的所有成员都在内存中的同一位置开始。 联合变量一次只能表示其一个成员的值。
在C++中,结构和联合体与类相同,只是它们的成员和继承在默认情况下是公开的。
您可以在该类型的变量定义之外单独声明结构或并集类型,如 结构和并集类型定义 和 结构和并集变量声明中所述; 也可以在一个语句中定义结构或并集数据类型以及具有该类型的所有变量,如 结构和并集类型以及单个语句中的变量定义中所述。
结构和结合都受到调整考虑因素的制约。 有关对齐的完整讨论,请参阅 "对齐数据"。
结构和并集类型定义
结构或并集 类型定义 包含 struct 或 union 关键字,后跟可选标识 (结构标记) 和用花括号括起的成员列表。
tag_identifier 为类型提供了名称。 如果未提供标记名称,那么必须将所有引用该类型的类型的变量定义放在该类型的声明中,如 结构和并集类型以及单个语句中的变量定义中所述。 同样,不能将类型限定符与结构或并集定义一起使用; 放在 struct 或 union 关键字前面的类型限定符只能应用于在类型定义中声明的变量。
成员声明
成员列表提供了结构或并集数据类型以及可存储在结构或并集中的值的描述。 成员的定义具有标准变量声明的形式。 成员变量的名称在单个结构或并集中必须是不同的,但在同一作用域内定义的另一个结构或并集类型中可以使用相同的成员名称,甚至可以与变量,函数或类型名称相同。
- 任何可变修改类型
void类型
功能- 任何不完整类型
类对象不能是具有构造函数、析构函数或重载复制赋值运算符的类成员,也不能是引用类型。 工会会员不能以 static 作为关键词进行搜索。 
不代表位字段的成员可以使用类型限定符 volatile 或 const进行限定。 结果是 lvalue。
结构成员按递增顺序分配给内存地址,第一个组件从结构名称本身的起始地址开始。 为了允许组件正确对齐,可在结构布局中的任何连续成员之间显示填充字节。
分配给联合的存储器是联合的最大成员所需的存储器 (加上需要的任何填充,以便联合将在其具有最严格要求的成员的自然边界处结束)。 所有并集的组件都有效覆盖在内存中: 并集的每个成员都从并集开始时开始分配存储器,并且一次只能有一个成员占用存储器。
- 灵活数组成员
柔性阵列成员 是 在结构中出现的无界阵列。 这是一个 C99 功能,
XL C/C++ 编译器支持它作为 IBM 扩展
。 可使用灵活的数组成员来访问可变长度对象。 允许柔性阵列成员作为结构的最后一个成员,前提是该结构具有多个指定成员。 它使用空索引进行声明,如下所示:array_identifier [];
b 是结构 f的灵活数组成员。struct f{
int a;
int b[];
};由于弹性阵列成员的类型不完整,因此无法将 sizeof 运算符应用于弹性阵列。 在此示例中,语句 sizeof(f) 返回与 sizeof(f.a)相同的结果,即整数的大小。 无法使用语句 sizeof(f.b) ,因为 b 是类型不完整的灵活数组成员。struct f{
int a;
int b[];
};
struct f fa[10]; // Error.
为了与GNU C/C++ 兼容, XL C/C++ 编译器扩展了标准C 和C++ ,以放宽对灵活数组成员的约束,并允许以下情况:- 可以在结构的任何部分中声明柔性阵列成员,而不仅仅是最后一个成员。 跟在柔性阵列成员后面的任何成员的类型都不需要与柔性阵列成员的类型兼容; 但是,当柔性阵列成员跟在不兼容类型的成员后面时,会发出警告消息。 以下示例对此进行了演示:
struct s { int a; int b[]; char c; // The compiler issues a warning message. } f; - 包含柔性阵列成员的结构可以是其他结构的成员。
只有满足以下两个条件之一时,才能对弹性阵列成员进行静态初始化:- 柔性阵列成员是结构的最后一个成员,例如:
struct f { int a; int b[]; } f1 = {1,{1,2,3}}; // Fine. struct a { int b; int c[]; int d[]; } e = { 1,{1,2},3}; // Error, c is not the last member // of structure a. - 柔性数组成员包含在嵌套结构的最外层结构中。 内部结构的成员不能以静态方式初始化,例如:
struct b { int c; int d[]; }; struct c { struct b f; int g[]; } h ={{1,{1,2}},{1,2}}; // Error, member d of structure b is // in the inner nested structure.

- 柔性阵列成员是结构的最后一个成员,例如:

- 零扩展数据块阵列成员 (IBM 扩展)
零扩展数据块数组是为了实现 GNU C/C++ 兼容性而提供的,可用于访问变长对象。
array_identifier [0]例如, b 是结构 f的零扩展数据块数组成员。struct f{
int a;
int b[0];
}; sizeof 运算符可以应用于零扩展数据块数组,返回的值为 0。 在此示例中,语句 sizeof(f) 返回与 sizeof(f.a)相同的结果,后者是整数的大小。 语句 sizeof(f.b) 返回 0。struct f{
int a;
int b[0];
};
struct f fa[10]; // Fine.{}静态初始化。 否则,必须将其初始化为动态分配的阵列。 例如:struct f{
int a;
int b[0];
};
struct f f1 = {100, {}}; //Fine.
struct f f2 = {100, {1, 2}}; //Error.#include <stdio.h>
struct s {
int a;
int b[0];
};
struct t1 {
struct s f;
int c[3];
} g1 = {{1},{1,2}};
struct t2 {
struct s f;
int c[3];
} g2 = {{1,{}},{1,2}};
int main() {
printf("%d %d %d %d\n", g1.f.a, g1.f.b[0], g1.f.b[1], g1.f.b[2]);
printf("%d %d %d %d\n", g2.f.a, g2.f.b[0], g2.f.b[1], g2.f.b[2]);
return 0;
} 在此示例中,两个 printf 语句生成相同的输出:1 1 2 0
struct s {
int a;
int b[0];
char c; // Issues a warning message
} f;int func(){
int a[0]; // error
struct S{
int x;
char b[0]; // fine
};
}- 位字段成员
C 和 C++ 都允许将整数成员存储到比编译器通常允许的更小的内存空间中。 这些节省空间的结构成员称为 位字段,它们的宽度 (以位计) 可以显式声明。 位字段用于必须强制数据结构与固定硬件表示相对应且不可移植的程序中。
constant_expression 是一个常量整数表达式,用于指示字段宽度 (以位计)。 位字段声明不能使用类型限定符 const 或 volatile。

_Bool, int, signed int和 unsigned int。
位域的数据类型也可以是任何版本的 char、 short 和 long。 如果某个位字段的类型为 char 或 short的任何版本,那么其类型将由 signed int 或 unsigned int替换,具体取决于 -qbitfields的设置。
如果位字段是纯文本 或纯文本 ,则位字段是 还是 取决于 的设置。 int long signed unsigned -qbitfields
位域可以是任何整数类型或枚举类型。 如果位域是纯文本 short、纯文本 int 或纯文本 long ,则位域是 signed 还是 unsigned 取决于 -qbitfields 的设置。
最大位字段长度为 64 位。 要提高可移植性,请不要使用大于 32 位大小的位字段。
kingdom, phylum和 genus,分别占 12 位, 6 位和 2 位:struct taxonomy {
int kingdom : 12;
int phylum : 6;
int genus : 2;
};当您将超出范围的值分配给位字段时,将保留低阶位模式并分配相应的位。
- 定义位字段数组
- 采用位字段的地址
- 有一个指向位字段的指针
- 具有对位字段的引用
struct {
int larry : 25; // Bit Field: offset 0 bytes and 0 bits.
int curly : 25; // Bit Field: offset 3 bytes and 1 bit (25 bits).
int moe; // non-Bit Field: offset 8 bytes and 0 bits (64 bits).
} stooges;larry 和 curly之间没有填充。 curly 的位偏移量将为 25 位。 成员 moe 将在下一个 4 字节边界上对齐,从而在 curly 和 moe之间产生 14 位填充。长度为 0 的位字段必须未命名。 无法引用或初始化未命名的位字段。
int 占用 4 个字节。 此示例声明标识 kitchen 的类型为 struct on_off:struct on_off {
unsigned light : 1;
unsigned toaster : 1;
int count; /* 4 bytes */
unsigned ac : 4;
unsigned : 4;
unsigned clock : 1;
unsigned : 0;
unsigned flag : 1;
} kitchen;kitchen 包含 8 个成员,总计 16 个字节。 下表描述了每个成员占用的存储器:| 成员名 | 存储器已占用 |
|---|---|
light |
1 位 |
toaster |
1 位 |
| (padding-30 位) | 到下一个 int 边界 |
count |
int 的大小 (4 字节) |
ac |
4 位 |
| (未命名字段) | 4 位 |
clock |
1 位 |
| (padding-23 位) | 到下一个 int 边界 (未命名字段) |
flag |
1 位 |
| (padding-31 位) | 到下一个 int 边界 |
结构和联合变量声明
结构或并集 声明 具有与定义相同的格式,但声明没有括在花括号内的成员列表。 必须先声明结构或并集数据类型,然后才能定义具有该类型的变量。
tag_identifier 指示结构或并集的 数据类型 。
关键字 struct 在结构变量声明中是可选的。
您可以声明具有任何存储类的结构或并集。 变量的存储类说明符和任何类型限定符必须出现在语句的开头。 使用 register 存储类说明符声明的结构或并集将被视为自动变量。
address:struct address {
int street_no;
char *street_name;
char *city;
char *prov;
char *postal_code;
};
address的结构变量:struct address perm_address;
struct address temp_address; 单个语句中的结构和并集类型以及变量定义
length):union {
float meters;
double centimeters;
long inches;
} length;请注意,由于此示例未命名数据类型,因此 length 是唯一可以具有此数据类型的变量。 将标识放在 struct 或 union 关键字之后可提供数据类型的名称,并允许您稍后在程序中声明此数据类型的其他变量。
static struct {
int street_no;
char *street_name;
char *city;
char *prov;
char *postal_code;
} perm_address, temp_address;在这种情况下,将同时为 perm_address 和 temp_address 分配静态存储器。volatile struct class1 {
char descript[20];
long code;
short complete;
} file1, file2;
struct class1 {
char descript[20];
long code;
short complete;
} volatile file1, file2;
在这两种情况下,结构 file1 和 file2 都限定为 volatile。
访问结构和联合成员
.) 指定变量名或使用箭头运算符 (->) 和成员名指定指针来引用成员。 例如,以下两项:perm_address.prov = "Ontario";
p_perm_address -> prov = "Ontario";将字符串 "Ontario" 分配给结构 perm_address中的指针 prov 。所有对结构和联合成员 (包括位字段) 的引用都必须是完全限定的。 在上一个示例中,第四个字段不能单独由 prov 引用,而只能由 perm_address.prov引用。

匿名结构
- 该结构嵌套在另一个结构或并集内。
- 该结构没有标记。
- 结构没有名称。
struct v {
union {
// This is an anonymous structure, because it has no tag, no name,
// and is a member of another structure or union.
struct { int i, j; };
// This is not an anonymous structure, because it has a name.
struct { long k, l; } w;
// This is not an anonymous structure, because
// the structure has a tag "phone".
struct phone {int number, areanumber;};
};
int m;
} v1;匿名工会
匿名联合 是没有标记或名称的联合,并且是另一个联合或结构的成员。 它不能后跟声明程序。 匿名联合不是一种类型; 它定义未命名的对象。
匿名联合的成员名必须与声明联合的作用域内的其他名称不同。 您可以直接在并集作用域中使用成员名,而无需任何其他成员访问语法。
i 和 cptr ,因为它们位于包含匿名联合的作用域中。 由于 i 和 cptr 是并集成员并且具有相同的地址,因此一次只能使用其中一个成员。 对成员 cptr 的分配将更改成员 i的值。void f() {
union { int i; char* cptr ; };
/* . . . */
i = 5;
cptr = "string_in_union"; // Overrides the value 5.
}
匿名工会不能有受保护或私人的成员,也不能有成员职能。 全局或命名空间匿名联合必须使用关键字 static 进行声明。

