【C语言】内存,数组与结构体

C语言内存,数组,结构体

1. 程序运行为什么需要内存

  • 程序在运行的过程中,会产生很多的数据,内存就是用来存放这些产生的数据

2. 内存的简单模型(逻辑)

  • 内存在逻辑上可以类比为一栋大楼
  • 整栋大楼的每一层都一模一样
  • 每一层之间有许多房间,房间之间也都一模一样

3. 内存自身拥有的两种属性

3.1 内存空间

  • 内存上面存在很多小的格子,每个小格子类似一个房间
  • 每个小格子大小为一个字节,通过字节进行编码。

3.2 内存的地址

  • 内存上面的每一个小格子,都有一个对应的地址编号,类似于房间的门牌号,每个地址空间都与一个对应的地址绑定
  • 每个地址都是独一无二的
  • 每个单独相邻的小格子之间的地址是连续的
    在这里插入图片描述

4. 语言如何操作内存

4.1 从一个简单的变量定义开始

char a;/*编译器帮助我们在内存大楼中申请了一个房间,并且把房间的门牌号(内存的地址)和变量名 a 绑定在一起*/

a = 10;/*因为 a 和 内存的地址绑定了,所以编译器通过 a 可以找到申请的房间,找到了之后就把 10 扔进这个房间*/

a += 4;//编译器把 a 对应的房间中的数字拿出来,送到 CPU 中和 4 相加之后,再把运算结果送回 a 对应的房间

4.2 数据类型的本质

  • 数据类型最本质的含义就是在使用内存时,判断需要用几个字节和什么样的翻译方法去翻译对应内存中的内容
4.2.1 使用多少个字节的问题
char a = 10;//编译器申请了一个字节的内存,然后把 10 放进去

int b = 10;//编译器申请了四个字节的内存,然后把 10 放进去

在以上的过程中,假设我们拥有一个内存的地址(0x1),如果我们给这个字节一个类型

  • 这个内存地址本身只有一个字节,如果我们给他这个内存一个整数类型,那么,编译器会把 0x1,0x2,0x3,0x4
    这四块内存空间当作一个整体,然后进行操作
  • 如果给这个内存单元一个字符型,那么编译器就会把0x1这块内存空间当作一个整体
4.2.2 翻译方法的问题
int x = 10;

printf("%d\n",x);//把 x 对应的内存空间中的内容以整型的方式翻译

printf("%f\n",x);//把 x 对应的内存空间中的内容以float型的方式翻译
  • 可以肯定的是,把整型的数据当作 float 型翻译肯定是不可以的,因为编码方式本身就不一样
    使用整型的翻译方法翻译浮点型肯定不行
  • 但是 x 对应的这块内存单元里面的数据是不变的,就像语文老师常常说的“一切景语皆情语”一样
    同样的事物,不同的心境看到,想到的却不一样,但是事物的确是一样的,不变的.

4.3 函数名的本质

  • 同样,在C语言中,函数名类似于变量名,编译器会寻找一块内存来存放整个函数的语句
    而函数名就是这块地址的首地址,在调用函数的过程中,我们只需要找到被调用函数的首
    地址,那么程序就会继续顺理成章的往下执行.

5. 数组和结构体

5.1 数据结构的意义

数据结构就是用来研究和组织数据在内存中的存放方式和处理方式。

5.2 最简单的数据结构-数组

为什么要有数组?

在程序中拥有很多个相同类型的变量需要进行管理的时候:

//比如我们需要五个整形数据需要管理,按照一般的方式,需要申明五个整形的变量。
int a1, a2, a3 ,a4, a5;
//数据量比较少的时候这样做也行,当我们需要一万个,十万个……数据的时候,这种写法就很令人窒息了,所以就有了数组
int b[10000];//申请了一万个整形的变量

当我们定义了一个数组的时候,编译器就会在内存中划分出一块连续的空间,用于存放我们申请的数组,比如int a[4];我们申请了一块连续的内存空间用来存放这个数组。
在这里插入图片描述
数组的优势:数组的结构简单,并且可以随机访问,通过下标可以只访问任意一个成员

数据的限制:
(1)数组中所有的元素的类型必须相同;
(2)数组的大小是定义的时候给出来的,而且确定了以后就不可以更改了,设计的灵活性较差;
(3)删除元素麻烦,因为数组在内存中是连续的一片空间,如果需要删除其中的某个数据的时候,只能让后面的数据覆盖前面的数据。
在这里插入图片描述

5.3 数组的升级版-结构体

结构体发明出来就是为了解决数组所有元素必须相同的缺陷;

示例1:管理三个学生的年龄

//使用数组的写法
int student_age[3];
//查看第二个学生的年龄
student_age[1];
//使用结构的写法
struct Students
{
    int student1_age;
    int student2_age;
    int studnet3_age;
};
struct Students students;
//查看第二个学生的年龄
students.student2_age;

使用两种方法都可以达到我们的目的,但是使用数组更加的方便,并且我们需要管理一万个学生的年龄的时候,使用结构体还是会出现定义一万次的缺点……;

示例2:管理三个学生的基本信息,包括年龄,语文的分数,数学的分数,英语的分数,总分,平均分等……

//使用数组的话,需要定义 6 个数组,每个学生对应所有数组的同一个下标
int students_age[3];
double students_chinese[3];
double students_math[3];
double students_english[3];
double students_sum[3];
double students_ave[3];
//获取第一个学生的信息
students_age[0]
········
    
//使用结构体的话
struct students
{
    int student_age;
    double student_chinese;
    double student_math;
    double student_english;
    double student_sum;
    double student_ave;
}
struct students st1;
struct students st2;
struct students st3;

从逻辑上来和操作上来说,结构体要比数组简单得多,比如这个时候,第二个学生因为成绩太差被退学了,需要删除第二个学生的所有信息,那是不是操作所有的数组删除一次就让人感到窒息……

5.4 数组使用下标访问原理

5.4.1 数组下标为什么从0 开始

数组从下标从 0 开始的主要意义为地址偏移量的确定的简洁性

在这里插入图片描述

比如我们需要访问数组中第三个元素,数组首地址为1000,i = 2, 数据类型的大小为 4,通过简单的计算,可以得到第三个数据的地址计算结果:addr = 1000 + 2 * 4 = 1008,可以看到刚好是图中第三个元素的首地址,而需要访问第一个元素的时候,计算结果为:addr = 1000 + 0 * 4 = 1000

如果数组下标是从 1 开始的话,按照上面的公式的思路,定位地址的公式就要变成 第 i 个元素的地址 = 首地址 + (i - 1) * 数据类型的大小,那么很明显的就能看出来,数组下标从 0 开始更加的简洁优雅,并且不需要多余的计算,效率比数组下标从 1 开始更好。

5.4.2 数组为什么能用下标访问

因为数组中,所有元素的类型都是一样的,数据类型的大小一样,所以按照地址定位公式,通过下标和首地址的计算,就能很轻松的访问到数组中的元素。

5.5 结构体的访问方式

  1. 因为结构可以用来存放不同的数据类型,例如:
struct test
{
    char t1;
    int t2;
    double t3;
    short t4;
}

我们知道第一个元素的地址,怎么算出第二个元素,第三个元素的地址呢?

答案是不可能算的出来的。所以结构体只能使用 ‘.’ 运算符进行访问,例:test.t1,test.t4……

结构体的字节对齐:

为了满足硬件上的效率,结构体的大小不是所有结构成员的大小简单相加,而是会有字节对齐的操作;

当我们创建上面的结构体test的变量的时候:

在这里插入图片描述

  • 编译器会在内存中找到能被 1 整除的内存地址,然后给 t1 分配内存空间;

  • 再往下寻找可以被 4 整除的内存地址,然后给 t2分配内存空间;

  • 再往下寻找可以被 8 整除的内存地址,然后给 t3分配内存空间;

  • 再往下寻找可以被 2 整除的内存地址,然后给 t4分配内存空间;

  • 最后还会检查结构体的大小能否被最大的 8 字节整除,如果不能,还会在最后一个元素之后增加填充字节。

由上图可以看出,最终花费的地址空间为 24 个字节,设计程序验证;

#include<stdio.h>
struct test
{
    char t1;
    int t2;
    double t3;
    short t4;
};
int main()
{
	struct test t;
	printf("t 占的空间为:%d",sizeof(t));
	return 0;
} 

在这里插入图片描述

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值