结构 structure
是C语言中两种聚合数据类型 aggregate data type
之一,是一些值的集合,这些值称为它的成员 member
,各个成员可能具有不同的类型,它们通过名字来访问。
结构变量属于标量类型,可以出现在普通标量变量可以出现的任何场合。结构也可以作为传递给函数的参数,它们也可以作为返回值从函数返回,相同类型的结构变量相互之间可以赋值。你可以声明指向结构的指针,取一个结构变量的地址,也可以声明结构数组。
结构声明
在声明结构时,必须列出它包含的所有成员。这个列表包括每个成员的类型和名字。
struct tag {
member;
member;
...
} variable-list;
-
struct
:关键字,用于引入结构声明。结构声明由包含在花括号内的一系列声明组成。 -
tag
:结构标记,用于为结构命名。 -
member
:结构中定义的变量称为结构成员。 -
variable-list
:结构体上声明的结构变量。
如下所示,定义一个 People
结构。
struct People {
char *name;
int id;
int age;
char *work;
};
如上所示,People
为结构体标记,包含 4 个成员。当结构定义完成后,需如下方式来声明结构变量。
struct People p1;
struct People *p2, p3[10];
还可以在定义结构时,就声明结构变量,如下所示。
struct People {
char *name;
int id;
int age;
char *work;
} p1, *p2, p3[10];
除此之外,声明结构可以用 typedef
创建一种新的类型,如下所示。
typedef struct {
char *name;
int id;
int age;
char *work;
} People;
这种声明后的结构,要想初始化时,需要向下面这样:
People p1;
People *p2, p3[10];
在声明变量时,还可以将标签省略,如下所示。
struct {
char *name;
int id;
int age;
char *work;
} p1, *p2, p3[10];
这种方式声明了变量 p1
、指针p2
和数组 p3
,但后面不可能在定义结构变量了。看一下下面的结构声明。
struct {
char *name;
int id;
int age;
char *work;
} p1;
struct {
char *name;
int id;
int age;
char *work;
} *p2, p3[10];
以上两个声明的成员列表完全相同,但会被编译器当作两种截然不同的类型,因此变量 p2
和 p3
的类型与 p1
的类型不同,因此 p2 = &p1
是非法的。
自引用
当想在一个结构内部包含一个结构本身的成员时,这种自引用是非法的。
struct People {
...
struct People people;
...
};
如上所示,这种方式会报 error: field 'people' has incomplete type
错误,因为成员 people
是另一个完整的结构,其内部还将包含另一个完整结构的成员 people
,如此永无止境的循环下去。但是可以像下面声明那样是合法的。
struct People {
...
struct People *people;
...
};
这种声明和前面的声明的区别是 people
现在是一个指针而不是一个结构。编译器在结构的长度确定之前就已经知道指针的长度,所以这种类型的自引用是合法的,它事实上所指向的是同一种类型的不同结构。
使用 typedef
方式创建的结构,使用指针方式自引用会失败,因为类型名直到声明的末尾才定义,所以在结构声明的内部尚未定义。
typedef struct {
...
People *people;
} People;
上述代码会报 error: unknown type name 'People'
错误,解决方案是定义一个结构标签来声明 people
。
typedef struct PTag {
...
struct PTag *people;
} People;
不完整的声明
当声明相互之间存在依赖的结构时,可以和自引用一样,每个结构都引用其它结构的标签,但是哪个结构先声明呢?解决方案是使用不完整声明 incomplete declaration
,它声明一个作为结构标签的标识符。然后把这个标签用在不需要知道这个结构的长度的声明中,如声明指向这个结构的指针。
struct RefB;
struct RefA {
struct RefB *b;
...
};
struct RefB {
struct RefA *a;
...
};
访问结构成员
定义结构时,在结构外部声明的任何变量都可以作为结构的成员,如标量、数组、指针甚至是其他结构,如下所示。
struct Work {
char *position;
char *company;
};
struct People {
char *name;
int id;
int age;
char *address[50];
struct Work work;
struct Work works[10];
};
直接访问
结构变量的成员可以通过 .
直接访问,如下所示。
struct People p1;
p1.address;
可以通过 .
操作符直接访问 address
这个结构成员,Work
结构也可以使用 .
操作符访问 work
结构成员。
p1.work.position;
.
操作符也可以访问 work
结构变量中的成员。当使用 .
操作符也可以访问结构数组中的成员。
p1.works[5].position;
间接访问
当结构声明为一个指针时,可以使用间接访问操作,获得这个结构,在使用 .
操作符来访问它的成员,如下所示。
struct People *p1;
(*p1).address;
对指针执行间接访问将访问结构,然后点操作符访问一个成员。除了上述方式,C 语言也提供了 ->
操作符来访问,如下所示。
p1->work;
p1->work.company;
结构初始化
结构初始化是位于一对花括号内部,通过逗号分隔初始值,按顺序与结构成员一一对应。如果初始列表的值不够,剩余结构成员将使用缺省值进行初始化。
struct People {
char *name;
int id;
int age;
char *work;
};
struct People p1 = {"Ming", 20111114, 23, "engineer"};
结构初始化列表也可以嵌套于结构的初始值列表内部。
struct People {
char *name;
int id;
int age;
char *work;
} p1 = {
"Ming",
20111114,
23,
"engineer"
};
作为函数参数
函数的参数把值传递给函数,也可以作为返回值从函数返回,值可以是标量、数组或指针。结构也可以作为参数传递给函数。
结构可以作为变量进行参数传递,如下所示。
void print(struct People p) {
printf("name=%s, id=%d, age=%d, work=%sn", p.name, p.id, p.age, p.work);
}
int main() {
struct People p1 = {"Ming", 20111114, 23, "engineer"};
print(p1);
}
// name=Ming, id=20111114, age=23, work=engineer
也可以作为结构指针进行参数传递。
void print(struct People *p) {
printf("name=%s, id=%d, age=%d, work=%sn", p->name, p->id, p->age, p->work);
}
int main() {
struct People p1 = {"Ming", 20111114, 23, "engineer"};
print(&p1);
}
// name=Ming, id=20111114, age=23, work=engineer
使用结构指针进行参数传递要比传递结构变量要小得多,所以把它压到堆栈上效率能提高很多。但向函数传递指针的缺陷在于函数可以对调用程序的结构变量进行修改。如果不希望修改,可以在函数中使用 const
关键字来防止这类修改。
void print(struct People const *p);
原文始发于微信公众号(海人为记):一文讲解C语言结构
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/27486.html