C语言学习之结构体

目录

一. 结构体的声明

1.结构的基础知识

2.结构的声明

 3.结构体成员的类型

二. 结构体初始化

三. 结构体成员访问

四. 结构体传参 


一. 结构体的声明

1.结构的基础知识

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

数组:一组相同类型元素的集合

结构体:其实是一组不一定相同类型元素的集合

生活中的描述:书:书名+作者+出版社+定价+书号.....

这些复杂的对象,不能通过内置类型直接描述,就有了结构体来描述这种复杂类型

2.结构的声明

//描述一个学生
//名字+年龄+性别

//声明结构体类型
struct Stu
{
	//成员变量,用来描述结构体对象的相关属性
	char name[20];
	int age;
	char sex[5];//男 女 保密\0
}s2,s3,s4;//s2,s3,s4就是结构体变量 - 全局变量

int main()
{
	struct Stu s1;//局部变量

	return 0;
}

类型相当于生活中的房子图纸,而变量相当于房子 

当然创建s2,s3和s4还可以这么写

typedef struct Stu
{
	char name[20];
	int age;
	char sex[5];
}Stu;//对这个结构体类型重新起了一个名字叫Stu


int main()
{
	struct Stu s1;//局部变量
	Stu s2;//用Stu来创建s2的时候跟s1的创建是一个道理
	//在C语言中,如果没有对结构体类型typedef,struct关键字不能省略
	return 0;
}

 3.结构体成员的类型

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

struct S
{
	int a;
	char arr[5];
	int* p;
};

struct B
{
	char ch[10];
	struct S s;
	double d;
};

二. 结构体初始化

struct S
{
	int a;
	char arr[5];
	int* p;
}s1;//第一种变量创建方式

struct S s2;//第二种变量创建方式

struct B
{
	char ch[10];
	struct S s;
	double d;
};

int main()
{
	struct S s3;//第三种变量创建方式
	return 0;
}
//第四种变量创建方式就是typedef

 初始化和数组一样,都是用大括号包围起来

struct S
{
	int a;
	char arr[5];
	int* p;
}s1 = {100, "bit", NULL};//要根据成员顺序来
struct S s2 = { .arr = "abc", .p = NULL, .a = 1 };//这样创建不用按照顺序,因为顺序你自己可以定

打印出来效果怎么样呢

三. 结构体成员访问

 如何用struct B 来创建变量呢?这个过程比较复杂,我直接上代码和结果吧

现在来定义一个Stu的结构体,并实现可以调整学生信息的set_stu()和打印学生信息的print()方法 

#include <string.h>//字符串操作需要加这一句

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

void set_stu(struct Stu t)
{
	t.age = 20;
	//t.name = "张三";//error 因为这里的“张三”是地址,这就不对了,我们应该把张三放到name申请的空间里面
	strcpy(t.name, "张三");//字符串拷贝
}

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

int main()
{
	struct Stu s = {0};
	set_stu(s);//设置s的值
	print_stu(s);
	return 0;
}

这代码看起来没毛病,但是打印一下

 欸为什么是0呢? 

因为这里set_stu(struct Stu t)的t是set_stu(s)里的s 的临时拷贝,在t里面设置age和name,设置的挺开心,回头一看因为临时拷贝,t的改变不影响s,所以print打印出来的啥也没有只有一个0。

所以我们要给set_stu里面传一个地址,然后print和set_stu函数里面传一个指针,根据地址找地址肯定是没错的

修改后的代码

struct Stu
{
	char name[20];
	int age;
};
//方式1
void set_stu(struct Stu* ps)
{
	(*ps).age = 20;
	//t.name = "张三";//err
	strcpy((*ps).name, "张三");//字符串拷贝
}
//方式2
//void set_stu(struct Stu* ps)
//{
//	ps->age = 20;//结构体指针->结构体成员
//	//t.name = "张三";//err
//	strcpy(ps->name, "张三");//字符串拷贝
//}


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

int main()
{
	struct Stu s = { 0 };
	set_stu(&s);
	print_stu(s);
	return 0;
}

打印结果

四. 结构体传参 

观察下面的代码

struct S
{
	int data[1000];
	int num;
};

struct S s = { {1,2,3,4},1000 };

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

int main()
{
	print1(s);//传结构体
	print2(&s);//传地址
	return 0;
}

 一般我们选择print2函数来进行打印,因为实参进行传值调用的时候,形参还要再拷贝一份

如果用print1函数,当实参结构体s特别大,再进行拷贝,又创建了一个特别大的空间,空间浪费严重。

我们知道,传参的过程,参数需要压栈,一旦参数的空间特别大的时候,压栈的开销会很大,既浪费空间也浪费时间。

而地址就不会发生这样的情况,地址的大小是4或8个字节,每次压栈的时候,系统的开销不是很大。

建议:传参的时候,建议传结构体的地址

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值