学习目标
1. 结构体声明和结构体类型定义
同一种类型的数据的集合是数组,和数组不同,结构体是多种类型的数据的集合。
假设我们要存储一个学生的信息,里面包含了名字、性别、年龄等等信息,就需要用到结构体。
1.1 结构体的声明规则
//声明一个结构体stu,stu是结构体名
//结构体里的声明是结构体成员
struct stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
};//必须要有分号
该声明描述了一个由三个字符数组和一个int型变量组成的结构。该声明并未创建实际的数据对象,只描述了该对象由什么组成。
1.2 结构体类型的定义
- 定义结构体类型变量
上述只是结构体的声明,要想真正存储数据还需要定义实体变量
struct stu data;//定义一个struct stu型的结构体变量data
编译器执行这行代码便创建了一个结构变量data。编译器使用stu模板为该变量分配空间(如下图所示)
- 还可以定义多个struct std类型的变量,包括指针变量
struct std means,document,*ptstd;//定义结构体变量
结构体变量means和document中都包括name、sex、age、id部分。指针ptstd可以指向任意struct std类型的结构体变量,例如means,document。
- 结构体局部变量与结构体全局变量
struct stu
{
//……中间代码省略
}a2;//定义一个全局变量a2,类型为struct stu
int main()
{
struct stu a1;//定义一个局部变量a1,类型为struct stu
return 0;
}
1.3 匿名结构体
//声明一个匿名结构体
struct
{
int a;
};
- 提问:如果创建两个匿名结构体,编译器能否分清楚谁是谁?
struct
{
int a;
}*p;
struct
{
int a;
}b1;
int main()
{
p = &b1;
return 0;
}
答案必然的,并且编译器会把上面的两个声明当成完全不同的两个类型,所以上述代码是非法的。
2. 访问结构体元素的方式
2.1 初始化结构体类型
- 初始化结构体变量也可以像普通变量和数组那样,以std为例:
struct std data = { "zhangsan",12,"man",123 };
这种初始化的方式,要一一对应结构体成员的类型,用逗号分隔。
2.2 用“.”运算符(句点运算符)访问
- 除了直接初始化,还可使用句点运算符来访问结构体元素,达到初始化的效果
struct A
{
int a;
};
int main()
{
struct A a1;//定义
a1.a = 1; //初始化
printf("%d\n", a1.a);
return 0;
}
2.3 用指针访问
- 要用指针访问结构A,首先要拿到struct A类型的成员的地址,然后进到该对象的内存空间进行访问
下面的代码就是拿到了结构体变量a1的地址,a1为struct A类型,那么它的地址就为struct A*类型,对应p1的类型,就可以赋值给p1。
拿到了它的地址以后就可以通过:
(1)箭头运算符解引用访问到它的成员a,并且给a赋值初始化;
(2)指针+句点运算符访问:p1指针变量存放a1的地址,解引用就是a1,那么就可以用句点运算符访问。(注意优先级,要先解引用p1再用句点运算符)
struct A
{
int a;
};
int main()
{
struct A a1;
struct A* p1 = &a1;
p1->a = 1;
printf("%d\n", p1->a);
(*p1).a = 2;
printf("%d\n", (*p1).a);
return 0;
}
3. 结构体传参
struct S
{
int data[1000];
int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s)
{
printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
printf("%d\n", ps->num);
}
int main()
{
print1(s); //传结构体
print2(&s); //传地址
return 0;
}
上面的 print1 和 print2 函数哪个好些?
答案是:首选print2函数。
- 原因
函数传参的时候,参数是需要压栈的。 如果传递一个结构体对象的时候,结构体过大,参数压栈
的系统开销比较大,所以会导致性能的下降。 - 结论
结构体传参的时候,要传结构体的地址。