C语言的底层逻辑剖析(结构体篇)结构体声明,结构成员的类型,结构体变量的定义和初始化,结构体成员的访问,结构体传参

结构体声明

结构体的概念

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

为什么要引入结构体呢?实际上是因为在生活中我们有许多复杂的事物,难以用一个int或者float等等一个简单的类型来描述清楚,例如一个学生,他应该有姓名,性别,身高,等等诸多因素,再比如一本书,有书名,作者,出版社,等等。所以在C语言中我们提供了自定义类型,有枚举,结构体,联合体等等,>我们今天只简单学一下结构体,其他的自定义类型会放到进阶里来学习。

结构的声明

假设我们要来描述一个学生,包括有姓名,性别,年龄,考试成绩这四个考虑因素,我们应该怎么来定义呢?

struct Stu
{
    //结构体成员列表
	char name[20];
	char sex[10];
	int age;
	float score;
};//注意这里是有分号的

这样我们就定义了一个结构体类型,我们要描述一个学生,起的名称叫做Stu。

结构成员的类型

结构成员的类型是没有什么要求的,标量,数组,指针,其他结构体也可以(又开始套娃了对吧)。这一般提到C/C++就是都不得不提到C/C++的灵活性,这种灵活性是体现在方方面面的,所以要把C学精用好还是很有难度的。

结构体变量的定义和初始化

这里就是我们的重点了,我们创建了一个类型要先去定义变量然后初始化,最后去使用这个变量对吧,这是我们最终的目的。那么结构体变量如何创建呢?

这里其实有两种方式,我们来看一段代码:

struct Stu
{
	char name;
	char sex;
	int age;
	float score;
}s4,s5;//这里两种方式其实是等价的,
struct Stu s6;//定义的都是全局的结构体变量

int main()
{
	struct Stu s1, s2, s3;//此处就是定义的局部的结构体变量了
	return 0;
}

那么接下来就是我们的初始化了,怎么进行初始化呢?

struct Stu
{
	char name[20];
	char sex[10];
	int age;
	float score;
}s4 = { "如花","female",20,60.0 };

int main()
{
	struct Stu s = { "zhangsan","male",18,95.5};
	struct Stu s4 = { "如花","female",20,60.0 };
    printf("%s %s %d %.1f\n", s.name, s.sex, s.age, s.score);
	return 0;
}

还有一点值得一提的是,结构体里面是可以放结构体的,例如下面代码示例:

struct S
{
	int a;
	char b;
};
struct Stu
{
	char name[20];
	struct S;//结构体成员可以是结构体
	int age;
};

int main()
{
    //初始化也用大括号嵌套
	struct Stu p = { "旺财",{28,'c'},18 };
	printf("%s %d %c %d\n", p.name, p.a, p.b, p.age);
	return 0;
}

看到这里,我想你也应该有点感觉了,结构体就是自己定义的一种类型,其实也不是什么高深的东西对不对。

结构体成员的访问

第一种访问方式

其实结构体成员的访问上面的代码已经示例了,就是通过’.'一个点这个操作符来访问的,它的操作数是两个,例如上面的使用:

printf("%s %s %d %.1f\n", s.name, s.sex, s.age, s.score);

printf("%s %d %c %d\n", p.name, p.a, p.b, p.age);

当我们使用 . 来访问的时候其实是有提示的,如图:

image-20221122204210746

我们只需要选择我们想要使用的成员即可,需要注意的是,要与前面的打印格式一一对应。这个是个小细节,否则报错你可能看不懂找不到错误。

当然我们还可以用函数的方式来打印:

struct Stu
{
	char name;
	int age;
};

void Print(struct Stu p1)
{
	printf("%s %d\n", p1.name, p1.age);
}

int main()
{
	struct Stu p = { "如花",18 };
	Print(p);//传值调用
	return 0;
}

第二种访问方式

除了上面的访问方式呢,还有另外一种,只能用于结构体指针,是通过->一个箭头来访问,代码示例如下:

#include<stdio.h>

struct S
{
	int a;
	char b[20];
};
struct Stu
{
	char name[20];
	struct S;
	int age;
};
void Print(struct Stu* sp)
{
    printf("%s %d %d %s\n", (*sp).b,(*sp).a,(*sp).age,(*sp.).name);//比较啰嗦的写法
	printf("%s %d %d %s\n", sp->b, sp->a, sp->age, sp->name);//使用箭头直接指向对象
}
int main()
{
	struct Stu p = { "旺财",{28,'c'},18 };
	Print(&p);
	return 0;
}

总结结构体成员两种访问格式,1.结构体变量.成员,2.结构体指针->成员。

结构体传参

其实上面我们连带着把结构体传参也已经涉及到了,但是我们这里再来总结一下:

#include<stdio.h>
struct s
{
	float score;
};

struct Stu
{
	char name[20];
	int age;
	struct s;
};

void Print1(struct Stu p1)
{
	printf("%s %d %.1f\n", p1.name, p1.age, p1.score);
}

void Print2(struct Stu* p2)
{
	printf("%s %d %.1f\n", p2->name, p2->age, p2->score);
}

int main()
{
	struct Stu p = { "李华",18,{90.5} };
	Print1(p);//传值调用
	Print2(&p);//传址调用

	return 0;

}

你觉得上面Print1和Print2哪一种传参方式好一点,传值和传址两种方式,其实这道题看似两种方法都可以,但实际上是有标准答案的,一定是第二种方式,传址调用,为什么呢?我们就要回到函数栈帧那块的内容。

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

我们用通俗的解释一下就是,我们都知道,函数的形参是一份临时拷贝,当我们传的参数过于多或者大的时候,是不是要浪费很多空间啊,而如果是传一个首元素地址过去,是不是就没什么压力,地址大小一定是4或8个字节嘛。

结论:函数传参的时候,要传地址来使用。

好啦,关于结构体这块内容就到这里了,只要理解了结构体是个什么东西,会使用即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

迟钝南瓜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值