本笔记参考B站up鹏哥C语言的视频
目录结构体的声明
结构的基本知识
结构的声明
结构成员的类型
结构体变量的定义和初始化
结构体的初始化
例
各种结构体初始化的总结
结构体成员的访问
点 *** 作符( . )
->的使用
结构体传参
知识点 - 压栈
解释
例如
结构体的声明 结构的基本知识
||| 就如同数组是相同类型的元素的集合,结构也是一些值的集合,这些值称为成员变量。
结构的每个成员可以是不同类型的变量。
(方便描述复杂对象)
结构的声明struct tag
{
member-list;
}variable-list;
如:描述一个学生
struct Stu
{
//成员变量
char name[20];//描述名字
int age;//描述年龄
char id[20];//描述学号
}s1, s2;
int main()
{
struct Stu s;//对象
return 0;
}
ps:
- 当输入[struct stu 变量名;]时,就(用类型)创建了一个对象。
- s1和s2也是结构体变量
- s1和s2是全局变量,而s是局部变量
结构成员可以是标量、数组、指针,甚至是其它结构体。
如:
struct B
{
char c;
short s;
double d;
};
struct Stu
{
char name[20];
int age;
char id[20];
struct B a;
}s1, s2;
结构体变量的定义和初始化
结构体的初始化
例
引用前述例子:
int main()
{
struct Stu s = { {'w', 20,3.14},"名字",30,"20202020" };//对象
return 0;
}
这就是结构体的初始化。
(因为结构体内还有一个结构体,所以再使用一个{ })
各种结构体初始化的总结struct Point
{
int x;
int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
//初始化:定义变量的同时赋初值
struct Point p3 = { 2, 3 };
struct Stu
{
char name[15];
int age;
};
struct Stu s = { "名字",20 };//初始化
struct Node
{
int data;
struct Point p;
struct Node* next;//结构体指针
}n1 = { 10,{4,10},NULL };//结构体嵌套初始化
struct Node n2 = { 20,{5,6},NULL };//结构体嵌套初始化
结构体成员的访问
点 *** 作符( . )
结构体变量的成员是通过点 *** 作符( . )访问的。
点 *** 作符接受两个 *** 作数。
如:
struct B
{
char a;
int b;
};
struct Stu
{
struct B b1;
char name[20];
int age;
};
int main()
{
struct Stu s1 = { {'w',2},"name",10 };
printf("%c\n", s1.b1.a);
printf("%d\n", s1.age);
printf("%s\n", s1.name);
return 0;
}
->的使用
如果想要通过指针访问结构体成员,要怎么做?
解引用的方式
struct Stu* ps = &s1; printf("%c", (*ps).b1.a);
->的使用方式
printf("%c\n", ps->b1.a);
这种方法也可以访问到a,打印结果为:w
结构体传参引用[->的使用]中使用的结构体及其变量。
void print1(struct Stu t)
{
printf("%c %d %s %d", t.b1.a, t.b1.b, t.name, t.age);
}
void print2(struct Stu* ps)
{
printf("%c %d %s %d", ps->b1.a, ps->b1.b, ps->name, ps->age);
}
int main()
{
struct Stu s1 = { {'w',2},"name",10 };
//写一个函数打印s的内容
print1(s1);
printf("\n");
print2(&s1);
return 0;
}
[print1(s1);] 传值调用
[print2(&s1);] 传址调用
知识点 - 压栈[print2]的优点:
- 可以改变原变量s1 - 功能更丰富
- 初始传参时,参数是需要压栈的。
如果传递一个结构体对象时,结构体过大,参数压栈的开销比较大,会导致性能的下降。
(浪费时间和空间)
推荐了解:函数栈帧的创建和销毁
函数调用的参数压栈:
栈,是一种数据结构:先进的后出,后进的先出。
当输入数据时:
1. 当放入 1 这个数字时,1 是从顶部放入,放到底部向上的第一个位置上
顶部 | |
1 | 底部 |
2.当放入 2 这个数字时, 2 从顶部放入,放到底部向上的第一个位置上
顶部 | |
2 | |
1 | 底部 |
……
5 | 顶部 |
4 | |
3 | |
2 | |
1 | 底部 |
从顶部放入数据的这种 *** 作就叫做压栈。
当删除数据时:
底部的 1 够不到,要删除数据,得先删除 5
1.删除5
5 | 顶部 |
4 | |
3 | |
2 | |
1 | 底部 |
↓
顶部 | |
4 | |
3 | |
2 | |
1 | 底部 |
2.删除4
顶部 | |
4 | |
3 | |
2 | |
1 | 底部 |
↓
顶部 | |
3 | |
2 | |
1 | 底部 |
……
这种删除数据的 *** 作就是出栈。
Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 3;
int b = 5;
int c = Add(a, b);
return 0;
}
复习:每一个函数的调用,都会在内存的栈区上开辟一块空间。
内存分为三块空间
栈区 | |
堆区 | |
静态区 |
在这之中,栈区:
· 创建 main函数,main函数 分配一块空间。
main函数 |
· 分配给 main函数 后,main函数 再划分空间,如:
当执行前三句代码时,main函数内
c = 0 |
b = 5 |
a = 3 |
接下来Add函数即将执行,需要传参。
(大部分情况下[Add(a, b)]内部是从右向左传参的)
先传b
栈区内
b' = 5 |
main函数 |
再传 a
栈区内
a' = 3 |
b' = 5 |
main函数 |
函数的参数传参也称函数的压栈 *** 作
Add函数被调用,在栈区内为Add函数开辟一块空间
Add函数 |
a' = 3 |
b' = 5 |
main函数 |
在Add函数执行时:
Add函数内
z = 0 |
此时,访问 x 和 y ,其实是访问 a' 和 b'
a' = 3(就是x) |
b' = 5(就是y) |
当调用完Add函数时,空间被返回。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)