1、结构体的声明
1.1 结构的基础知识
结构是一些值得集合,这些值成为成员变量。结构的每个成员可以是不同类型的变量。
1.2 结构的声明
struct tag//tag结构体名,struct是结构体关键字 { member-list; }variable-list;//这里的分号不可缺少
我们以书来举例:
#include <stdio.h> struct BOOK { char name[20]; float price; }b3,b4,b5; int main() { struct BOOK b1; struct BOOK b2; return 0; }
分析:
这里我们创建了一个结构体类型,struct BOOK 类型。
这里每次定义结构体变量的时候类型太长,我们可以对它进行重命名。
重命名
这里用到一个关键字typedef,我们就对上面的代码进行改写:
#include <stdio.h> typedef struct BOOK { char name[20]; float price; }BOOK; int main() { BOOK b1; BOOK b2; return 0; }
分析:
1.3 结构成员的类型
结构的成员可以是标量、数组、指针,甚至是其他结构体。
1.4 结构体变量的定义和初始化
有了结构体类型,那如何定义变量,其实很简单。
1.4.1 结构体变量的定义
我们来看下面的代码:
struct BOOK { char name[20]; float price; }b3;//全局变量 struct BOOK b4;//全局变量 int main() { struct BOOK b1;//局部变量 struct BOOK b2;//局部变量 return 0; }
这里的 b1, b2 是局部变量,b3,b4 是全局变量,全局变量可以在创建结构体的时候就创建。
1.4.2 结构体变量的初始化
我们来看代码:
struct S { char sex[6]; char name[20]; int age; }; int main() { struct S s1 = { "male", "zhangsan", 20 }; return 0; }
上面代码是按照结构体成员的顺序来初始化的。我们也可以不按照顺序来初始化。
我们来看代码:
struct S { char sex[6]; char name[20]; int age; }; int main() { struct S s1 = { "male", "zhangsan", 20 }; struct S s2 = { .name = "wangwu", .sex = "male", .age = 20 }; printf("%s %s %d\n", s1.sex, s1.name, s1.age); printf("%s %s %d\n", s2.sex, s2.name, s2.age); return 0; }
效果展示:
我们不按照顺序初始化的时候是用 .成员名 来赋值的,这样也是可以的。
2、 结构体成员的访问
2.1 结构体变量访问成员
结构变量的成员是通过点操作符(.)访问的。点操作符接受两个操作数。
语法: 结构体变量 . 结构体成员
我们就以上面的代码来看:
struct S { char sex[6]; char name[20]; int age; }; int main() { struct S s1 = { "male", "zhangsan", 20 }; struct S s2 = { .name = "wangwu", .sex = "male", .age = 20 }; //结构体变量.结构体成员 printf("%s %s %d\n", s1.sex, s1.name, s1.age); printf("%s %s %d\n", s2.sex, s2.name, s2.age); return 0; }
2.2 结构体指针访问指向变量的成员
有时候我们得到的不是一个结构体变量,而是指向一个结构体的指针。
那么如何访问成员。
语法:结构体指针->结构体成员
如下:
struct S { char sex[6]; char name[20]; int age; }; void Print(struct S* p1) { //结构体指针->结构体成员 printf("%s %s %d\", p1->sex, p1->name, p1->age); } int main() { struct S s1 = { "male", "zhangsan", 20 }; struct S s2 = { .name = "wangwu", .sex = "male", .age = 20 }; //结构体变量.结构体成员 printf("%s %s %d\n", s1.sex, s1.name, s1.age); printf("%s %s %d\n", s2.sex, s2.name, s2.age); Print(&s1);//结构体地址传参 return 0; }
3、结构体传参
struct S { int arr[100]; int n; }; void Print1(struct S ss) { int i = 0; for (i = 0; i < 5; i++) { printf("%d ", ss.arr[i]); } printf("\n%d\n", ss.n); } int main() { struct S s = { {1,2,3,4,5}, 100 }; Print1(s); return 0; }
效果展示:
性能分析:
这里的结构体 s 的成员arr的字节是400,结构体整体是大于400的,再加上形参再创建一份,这样浪费空间很严重。因此我们直接将 s 交给Print1 来打印,这样就不会浪费空间的。
因此,我们来实现的第二种方法:
struct S { int arr[100]; int n; }; void Print2(struct S* ps) { int i = 0; for (i = 0; i < 5; i++) { printf("%d ", ps->arr[i]); } printf("\n%d\n", ps->n); } int main() { struct S s = { {1,2,3,4,5}, 100 }; Print2(&s); return 0; }
效果展示:
这里两次的打印结果是一样的,传参传的是指针,最多就 8 个字节(x32平台是 4 字节,x64平台是 8 字节),这样就不会额外开辟空间。
Q:在实现打印的时候,上面的 Print1 和 Print2 函数选择哪个好些?
答案是:首选 Print2 函数。
函数传参的时候,参数是需要压栈的。
如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。
结论:结构体传参的时候,要穿结构体的地址。