C语言_a04_结构体、共用体/联合体、动态内存分配

结构体、共用体/联合体、动态内存分配

一、结构体
1. 定义及使用

​ 自定义的数据类型,一批数据组合成的结构型数据,每一个数据就是结构体的”成员“。

struct
{
	char name[100];
	int age;
    char gender;
	double height;
}

书写在函数内部((局部位置)只可在本函数使用;书写在函数外部(全局位置)可以全局使用。

结构体使用:

//此处为每个属性单个赋值的写法,下个代码示例有更简便的写法
struct student stu;
strcpy(stu.name, "张三");	
stu.age = 18;
stu.gender = 'M';
stu.height = 1.83;

printf("%s年龄%d,性别%c,身高%.2f\n", stu.name, stu.age, stu.gender, stu.height);
//输出结果为:张三年龄18,性别M,身高1.83
2. 结构体数组

​ 例:定义结构体表示学生,将三个学生信息放入数组并遍历

struct student
{
	char name[100];
	int age;
	char gender;
	double height;
};

int main()
{
	struct student stu1 = {"张三", 18, 'M', 1.83};
	struct student stu2 = { "李四", 20, 'M', 1.75 };
	struct student stu3 = { "王五", 22, 'F', 1.70 };
    
	struct student stuArr[3] = {stu1, stu2, stu3};

	for (int i = 0; i < 3; i++)
{
	struct student temp = stuArr[i];
	printf("%s年龄%d,性别%c,身高%.2f\n", temp.name, temp.age, temp.gender, temp.height);
}

	return 0;
}
3. 别名

​ 使用别名可以方便我们定义结构体,体现在两个方面:

  • 简化”数据类型“名字的书写;
  • 定义时不需要再加上struct;

代码示例:

#include <stdio.h>

typedef struct heroCharacter	//此处有完整的名称,并且前面加上typedef
{
	char name[100];
	int powerNum;
	int defenseNum;
	int blood;
}man;	//别名

int main()
{
	man man1 = { "莉莉娅", 150, 200, 600 };
	man man2 = { "凯隐", 160, 200, 600 };
	man man3 = { "弗耶戈", 160, 200, 600 };

	man manArr[3] = {man1, man2, man3};

	for (int i = 0; i < 3; i++)
	{
		man temp = manArr[i];
		printf("%s的攻击力为%d,防御力为%d,血量为%d\n", temp.name, temp.powerNum, temp.defenseNum, temp.blood);
	}

	return 0;
}
4. 结构体作为函数的参数

​ 两种情况:传递结构体中的数据值;传递结构体的地址值。

​ 函数的声明建议写在结构体定义的下面,因为程序自上而下运行,如果函数的参数中存在结构体,而函数声明写在结构体上面,就会报错。

(我已经报错过了,报错后才发现问题,哭)

​ 例:书写一个修改结构体变量的数据的函数

typedef struct student
{
	char name[100];
	int age;
	char gender;
	double height;
}S;

void updateStu(S* p);


int main()
{
	struct student stu1 = {"张三", 18, 'm', 1.83};
	
	updateStu(&stu1);
	printf("%s年龄%d,性别%c,身高%.2f\n", stu1.name, stu1.age, stu1.gender, stu1.height);

	return 0;
}

void updateStu(S* p)
{
	printf("请输入新的姓名:");
	scanf("%s", (*p).name);

	printf("请输入新的年龄:");
	scanf("%d", &((*p).age));
} 
5. 结构体嵌套

​ 例.为上述的结构体添加联系方式,联系方式包括手机号、电子邮箱

typedef struct contactWay
{
	char phoneNum[12];
	char e_mail[100];
}Contact;

typedef struct Student
{
	char name[100];
	int age;
	char gender;
	double heihght;
	Contact contact;
}Stu;
// 注意给字符串赋值要使用strcpy()函数
// 也可以整体赋值,如下:
	Stu stu1 = { "李四", 22, 'M', 1.73, {"13412345678", } };
6. 内存对齐

​ 规则:不管是结构体还是普通的变量都存在内存对齐。

在这里插入图片描述

  • 确定变量位置:只能放在自己类型整数倍的内存地址上

    如int类型,方便理解使用十进制数进行解释,int类型占4个字节,内存地址为0、4、8等可以存储,内存地址为2、3、5等就不可以。

    即“内存地址 / 占用字节,可以整除”。

    对齐的时候会补空白字节,但不会改变原本占用字节的大小,如char补位之后仍是占用一个字节。

​ **结构体在此基础上多一条控制总大小的补位:

  • 最后一个补位结构体的总大小,是最大类型的整数倍(用于确定最后一个数据补位的情况)。

    比如一个结构体中有:int、double、char等数据类型,并且已经占了17个字节的位置,但因为double占用8个字节,为保持整数倍,这个结构体会共占用24个字节。

在这里插入图片描述

​ 因此在定义结构体的时候一般会将小的数据类型放在上面,大的数据类型放在下面,以此节约空间。

二、共用体
1. 核心

​ 核心:一个数据可能有多种数据类型。

​ 比如金额有整数、小数、汉字形式书写,分别需要使用整型、浮点型、字符串等数据类型。

代码示例:

union moneyType				//定义共同体
{
	double moneyD;
	int moneyI;
	char moneyC[100];
};

int main()
{
	union moneyType money;	//定义共同体变量
	money.moneyD = 100.2;   //赋值

	printf("%s\n", money.moneyD);
	return 0;
}
2. 起别名

​ 同样的,共同体也可以起别名。

typedef union moneyType		//定义共同体
{
	double moneyD;
	int moneyI;
	char moneyC[100];
}MT;

int main()
{
	MT money;				//定义共同体变量
	money.moneyD = 100.2;   //赋值

	printf("%s\n", money.moneyD);
	return 0;
}
3. 特点
  • 共用体,也叫联合体、共同体;

  • 所有的变量使用同一个内存空间

在这里插入图片描述

如图所示:地址相同。

  • 所占用的内存大小 = 最大成员的长度(受内存对齐影响) && 最大数据类型的整数倍;

    1. 比如一个共用体中为double和int两种成员,那么占用的就是8个字节。

    2. 但同时也要注意 总大小一定是最大数据类型长度的整数倍。

    思考:占用内存的长度本来就是最大成员的长度了,那一定是整数倍啊,为什么会要注意这个呢?

    下面这个结构体占用多少空间呢?

    union MoneyType
    {
    	int moneyI;
    	double moneyD;
    	char moneyC[100]
    };
    

    占用104个,字符串底层是字符数组,一个字符占用一个字节,最大成员moneyC的长度为100,但最大的数据类型为double,8个字节的长度,所以占用空间应该是8的整数倍,所以会填补4个空白字节,共占用104个。

  • 每次只能给一个变量赋值,因为第二次赋值会覆盖原有的数据;

    如图蓝色部分所示:

在这里插入图片描述

三、结构体和共用体的区别
  1. 在底层:

    结构体:一种事物中包含多个属性(一个人的信息,姓名、年龄、身高、体重等);

    共用体:一个属性有多种类型(金额为整数、小数、字符串等);

  2. 存储方式:

    结构体:每个属性各存各的,占用不同的内存地址;

    共用体:存在一起,多次存会覆盖;

  3. 内存占用:

    结构体:各个变量的总和(内存对齐的影响);

    共用体:最大类型(内存对齐的影响);

    大概可以想结构体串联,共用体并联。

四、动态内存分配
1. 常用函数

​ 这些函数定义在了stdlib.h头文件中,所以要先导入该头文件。

在这里插入图片描述

1.1 malloc – 申请空间(掌握)

该函数使用次数最多,要掌握,效率会更高。

​ 申请连续的空间,函数参数为总长度,单位为字节,在这里插入图片描述

​ 示例代码如下:

	int* p = malloc(100 * sizeof(int));		

	for (int  i = 0; i < 10; i++)
	{
		*(p + i) = (i + 1) * 10;	//赋值方式一
        p[i] = (i + 1) * 10;	    //赋值方式二
	}

	for (int i = 0; i < 10; i++)
	{
		printf("%d  ", *(p + i));	//取值方式一
        printf("%d  ", p[i]);		//取值方式二
	}
1.2 calloc – 申请空间(了解)

​ 该函数使用次数较少,作为了解,因为初始化的数据一般用不到。

​ 申请空间并进行初始化,函数参数为元素个数、单个元素的大小。

在这里插入图片描述

	int* p = Calloc(10, sizeof(int));		

	for (int i = 0; i < 10; i++)
	{
		printf("%d  ", *(p + i));	//取值方式一
        printf("%d  ", p[i]);		//取值方式二
	}
1.3 realloc – 扩容(了解)

​ 修改空间的大小,函数参数为被修改空间的首地址及要修改的大小,单位为字节。

在这里插入图片描述

​ 作为了解,因为内存空间一旦申请完毕,一般不会再去变动。

  • ​ 修改后地址值可能发生变化,也可能不发生变化(取决于原空间后面有没有空间),所以最好是用一个变量进行接收。

  • ​ 原本空间不需要进行释放,底层会进行处理。

int* p = malloc(10 * sizeof(int));

printf("%p\n", p);

for (int  i = 0; i < 10; i++)
{
	*(p + i) = (i + 1) * 10;
}

int *pp = realloc(p, 20 * sizeof(int)); //也可以改小

printf("%p\n", pp);		

for (int i = 0; i < 20; i++)
{
	printf("%d  ", *(p + i)); 
}
1.4 free – 释放空间(掌握)

​ 如果申请的空间不再进行使用,一定要记得进行释放,如果不做释放,占用的空间会越来越多。函数参数为指针。

在这里插入图片描述

free(p);
2. malloc函数的细节

​ 细节很多,但是我们不需要背下来,只要在代码运行结果与自己预期不一样时候知道怎么去改就可以了。

在这里插入图片描述

注释

  • malloc返回的是void类型具有通用性,可以转换为其他类型;
  • int*记录的是步长,p记录的是首地址,所以sizeof§得不到内存空间的大小,需要定义一个变量进行记录,且最好记录的是数据的个数而不是占用内存空间的大小;
  • 内存泄漏:不断地申请空间总有一天会满,这种情况就叫做内存泄露,所以要养成释放内存的好习惯hh;
  • 虚拟内存:每一个内存空间不会在刚申请时就立马使用,当开始存储数据时才会真正的分配空间(有效提高了内存的使用效率);
3. C语言的内存结构

在这里插入图片描述

​ 我们已经学习过了数组,为什么还要使用malloc等函数来申请空间、分配内存呢?

  1. 数组是整体存储在栈中,使用malloc等函数申请的空间是在堆当中,而堆的空间要比栈的空间大很多很多;
  2. 当函数运行结束,里面的数组也会随之消失,如果用malloc等函数,它占用的空间是在堆里面,只要不free(),就会一直存在直至程序结束。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值