C语言 底层逻辑详细阐述结构体 #结构体的声明 #结构体的初始化 #结构体成员访问 #结构体传参

前言

基于自我理解的角度来讲结构体;


一、结构体的基础知识

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

注:数组是相同元素的集合;而结构体中可以放不同类型的数据,例如想要做一个表达一个复杂对象的数据;比如说是描述一个人,就得说这个人的姓名、年龄,性别、身高、电话等,想要准确存储这些信息就得使用不同的变量,把这些不同类型的变量整合在另个一变量中,便就是结构体变量;

二、结构体的初始化

当我们想要初始化一个整型变量,int a = 4; --> 需要变量的类型+变量名+ 初始化的值;

所以首先我们要声明其类型:

1、结构体类型声明:

下图是结构体类型声明的模板:

其中 struct tag 是结构体的类型,stract 不可以更改,tag 可以更改;number-list  就是成员列表; variable-list 是变量列表,在声明的过程中可要可不要; 列表就是不止一个的意思;

方式一:声明时省略variable-list(通常是这样的方式) :

这里就仅仅声明了一个结构体类型

方式二:声明时不省略variable-list :

利用结构体类型创建了变量p1、p2 , 此处的结构体类型用来描述一个人的信息,那么p1、p2 也可以理解为被描述的两个人;

其中p1、p2 为全局的结构体变量(一般不推荐用全局变量),这也就是不常用此方法的原因。

注:结构体类型的声明通常是在main函数外进行的;当然也可以在主函数内部进行声明,只不过此时的结构体类型就为局部变量,只能在main 函数的内部使用;此处并不是全局变量与局部变量,请注意。

重点分析:在对结构体类型进行声明的时候,并不会向内存申请空间;当只有创建一个变量时才会向内存申请空间;可以将结构体类型当作建房子的图纸,图纸并会占据你要建房地基的空间,只有当你真正意义上开始建房子(创建变量)的时候,才会占据空间;同理。

2、结构体成员的类型

结构体成员可以是标量、数组、指针,甚至是其他结构体

代码如下:

#include<stdio.h>

struct Peo
{
	char name[20];
	int age;
	char sex[10];
	int high;
	char tel[10];
	struct home s;
}p5; 

struct home
{
	char name[20];
	int age;
	char sex[10];
};

struct Peo p2,p3,p4;  //结构体变量的创建


int main()
{
	struct Peo p1; //结构体变量的创建

	return 0;
}

其中p1为局部变量,p2、p3、p4为全局变量,同时p5 也是全局变量;结构体成员的类型可以是标量、数组、指针、结构体;

3、结构体变量的初始化:

在结构体变量创建时再给它一个初始值就是结构体变量的初始化。

注:在创建结构体变量赋值时,至少为一个结构体成员赋值;故而分为完全初始化与不完全初始化;

代码如下:

#include<stdio.h>
struct Peo
{
	char name[20];
	int age;
	char sex[10];
	int high;
	char tel[20];
}p1,p2;

int main()
{
	struct Peo p1 = { "zhangsan",19,"保密",180,"123456789" };

	return 0;
}

而若是对结构体中成员是结构体的这种情况是该如何初始化呢?

代码如下:

#include<stdio.h>

struct home
{
	char name[20];
	int age;
	char sex[10];
};

struct Peo
{
	char name[20];
	int age;
	char sex[10];
	int high;
	char tel[10];
	struct home s;
};


int main()
{
	struct Peo p1 = { "zhangsan",19,"保密",185,"1234567",{"lisi",11,"女"} };
	return 0;
}

如上,按照所创建变量的类型,按照类型声明时的顺序进行初始化就行,成员之间的数据用 ' , ' 隔开,如果遇到结构体的成员中有结构体,就将另一结构体的数据放入 { } 中,结构体的成员中的结构体的成员之间的数据也是用 ' , ' 隔开。

同理,在数组中若初始化时的数据个数小于数组元素的个数,那么就存在未初始化的数组元素,这就是未完全初始化;请看一下代码:

#include<stdio.h>

struct home
{
	char name[20];
	int age;
	char sex[10];
};

struct Peo
{
	char name[20];
	int age;
	char sex[10];
	int high;
	char tel[10];
	struct home s;
};

int main()
{
	struct Peo p1 = { "zhangsan",19,"保密",185,"1234567",{"lisi"} };
	return 0;
}

代码调试结果如下:

分析:可以发现,我们此处未对 struct home s 中后两个成员进行赋值;在数组中,初始化时未被赋值的元素会默认为 ’\0‘ (因 '\0' 的ASCII码值为0,故而可以将 '\0' 就当作0);对结构体变量进行初始化本质上是对结构体中的成员进行初始化,而其成员就是我们平时接触的内置类型、数组(无非其成员可能会是结构体变量,本质上就是嵌套结构体,一层一层进行赋值即可),若是结构体变量未初始化相对应的内置类型表现为随机值,而数组就是 ’\0‘;

三、结构体成员访问 

会用到两个操作符 . 点操作符  -> 箭头操作符

二者访问结构体变量成员的运用:

. 点操作符: 结构体变量 . 成员名  

-> 箭头操作符 : 结构体变量指针 -> 成员名

注:结构体变量 = * 结构体变量指针,故而存在 ( * 结构体变量指针 ). 成员名

以下代码将展示这三种结构体成员的访问:

代码如下:

#include<stdio.h>

struct home
{
	char name[20];
	int age;
	char sex[10];
};

struct Peo
{
	char name[20];
	int age;
	char sex[10];
	int high;
	char tel[10];
	struct home s;
};

void print(struct Peo* s)
{
	printf("%s %d %s %d %s %s %d %s\n", (*s).name, (*s).age, (*s).sex, (*s).high, (*s).tel, (*s).name, (*s).age, (*s).sex);
	printf("%s %d %s %d %s %s %d %s\n", s->name, s->age, s->sex, s->high, s->tel, s->name, s->age, s->sex);
}

int main()
{
	struct Peo p = { "zhangsan",19,"保密",185,"1234567",{"lisi",11,"保密"}};
	printf("%s %d %s %d %s %s %d %s\n", p.name, p.age, p.sex, p.high, p.tel, p.name, p.age, p.sex);
	print(&p);
	return 0;
}

代码运行结果如下:

四、结构体传参

在学习函数的时候,我们知道有传值传参和传址传参

传值传参,形参是对实参的一份临时拷贝;传址传参就是将其变量的地址传过去,即对存在内存中的数据进行操作;结构体变量也存在传值传参和传址传参,且道理与函数传参相同;

我们来看一下这两种传参方式的例子:

代码如下:

#include<stdio.h>

struct Peo
{
	char name[10];
};

void print1(struct Peo ps)
{
	printf("%s\n", ps.name);
}
void print2(struct Peo* s)
{
	printf("%s\n", s->name);
}

int main()
{
	struct Peo p = { "zhangsan" };
	print1(p);//传值传参
	print2(&p); //传址传参
	return 0;
}

代码运行结果如下::

思考:若是讨论传参用来打印的话,传址传参比传值传参更好,因为如果结构体对象很大即其所占的空间很大,那么传值传参就会浪费空间来进行拷贝,并且在拷贝的时候也会废时,这就会导致其性能降低;

当实参进行传值传参时,形参是对实参的一份临时拷贝,此时形参会在栈帧(即为本次 printf() 所开辟于函数栈帧中找的空间) 中存放 ;若结构体在内存空间中所占的空间很大,栈帧就会因形参的拷贝而增大,于是乎就浪费了很多空间;

专业描述原因:

1、函数传参时,参数需要压栈,会有时间和空间上的开销;

2、如果传递的一个结构体变量,其在空间中所占的内存很大时,参数压栈的系统开销比较大,所以会导致性能的下降;

所以:结构体传参时,最好传其地址


总结

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

2、结构体类型的声明通常是在main函数外进行的;当然也可以在主函数内部进行声明,只不过此时的结构体类型就为局部变量,只能在main 函数的内部使用;此处并不是全局变量与局部变量,请注意。

3、在结构体变量创建时再给它一个初始值就是结构体变量的初始化;在创建结构体变量赋值时,至少为一个结构体成员赋值;故而分为完全初始化与不完全初始化;

4、. 点操作符: 结构体变量 . 成员名  

-> 箭头操作符 : 结构体变量指针 -> 成员名

注:结构体变量 = * 结构体变量指针,故而存在 ( * 结构体变量指针 ). 成员名

5、函数传参时,参数需要压栈,会有时间和空间上的开销;如果传递的一个结构体变量,其在空间中所占的内存很大时,参数压栈的系统开销比较大,所以会导致性能的下降;

所以:结构体传参时,最好传其地址;

  • 29
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值