从0开始学c语言-22-结构体声明和初始化、结构体大小、结构体成员访问、结构体传参

发布于:2022-12-13 ⋅ 阅读:(728) ⋅ 点赞:(0)

CSDN话题挑战赛第2期
参赛话题:学习笔记

本人0基础开始学编程,我能学会的,你也一定可以,学会多少写多少。

下载安装请从官网入手,社区版本即可,这里主要使用的软件是VS2019,图标如下。

  上一篇:从0开始学c语言-21-指针和数组、二级指针、指针数组_阿秋的阿秋不是阿秋的博客-CSDN博客

目录

1·结构体声明

基础知识

定义

代码

结构成员的类型

定义和初始化

2·结构体成员的访问

结构体大小

偏移量和地址对齐

例子展示

现在进行结构体的访问。

3·结构体传参

结构体地址传参

结构体两种传参方式对比


1·结构体声明

基础知识

定义

        结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

代码

        代码就是这样子,这是声明结构体的时候,同时定义了名为variable-list的结构体变量,也是全局变量。

        注意:结构体的声明只是告诉编译器该如何表示数据,并没有向计算机申请分配空间。

        下面这段代码是声明的同时还定义了个结构体变量,所以是分配了内存空间的,如果去掉名为variable-list的结构体变量,那便只是结构体类型声明。

struct tag  //struct是关键字,tag是标签名
{
 member-list; //成员变量
}variable-list; //结构体变量,也是全局变量

比如用结构体描述一个学生

typedef struct Stu
{
 char name[20];//名字
 int age;//年龄
 char sex[5];//性别
 char id[20];//学号
}Stu; //分号不能丢

结构成员的类型

结构的成员可以是标量、数组、指针,甚至是其他结构体。
struct bike
{
	char name[20];
	int speed;
};
struct book
{
	//成员变量
	struct bike i;
	char name[20];
	int page;
	char id[10];
}s; //s全局变量,也是结构体变量

就像上面这段代码,我们给struct book结构体的成员变量中加了struct bike结构体。

定义和初始化

注意:定义结构体变量后才会为结构体分配内存空间

struct Point //声明类型
{
 int x;
 int y; 
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
初始化: 定义变量的同时赋初值。
struct Point p3 = {x, y};

声明、定义和初始化连起来就像这样

struct Stu        //类型声明
{
 char name[15];//名字
 int age;      //年龄
};
struct Stu s = {"zhangsan", 20};//定义并初始化

结构体嵌套初始化

就是说,结构体成员变量中有一个结构体,然后我们在初始化的时候,把结构体中的结构体也初始化。

struct Node
{
 int data;
 struct Point p;
 struct Node* next; 
}n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化

结构体类型

struct Stu        //类型声明
{
 char name[15];//名字
 int age;      //年龄
};
struct Stu s = {"zhangsan", 20};//定义并初始化

请问s的类型是?

答案:Stu

也就是说,结构体的类型是你自己定义的。

只要前面有struct修饰,Stu就是 后面 定义 的 结构体变量 的类型。

2·结构体成员的访问

结构体大小

最新的求法在这里从0开始学c语言-32-自定义类型:结构体,枚举,联合_阿秋的阿秋不是阿秋的博客-CSDN博客

struct bike
{
	char name[20];
	int speed;
};
struct book
{
	//成员变量
	struct bike i;
	char name[20];
	int page;
	char id[10];
}s; //s全局变量,也是结构体变量

你猜猜结构体变量s会有多大?

output:60

你是不是算出来58?

偏移量和地址对齐

偏移量是结构体变量中成员的地址结构体变量地址

结构体大小=最后一个成员的偏移量+最后一个成员的大小。

实际储存变量时候,会要求地址对齐,

1·结构体变量中,成员的偏移量必须是成员自身对齐量大小整数倍。(0是任何数的整数倍)

2.结构体大小必须是每个成员大小的整数倍,也就是所有成员大小的公倍数。

(对于嵌套结构体,这里的成员是指展开后的成员,也就是说 是 展开后结构体每个成员 的整数倍)

3·嵌套结构体中,要求 展开后的结构体 的 第一个成员的偏移量 = 展开后的结构体 最大成员的整数倍

4.第一个成员的偏移量为0,下一个成员的偏移量 = 上一个成员偏移量+上一个成员的大小

没有成员的结构体占用的空间是几个字节?

一个字节!(不解释,记住,要说的话,原因就是实例化)

例子展示

理论补充到这里,现在进行实战。

 因为是嵌套结构体,所以进行展开。

嵌套结构体中,要求 展开后的结构体 的 第一个成员的偏移量 = 展开后的结构体 最大成员的整数倍

 第一个成员是结构体,所以我们进行了展开。

并且可以看到name偏移量是0,成员大小是结构体最大成员的整数倍。

第一个成员的偏移量为0,下一个成员的偏移量 = 上一个成员偏移量+上一个成员的大小

speed的偏移量 = 上一个成员偏移量 0+上一个成员的大小 20

结构体变量中,成员的偏移量必须是成员自身对齐量大小整数倍。(0是任何数的整数倍)

遵循这条原则,我们所有数字都满足成员的偏移量必须是成员自身对齐量大小整数倍。

对齐量如何确定?

在vs中默认是8和成员自身大小(数组的大小就是数组存放的类型大小)当中较小的那个数字。

结构体大小=最后一个成员的偏移量+最后一个成员的大小。

那么结构体s 的大小=最后一个成员的偏移量 48+最后一个成员的大小 10 =58

结构体大小必须是每个成员大小的整数倍,也就是所有成员大小的公倍数。

(对于嵌套结构体,这里的成员是指展开后的成员,也就是说 是 展开后结构体每个成员 的整数倍)

58不是最大成员20的公倍数,所以地址对齐58+2=60.

现在进行结构体的访问。

结构变量的成员是通过点操作符(.)访问的。点操作符接受两个操作数。
也可以通过 ->来访问 ,不过前提是指针才能用这个操作符。
具体看代码。
struct bike
{
	char name[20];
	int speed;
};
struct book
{
	//成员变量
	struct bike i;
	char name[20];
	int page;
	char id[10];
}s; //s全局变量,也是结构体变量

int main()
{
	struct book qween = { {"study",90},"bird",90,"8787888"};
//访问结构体
	printf("%s\n", qween.i.name);
	printf("%d\n", (&qween)->page);
//创建指针
	struct book* p = &qween; //p=&qween
	printf("%s\n", (*p).i.name); //*p=qween
	printf("%s\n", p->i.name);  
//p是指针,i不是指针,所以p能用->
	return 0;
}

3·结构体传参

结构体地址传参

void print(struct Stu* ps) 
{
 printf("name = %s   age = %d\n", (*ps).name, (*ps).age);
    //使用 结构体指针 访问指向对象的成员
 printf("name = %s   age = %d\n", ps->name, ps->age);
}
int main()
{
    struct Stu s = {"zhangsan", 20};
    print(&s);//结构体地址传参
    return 0; 
}

结构体两种传参方式对比

struct bike
{
	char name[20];
	int speed;
};
struct book
{
	//成员变量
	struct bike i;
	char name[20];
	int page;
	char id[10];
}s; //s全局变量,也是结构体变量
void print1(qween)
{
 //只能用.访问
}
void print2(struct book*qween) 
{
 //可以用->访问
}
int main()
{
	struct book qween = { {"study",90},"bird",90,"8787888" };
//两种传参方式
	print1(qween);
	print2(&qween);
	return 0;
}

这两种传参方式哪个好呢?

传地址好,因为传值需要时间与空间。压栈操作,当所占内存过大时,可能造成栈溢出,但是传地址就不用临时开辟空间接收实参。

如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。

因此,结构体传参的时候,要传结构体的地址。

下一篇:从0开始学c语言-23-如何写出好(易于调试)的代码、模拟实现库函数:strcpy、strlen 、编程常见错误_阿秋的阿秋不是阿秋的博客-CSDN博客


网站公告

今日签到

点亮在社区的每一天
去签到