一.结构体的声明
1.结构体类型的声明
1.1结构的基础知识
结构是一些值的集合,这些值称为称为变量。结构的每个成员可以是不同类型的变量。
1.2结构的声明
struct tag //struct是结构体关键字,tag是结构体类型名称
{
member - list;//成员变量的列表
}variable - list;//变量列表
为什么要学习结构体?之前学过的char、int、short、double等都是一些内置类型,我们用这些类型是无法表示一个复杂对象——人:姓名+性别+年龄+电话+地址...所以C语言引入了结构体来近似的描述上述人(对象)的特征。比方说我们要创建一个学生结构体,首先要声明学生类型,因为C语言规定利用类型创建变量(为啥变量要有类型?因为你得告诉内存你需要多大空间,int类型的变量就占4字节,char就占1字节),再把值存变量里->int a = 10。所以要先有学生这个类型,而要有学生这个类型,我们就需要声明一个结构体来做学生类型,再把创建一个学生类型的变量->
struct Student
{//学生的相关属性char name[20];int age;char sex[5];char tele[12];//...
};
使用这个类型来创建变量,其实就是告诉内存我要申请多大空间来存我的复杂对象。
struct Student
{//学生的相关属性char name[20];int age;char sex[5];char tele[12];//...
}s3, s4;int main()
{struct Student s1;struct Student s2;return 0;
}
注意创建s1、s2变量的 方法别漏加struct(C语言中,struct Student才是类型名)。s1-s4都是结构体变量,两种创建方式都ok。差异是s3、s4是全局变量,s1和s2是局部变量。如果想偷懒省略掉可以使用typedef重定义类型名->
typedef struct Student
{//学生的相关属性char name[20];int age;char sex[5];char tele[12];//...
}Student;Student s3, s4;int main()
{Student s1;Student s2;return 0;
}
注意:此时用struct Student来创建变量也可以,typedef之后,全局变量的形式就变成了上面那种,不能跟着写Student后面。
1.3结构体成员类型
结构体成员可以是标量(基本数据类型——char,int,double等,不是复合数据类型——数组,结构体,联合体等)、数组、指针,甚至是其他结构体。
1.4结构体成员的定义与初始化
定义(创建):
struct B
{char c;int i;
};struct S
{char c;int num;int arr[20];double* pd;struct B sb;struct B* pb;
};
初始化(定义的同时给值):
#include <stdio.h>struct B
{char c;int i;
};struct S
{char c;int num;int arr[20];double* pd;struct B sb;struct B* pb;
};int main()
{double d = 3.14;//按照顺序初始化struct S s3 = { 'q', 100, { 1, 2, 3}, &d, {'a', 99}, NULL };//记得包含头文件stdio.h//指定成员初始化struct S s4 = { .num = 1000, .arr = {1, 2, 3, 4, 5} };return 0;
}
可以观察到s4里面未赋值到的成员是默认给0,s4要是一个成员都不初始化,那就是随机值,和数组的初始化是类似的。
二.结构体成员的访问
结构体变量的成员是通过点操作符.访问的,点操作符接受两个操作数,结构体变量.成员名还可以通过结构体指针的->,访问结构体指针->成员名。
#include <stdio.h>
#include <string.h>struct S
{char name[20];int age;
};void set_S(struct S t)
{t.age = 20;//t.name = "zhangsan";//这是错的,因为name是数组名,它是个地址常量strcpy(t.name, "zhangsan");//应该用字符串拷贝
}int main()
{struct S s = { 0 };//写一个函数给S存放数据set_S(s);return 0;
}
能够发现结构体s并没有被赋值成功,那问题出在哪呢?因为传参的时候,用的是传值传参,形参是实参的一份临时拷贝,二者并不是同一块内存空间,所以结构体变量t被赋值完,出了函数,生命周期结束,跟s没啥关系。所以我们应该传址传参->
既然Set_S()函数的参数用的是指针接受,那就可以用结构体指针->成员名来访问成员,简单直观明了。
如果要实现一个打印结构体成员的函数,传值或者传址都是行的,因为我只是打印,不对数据做修改->
void Print_S(struct S t)
{printf("%s %d", t.age, t.name);
}
三.结构体传参
在结构体成员访问那块,其实已经讲了结构体传参,当我们在打印的时候,有两种传参方式都是可以的,但是传指针的效率高一点,因为传值,要先生成一份拷贝,当结构体很大的时候,压栈的时间花销是很大的;而传指针,大小是4/8,时间和空间效率都很高。 所以结构体传参也最好使用传址传参。
void Print_S(struct S* t)
{printf("%s %d", t->name, t->age);
}