目录
前言
我们之前了解了数组,知道数组是一组相同类型元素的组合,元素类型是char,int,float,double等中的一种。单一的类型可以描述事物的部分特征,但并不能表述清楚一个复杂对象,例如,人:姓名+性别+年龄+电话+地址+.....。
因此,C语言提供了一种自定义数据类型,可以包含若干个“成员”,其类型可以相同也可以不同,这种数据结构成为结构体。
一、结构体类型的声明
1.1 结构体类型的声明
struct tag
{
member - list;
}variable - list;
首先,声明结构体,必须声明(创建)结构的类型,关键字:struct
其中,tag:结构体标签,根据实际情况命名;member - list:成员变量的列表,需要列举了结构体包含的所有变量的类型;variable - list:变量列表,可写可不写,其作用在于创建了一个结构体变量,变量的类型是创建的结构体的类型,如果不写,就是表明创建了结构体变量。
例如,描述一个学生的相关属性:
struct Stu
{
char name[10];
int age;
char sex[5];
char tele[12]
}s1, s2;
上述代码表示声明了一个结构体类型,结构体类型名为Stu,s1和s2是结构体类型变量。也可以在main函数中创建结构体变量:
int main()
{
struct Stu s3;
struct Stu s4;
return 0;
}
区别在于,s1和s2是全局变量,s3和s4是局部变量。
1.2 typedef关键字
当结构体类型名过程时,可以采用typedef进行简化重定义。例如:
struct student Stu //使用typedef,可以在下边省略struct
{
char name[10];
int age;
char sex[5];
char tele[12];
}Stu; //Stu是重命名产生的新的类型
int main()
{
Stu s5; //在使用Stu的时候其实就是struct Stu
Stu s6;
return 0;
}
1.3 结构体成员的类型
结构体的成员可以是标量、数组、指针,甚至是其他结构体。例如:
struct T
{
char c;
int i;
};
struct S
{
char c;
int num;
int arr[10];
double* pd;
struct T st;
struct T* pt;
}s1; //s1是全局变量
struct S s2; //s2是全局变量
int main()
{
struct S s3; //s3是局部变量
return 0;
}
1.4 结构体变量的定义和初始化
以1.3结构体成员类型为例:
int main()
{
double d = 3.14;
//严格按照顺序初始化
struct S s3 = {'q', 100, {1, 2, 3}, &d, {'a', 99}, NULL};
//指定成员来初始化
struct S s4 = {.num = 1000, .arr = {1, 2, 3, 4, 5} };
return 0;
}
二、结构体成员的访问
结构体变量和结构体指针都可以对结构体中的成员进行访问。首先我们创建一个结构体变量:
struct S
{
char name[20];
int age;
}
2.1 结构体变量访问成员
结构体变量中的成员是通过点操作符(.)直接访问的。
使用方式:结构体变量.成员名
#include<string.h>
void set_s(struct S t)
{
t.age = 18;
//t.name = "zhangsan"; //t.name确实能够找到结构体中的name,但这样写是错误的,name是这块空间的起始地址
//因为name是数组名,相当于地址。应该把“zhangsan”放在name的空间中
strcpy(t.name, "zhangsan");//字符串拷贝,把后边的字符串拷贝到一块空间中
}
void print_s(struct S t)
{
printf("%s %d\n", t.name, t.age);
}
int main()
{
struct S s = { 0 };
//写一个函数给s中存放数据
set_s(s);
//写一个函数打印s中的数据
print_s(s);
return 0;
}
但此时打印出的结果并不是我们所需要设置的,经过调试后发现其原理如图:
把数据放在 t 中并不会影响 s 。实参传递给形参的时候,形参是实参的一份临时拷贝,对形参的修改不会影响实参。
2.2 结构体指针访问成员
其实,set_s的目的在于设置结构体中的数据,所以我们应该传地址过去。
因此,我们把ps解引用,找到s:
#include<string.h>
void set_s(struct S* ps)
{
(*ps).age = 18;
strcpy((*ps).name, "zhangsan");
}
int main()
{
struct S s = { 0 };
set_s(&s);
print_s(s);
return 0;
}
通过调试,我们发现,ps确实远程遥控了s。
但上述有些繁琐,我们知道:结构体指针通过箭头操作符(->)访问成员。
使⽤⽅式:结构体指针->成员名
所以有:
#include<string.h>
void set_s(struct S* ps)
{
ps->age = 18; //ps指向的对象成员age
strcpy(ps->name, "zhangsan");
}
int main()
{
struct S s = { 0 };
set_s(&s);
print_s(s);
return 0;
}
三、结构体传参
由2.1可知,结构体传参可以传地址,也可以传结构体变量。所以当我们涉及到结构体传参的时候,结构体变量和地址,传哪个比较合适呢?
结构体传参,尽量传址,因为函数传参的时候,参数是需要压栈的。
如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能下降。