文章目录
- C语言基础7:结构体类型、声明、成员类型、定义、初始化、成员访问、传参
- 1. 结构体类型的声明
- 1.1 结构体的基础知识
- 1.2 结构体的声明
- 1.3 结构体成员的类型
- 1.4 结构体变量的定义和初始化
- 2. 结构体成员访问
- 4. 结构体传参
C语言基础7:结构体类型、声明、成员类型、定义、初始化、成员访问、传参
1. 结构体类型的声明
1.1 结构体的基础知识
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
就好比一个学生,有身高、体重、学号、性别、身份证编号等信息,学生就可以理解为(身高、体重、学号、性别、身份证编号)的集合,而(身高、体重、学号、性别、身份证编号)这些值可称为成员变量,这些成员变量的类型都不一样,有整数、浮点型等
1.2 结构体的声明
//struct:结构体关键字,tag:结构体标签,struct tag:结构体类型
struct tag
{
member-list;//成员列表
}variable-list;//变量列表
例如:描述一个学生:
struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}s1,s2,s3;//分号不能丢,s1,s2,s3是3个全局的结构体变量
int main()
{
struct Stu s;//局部变量
return 0;
}
//struct Stu 另一种定义方式
typedef struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}Stu;//分号不能丢,这里的Stu是类型
int main()
{
struct Stu student1;//局部变量
Stu student2;
return 0;
}
1.3 结构体成员的类型
结构的成员可以是标量(普通变量)、数组、指针,甚至是其他结构体。
1.4 结构体变量的定义和初始化
struct Point
{
int x;
int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
//初始化:定义变量的同时赋初值。
struct Point p3 = {x, y};
typedef struct Stu //类型声明
{
char name[15];//名字
int age; //年龄
};
struct Stu s1 = {"zhangsan", 20};//初始化
Stu s2 = {"张三", 21};//初始化
struct Node
{
int data;
struct Point p;
struct Node* next;
}n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化
struct S s;
strcpy(s.name, "zhangsan");//使用.访问name成员
s.age = 20;//使用.访问age成员
struct Stu
{
char name[20];
int age;
};
2. 结构体成员访问
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct S
{
int a;
char c;
char arr[20];
double d;
};
struct T
{
char ch[10];
struct S s;
char* pc;
};
int main()
{
char arr[] = "hello !\n";
struct T t = { "hehe",{1,'a',"hello world",3.1415926},arr };
printf("%s\n", t.ch);
printf("%s\n", t.s.arr);
printf("%lf\n", t.s.d);
printf("%s\n", t.pc);
return 0;
}
通过函数打印结构体数据:
方式一:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
typedef struct Stu
{
char name[20];
short age;
char telephone[12];
char sex[5];
};
void Print1(Stu tmp)
{
printf("name: %s\n", tmp.name);
printf("name: %d\n", tmp.age);
printf("name: %s\n", tmp.telephone);
printf("name: %s\n", tmp.sex);
}
int main()
{
Stu s = { "张三", 21,"18866669999", "男" };
Print1(s);
}
方式二:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
typedef struct Stu
{
char name[20];
short age;
char telephone[12];
char sex[5];
};
void Print2(struct Stu* tmp)
{
printf("name: %s\n", tmp->name);
printf("name: %d\n", tmp->age);
printf("name: %s\n", tmp->telephone);
printf("name: %s\n", tmp->sex);
}
int main()
{
Stu s = { "张三", 21,"18866669999", "男" };
Print2(&s);
}
上面两种方式较好的是第2种,原因:
对于第①种方式:当Stu s = { "张三", 21,"18866669999", "男" };
结构体创建时,假设会占用200个字节的空间。那么此时就占用了200个字节的空间,当函数在传参的时候,又会给 tmp 又会划出200个字节的空间,这样空间浪费就非常严重,同时,数据拷贝也非常消耗时间。
对于第②种方式:在传参的时候,传的是一个地址过去,也就是占用4或8个字节。之后通过指针找回相关数据,然后进行相关数据的打印。
总结:函数传参的时候,参数是需要压栈的。如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。
结构体传参的时候,要传结构体的地址。
4. 结构体传参
压栈:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 20;
int ret = 0;
ret = Add(a, b);
return 0;
}
上面这个函数在我们内存空间是如何完成的呢,首先我们知道,任何一次函数调用,都会在内存当中的栈区上分配一块空间:
(1)栈区:①局部变量②函数的形式参数③函数调用也开辟内存空间
(2)堆区:①动态内存分配,涉及函数:malloc、free、realloc、calloc
(3)静态区:①全局变量②静态变量
上面函数运行的流程:
(1)进入main函数,此时内存中的栈区会给main函数开辟一块内存单元
(2)之后创建变量:a、b、ret,同时会在分配给main函数的内存单元中再开辟3块儿内存单元区域,分别给a、b、ret。
(3)接下来调用Add函数,当调用Add函数的时候,首先需要进行传参,需要将 a 和 b 这两个参数传过去一个赋给x,一个赋给y,接下来就进行传参:
①先将b进行传参,同时在分配给main函数的内存单元外的栈区分配一块儿空间,这块儿空间叫 y ,
②a同理,再将a进行传参,再划一块儿内存空间给a,这块儿空间叫 x
(4)接下来进入Add函数,同样的,需要在栈区分配一块儿空间给Add函数,
(5)创建变量 z ,同样是在 Add函数中的内存空间内进行开辟。
(6)将x和y进行相加运算,赋值给z。
这个过程把 a 和 b 放进去了,这个动作叫做压栈操作,