- 结构体类型的声明
- 结构体初始化
- 结构体成员访问
- 结构体传参
1. 结构体类型的声明
定义
结构是一些值的集合,这些值称为成员变量,结构的每个成员可以是不同类型的变量。
类比数组:数组是一组相同类型的元素的集合,而结构体是一些值的集合,但是值的类型可以不同。
举例:
struct Stu //struct 是结构体关键字,Stu是这个结构体类型的名字
{
//下面这些是结构体的相关属性,即结构的成员变量
char name[20];//名字
int age;//年龄
char id[20];
}
//这里我们创造出来了一个类型,一个结构体类型
//类型类比于int整形,char字符型
//举例
struct Stu s;//这里表示你拿struct Stu 这个类型创建了一个变量s
区分类型和对象
struct Stu;这就是类型,struct Stu s; 这就是对象,对象是s,类型是struct Stu
还有另外一种方式创建结构体变量,要有区分全局变量和局部变量 。举例:
struct Stu
{
//成员变量
char name[20];//名字
int age;//年龄
char id[20];
} s1,s2;//s1和s2也是结构体变量
//s1,s2是全局变量,因为我在{}外面创建的这两个变量
//同时是在创建这个类型的同时,后面跟着的两个变量
int main()
{
//s是局部变量,仅能在main函数中用的
struct Stu s;
return 0;
}
结构体成员的类型
结构的成员可以是标量、数组、指针,甚至是其它结构体。举例:
struct B
{
char c;
short s;
double d;
};
struct Stu
{
//成员变量
struct B sb;//这个是结构体成员sb,这个结构体成员的类型是 struct B
char name[20];//名字
int age;//年龄
char id[20];
}
2. 结构体变量的定义和初始化
数组的初始化是用{}来初始化的,结构体的初始化也是如此。
举例1:
struct B
{
char c;
short s;
double d;
};
struct Stu
{
//成员变量
struct B sb;//这个是结构体成员sb,这个结构体成员的类型是 struct B
char name[20];//名字
int age;//年龄
char id[20];
}
int main()
{
//s是局部变量
struct Stu s = { {'w', 20, 3.14}, "张三", 30, "202005034"};//对象
//这里有两个{},第一个{}是用来初始化 struct Stu的,第二个是用来初始化变量s的
// {'w', 20, 3.14}用来初始化sb这个结构体成员的
// "张三", 30, "202005034",依次初始化名字、姓名、id这3个成员
return 0;
}
举例2:
struct A
{
int x;
int y;
}p1;//声明类型的同时定义全局变量p1
struct A a;//定义结构体struct A 类型的一个变量a
struct A b = { 3,5 };//定义变量b的同时赋初值
struct B
{
char name[15];
int age;
};
struct B c = { "张三",30 };//对类型struct B 的一个变量c进行初始化
struct C
{
int data;
struct A d;
struct C* next;
}; n1 = { 10,{3,4},NULL };//结构体嵌套初始化
//这里谨记就是有一个struct C类型的指针next变量
//对于指针变量的初始化就是赋空指针
struct C n2 = { 30,{3,9},NULL };
3. 结构体成员的访问
结构体变量的成员是通过点操作符“.”和“->”访问的。点操作符接受两个操作数。
两个操作符的区别:操作符“->”应用于结构体指针(应用的是地址);操作符“."应用于结构体类型的变量(不是地址,而是值)
举例:
struct B
{
char c;
short s;
double d;
};
struct Stu
{
//成员变量
struct B sb;
char name[20];//名字
int age;//年龄
char id[20];
} s1,s2;//s1和s2也是结构体变量
int main()
{
struct Stu s = { {'w', 20, 3.14}, "张三", 30, "202005034" };//对象
//访问结构体变量的成员有两个操作符:. ->
printf("%c\n", s.sb.c);
//先访问结构体struct Stu 类型的变量s,s涵盖了结构体struct Stu中的所有成员
//所以,接着访问成员之一sb,然后sb也是一个结构体类型的变量,故访问另一个
//结构体中的成员c
printf("%s\n", s.id);
//先访问结构体struct Stu 类型的变量s,接着访问成员之一id
struct Stu* ps = &s;
//将s地址传给struct Stu 类型指针ps,然后此时的*ps <==> s
printf("%c\n", (*ps).sb.c);//*ps是一个“值”
printf("%c\n", ps->sb.c);//ps是一个地址
printf("%p\n", ps);
//操作符->应用于结构体指针;操作符“."应用于结构体类型的变量(不是地址,而是值)
return 0;
}
4. 结构体传参
struct B
{
char c;
short s;
double d;
};
struct Stu
{
//成员变量
struct B sb;
char name[20];//名字
int age;//年龄
char id[20];
};
void print1(struct Stu t)
//传过来一个结构体struct Stu 类型的变量s由t来接收
{
printf("%c %d %lf %s %d %s\n", t.sb.c, t.sb.s, t.sb.d,
t.name, t.age, t.id);
//字符型用%c,整形用%d,double型用%lf,字符串用%s
}
void print2(struct Stu* ps)
//传过来一个结构体struct Stu 类型的变量s的地址由同类型的结构体指针变量ps来接收
{
printf("%c %d %lf %s %d %s\n", ps->sb.c, ps->sb.s, ps->sb.d,
ps->name, ps->age, ps->id);
}
int main()
{
//s是局部变量
struct Stu s = { {'w', 20, 3.14}, "张三", 30, "202005034" };//对象
//写一个函数打印s的内容
print1(s);//s是一个结构体类型
print2(&s);//&s是一个结构体类型的地址
return 0;
}
如上述代码,print1(s);是一个传值调用,print2(&s);是一个传址调用。更好的方式应该是传址调用,原因在于:传值调用需要额外开辟一个和s值空间大小一样的空间,系统开销比较大,会导致性能下降;而传址调用,仅需要占用4个字节或8个字节的标号。同时,指针传递过去,效率更高时间更快。此外,传址调用ps能够改变s的数据,功能上更强大。综上,在结构体传参时,优先选择用传址调用即指针的方式。
补充
每一个函数调用都会在内存的栈区上开辟一块空间,函数传参实际上是一个压栈的过程。栈是一种数据结构,先进的后出,后进的先出。
参考书籍:《函数栈帧的创建和销毁》