结构体声明
结构体是一种类型,它由一系列成员组成,这些成员的存储空间按顺序分配(与联合体相反,联合体是一种类型,它由一系列成员组成,这些成员的存储空间重叠)。
结构体的类型说明符与union
类型说明符相同,只是关键字不同
目录 |
[编辑] 语法
struct attr-spec-seq(可选) name(可选) { struct-declaration-list } |
(1) | ||||||||
struct attr-spec-seq(可选) name |
(2) | ||||||||
struct
name ;
,则声明但不定义结构体 name
(参见下面的前置声明)。在其他上下文中,命名先前声明的结构体,并且不允许使用 attr-spec-seq。name | - | 正在定义的结构体的名称 |
struct-declaration-list | - | 任意数量的变量声明、位域声明和 静态断言声明。不允许使用不完整类型的成员和函数类型的成员(柔性数组成员除外,如下所述) |
attr-spec-seq | - | (C23)应用于结构体类型的属性的可选列表 |
[编辑] 解释
在结构体对象中,其元素的地址(以及位域分配单元的地址)按照成员定义的顺序递增。指向结构体的指针可以转换为指向其第一个成员的指针(或者,如果成员是位域,则转换为其分配单元的指针)。同样,指向结构体第一个成员的指针可以转换为指向包含该结构体的指针。结构体的任意两个成员之间或最后一个成员之后可能存在未命名的填充,但第一个成员之前不存在。结构体的大小至少与其成员大小之和一样大。
如果结构体定义了至少一个命名成员,则允许额外声明其最后一个成员为不完整数组类型。当访问柔性数组成员的元素时(在表达式中使用运算符 struct s { int n; double d[]; }; // s.d is a flexible array member struct s t1 = { 0 }; // OK, d is as if double d[1], but UB to access struct s t2 = { 1, { 4.2 } }; // error: initialization ignores flexible array // if sizeof (double) == 8 struct s *s1 = malloc(sizeof (struct s) + 64); // as if d was double d[8] struct s *s2 = malloc(sizeof (struct s) + 40); // as if d was double d[5] s1 = malloc(sizeof (struct s) + 10); // now as if d was double d[1]. Two bytes excess. double *dp = &(s1->d[0]); // OK *dp = 42; // OK s1->d[1]++; // Undefined behavior. 2 excess bytes can't be accessed // as double. s2 = malloc(sizeof (struct s) + 6); // same, but UB to access because 2 bytes are // missing to complete 1 double dp = &(s2->d[0]); // OK, can take address just fine *dp = 42; // undefined behavior *s1 = *s2; // only copies s.n, not any element of s.d // except those caught in sizeof (struct s) |
(自 C99 起) |
与联合体类似,类型为不带 name 的结构体的结构体的未命名成员称为匿名结构体。匿名结构体的每个成员都被认为是封闭结构体或联合体的成员,并保持其结构布局。如果封闭结构体或联合体也是匿名的,则这会递归应用。 struct v { union // anonymous union { struct { int i, j; }; // anonymous structure struct { long k, l; } w; }; int m; } v1; v1.i = 2; // valid v1.k = 3; // invalid: inner structure is not anonymous v1.w.k = 5; // valid 与联合体类似,如果结构体在没有定义任何命名成员(包括通过匿名嵌套结构体或联合体获得的成员)的情况下定义,则程序的行为是未定义的。 |
(自 C11 起) |
[编辑] 前置声明
以下形式的声明
struct attr-spec-seq(可选) name ; |
|||||||||
隐藏标签命名空间中名称 name 的任何先前声明的含义,并在当前作用域中将 name 声明为新的结构体名称,该名称将在稍后定义。在定义出现之前,此结构体名称具有不完整类型。
这允许相互引用的结构体
struct y; struct x { struct y *p; /* ... */ }; struct y { struct x *q; /* ... */ };
请注意,仅通过在另一个声明中使用结构体标签也可以引入新的结构体名称,但是如果标签命名空间中存在具有相同名称的先前声明的结构体,则该标签将引用该名称
struct s* p = NULL; // tag naming an unknown struct declares it struct s { int a; }; // definition for the struct pointed to by p void g(void) { struct s; // forward declaration of a new, local struct s // this hides global struct s until the end of this block struct s *p; // pointer to local struct s // without the forward declaration above, // this would point at the file-scope s struct s { char* p; }; // definitions of the local struct s }
[编辑] 关键字
[编辑] 注释
有关结构体初始化器的规则,请参见结构体初始化。
由于不允许使用不完整类型的成员,并且结构体类型在定义结束之前不是完整的,因此结构体不能具有自身类型的成员。允许使用指向自身类型的指针,并且通常用于实现链表或树中的节点。
由于结构体声明不建立作用域,因此在 struct-declaration-list 中的声明引入的嵌套类型、枚举和枚举器在定义结构体的周围作用域中可见。
[编辑] 示例
#include <stddef.h> #include <stdio.h> int main(void) { // Declare the struct type. struct car { char* make; int year; }; // Declare and initialize an object of a previously-declared struct type. struct car c = {.year = 1923, .make = "Nash"}; printf("1) Car: %d %s\n", c.year, c.make); // Declare a struct type, an object of that type, and a pointer to it. struct spaceship { char* model; int max_speed; } ship = {"T-65 X-wing starfighter", 1050}, *pship = &ship; printf("2) Spaceship: %s. Max speed: %d km/h\n\n", ship.model, ship.max_speed); // Address increase in order of definition. Padding may be inserted. struct A { char a; double b; char c; }; printf( "3) Offset of char a = %zu\n" "4) Offset of double b = %zu\n" "5) Offset of char c = %zu\n" "6) Size of struct A = %zu\n\n", offsetof(struct A, a), offsetof(struct A, b), offsetof(struct A, c), sizeof(struct A) ); struct B { char a; char b; double c; }; printf( "7) Offset of char a = %zu\n" "8) Offset of char b = %zu\n" "9) Offset of double c = %zu\n" "A) Size of struct B = %zu\n\n", offsetof(struct B, a), offsetof(struct B, b), offsetof(struct B, c), sizeof(struct B) ); // A pointer to a struct can be cast to a pointer // to its first member and vice versa. char** pmodel = (char **)pship; printf("B) %s\n", *pmodel); pship = (struct spaceship *)pmodel; }
可能的输出
1) Car: 1923 Nash 2) Spaceship: T-65 X-wing starfighter. Max speed: 1050 km/h 3) Offset of char a = 0 4) Offset of double b = 8 5) Offset of char c = 16 6) Size of struct A = 24 7) Offset of char a = 0 8) Offset of char b = 1 9) Offset of double c = 8 A) Size of struct B = 16 B) T-65 X-wing starfighter
[编辑] 缺陷报告
以下行为变更缺陷报告已追溯应用于先前发布的 C 标准。
DR | 应用于 | 已发布时的行为 | 正确的行为 |
---|---|---|---|
DR 499 | C11 | 匿名结构体/联合体的成员被认为是封闭结构体/联合体的成员 | 它们保留其内存布局 |
[编辑] 参考
- C23 标准 (ISO/IEC 9899:2024)
- 6.7.2.1 结构体和联合体说明符 (p: TBD)
- C17 标准 (ISO/IEC 9899:2018)
- 6.7.2.1 结构体和联合体说明符 (p: 81-84)
- C11 标准 (ISO/IEC 9899:2011)
- 6.7.2.1 结构体和联合体说明符 (p: 112-117)
- C99 标准 (ISO/IEC 9899:1999)
- 6.7.2.1 结构体和联合体说明符 (p: 101-104)
- C89/C90 标准 (ISO/IEC 9899:1990)
- 3.5.2.1 结构体和联合体说明符
[编辑] 参见
C++ 文档 for 类声明
|