Linux-C P9 内存管理

Linux-C P9 内存管理

在这里插入图片描述

内存管理基础

在这里插入图片描述

基础概念

什么是内存管理?
内存的使用时程序设计中需要考虑的重要因素之一,这不仅由于系统内存是有限的,而且内存分配也会字节映像到程序的效率。所以做好内存管理是非常重要的

上来一顿操作

这样,先来看一个程序的内存分布情况来分析

在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
	printf("Hello\n");
	return 0;
}

在这里插入图片描述

这里运行了结果,并查看了元数据类型文件的编码格式文件数据的各个段及大小

ls命名这里不多做介绍(参见Linux-C P1),这里介绍一下file命令size命令

在此之前先来看看这部分

在这里插入图片描述

文件类型与权限链接占用的节点文件所有者文件所有者用户组文件大小文件的创建时间或最后修改时间文件名称
-rwxrwxr-x1linuxlinux7291Jan 5 20:56a.out

file命令

内容
命令名称file
命令格式file [-bcLvz][-f <名称文件>][-m <魔法数字文件>…][文件或目录…]
命令功能-b  列出辨识结果时,不显示文件名称
-c  详细显示指令执行过程,便于排错或分析程序执行的情形
-f<名称文件>  指定名称文件,其内容有一个或多个文件名称时,让file依序辨识这些文件,格式为每列一个文件名称
-L  直接显示符号连接所指向的文件的类别
-m<魔法数字文件>  指定魔法数字文件
-v  显示版本信息
-z  尝试去解读压缩文件的内容
[文件或目录…] 要确定类型的文件列表,多个文件之间使用空格分开,可以使用shell通配符匹配多个文件

当然这里只做简单的使用来辨识文件的类型

在这里插入图片描述

size命令

内容
命令名称size
命令格式size [-A
命令功能控制输出格式。-A 或 --format=sysv 表示使用 System V size 风格
-B 或 --format=berkeley 表示使用 Berkeley size 风格

在这里插入图片描述

代码区数据区未初始化区十进制总和十六进制总和文件名
textdatabssdechexfilename
11022804138656aa.out

这里就可以看出,一个程序由代码区数据区未初始化区,它还包括堆区栈区,下面就来详细了解这些内存区间

内存区间

为什么查看内存分布情况的时候只有三个分区,而这里说内存一共由5个分区组成呢?

先来看看程序存储时运行时分区情况

在这里插入图片描述

因为堆区和栈区在运行时才会被分配和使用


代码区(text segment)

代码区主要存放程序中的代码,代码区指令根据程序设计流程依次执行,对于顺序指令,则只会执行一次,如果反复,则需要使用跳转指令,如果进行递归,则需要借助栈来实现
代码段(code segment/text segment )通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序


全局变量与静态变量区(data segment)

全局变量与静态变量区也称为静态存储区域,内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在


未初始化数据区(bss)

未初始化数据区(bss)用来存放程序中未初始化的全局变量的一块内存区域
BSS是英文Block Started by Symbol 的简称,BSS 段属于静态内存分配,即程序一开始就将其清零了,一般在初始化时BSS段部分将会清零


栈区(stack)

在栈上创建,在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放
由编译器自动分配释放,存放函数的参数值、局部变量的值等


堆区(heap)

用于动态内存分配,有些操作对象只有在程序运行时才能确定,这样编译器在编译时就无法为它们预先分配空间,只能在程序运行时才分配

分配方式

为什么要分配内存

(1)一个进程在运行过程中,代码是根据流程依次执行的,只需要访问一次,当然跳转和递归有可能使代码执行多次,而数据一般都需要访问多次,因此单独开辟空间以方便访问和节约空间。
(2)临时数据及需要再次使用的代码在运行时放入栈区中,生命周期短。
(3)全局数据和静态数据有可能在整个程序执行过程中都需要访问,因此单独存储管理。
(4)堆区由用户自由分配,以便管理。

静态分配与动态分配
静态分配:编译器在处理程序源代码时分配
动态分配:程序在执行时调用malloc库函数申请分配

栈和堆的区别

申请方式

:是由系统自动分配的

:需要程序员自己申请

申请大小

:是向低地址扩展的数据结构,是一块连续的内存区域

:是向高地址扩展的数据结构,是不连续的内存区域

申请速度

:由系统自动分配,速度较快

:由malloc等语句分配内存,一般速度比较慢

存储内容

:在调用函数时,第一个进栈的是函数调用语句的下一条可执行语句的地址,然后是函数的各个参数,参数是由右往左入栈的,然后是函数中的局部变量;当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始的存储地址,也就是调用该函数处的下一条指令,程序由该点继续运行

:一般在堆的头部用一个字节存放堆的大小

动态内存管理

在这里插入图片描述

动态内存基础

当程序运行到需要一个动态分配的变量时,必须向系统申请取得堆中的一块所需大小的存储空间,用于存储该变量。当不再使用该变量时, 也就是它的生命结束时,要显式释放它所占用的存储空间,这样系统就能对该堆空间进行再次分配,做到重复使用有限的资源

申请动态内存空间(malloc)

内容
函数名称malloc
所需头文件#include <stdlib.h>
函数原型void *malloc(size_t size)
函数功能分配所需的内存空间,并返回一个指向它的指针
函数传入值size – 内存块的大小,以字节为单位
函数返回值指向内存空间的指针

一般形式

#include <stdlib.h>

void *malloc(size_t size)

注意事项

1.只申请内存的大小,单位为字节
2.申请的是一块连续的内存
3.返回值类型是void*,不是某种具体类型的指针
4.显示初始化

举个栗子

这里来进行一个简单的分配动态空间,然后给分配好动态空间的变量赋值

在这里插入图片描述

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
	char *s1;
	char *s2 = "hello!";

	s1 = (char *)malloc(sizeof(s2));
	strcpy(s1,s2);
	printf("s = %s\n",s1);

	return 0;
}

在这里插入图片描述

输出结果可以看出,输出了正确的结果,但是在后面报了一段错误,为什么呢?

这就是接下要讲的free要发挥作用了,因为动态内存编译器是不负责释放的,所以在使用完之后需要进行手动释放

释放动态内存空间(free)

内容
函数名称free
所需头文件#include <stdlib.h>
函数原型void free(void *ptr)
函数功能释放之前调用 calloc、malloc 或 realloc 所分配的内存空间
函数传入值ptr 指针指向一个要释放内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的
函数返回值无返回值

一般形式

#include <stdlib.h>
void free(void *ptr)

注意事项

1.必须提供内存的起始地址,不能提供部分地址,释放内存中的一部分是不允许的
2.malloc和free配对使用,编译器不负责动态内存的释放,需要程序员手动释放
3.不允许重复释放
4.free只能释放堆空间

举个栗子

对上面的栗子进行修改

在这里插入图片描述

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
	char *s1;
	char *s2 = "hello!";

	s1 = (char *)malloc(sizeof(s2));
	strcpy(s1,s2);
	printf("s = %s\n",s1);

	free(s1);

	return 0;
}

在这里插入图片描述

这样就是一个完整的动态内存分配与释放的程序

申请并初始化内存空间(calloc)

内容
函数名称calloc
所需头文件#include <stdlb.h>
函数原型void *calloc(size_t nitems, size_t size)
函数功能分配所需的内存空间,并返回一个指向它的指针
函数传入值nitems :要被分配的元素个数
size:元素的大小
函数返回值返回一个指针,指向已分配的内存

同样是是分配内存空间,与malloc不同的是,calloc可以选择分配元素的大小并选择分配元素的多少

一般形式

#include <stdlb.h>
void *calloc(size_t nitems, size_t size) 

举个栗子

在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
	int i,n;
	int *x;

	printf("要输入元素的个数:");
	scanf("%d",&n);

	x = (int *)calloc(n,sizeof(int));
	for(i = 0;i < n;i++){
		printf("输入第%d个数字: ",i+1);
		scanf("%d",&x[i]);
	}

	printf("--------------------\n");

	printf("输入的数字为:");
	for(i = 0;i < n;i++){
		printf("%d  ",x[i]);
	}
	printf("\n");

	free(x);

	return 0;
}

在这里插入图片描述

重新分配内存空间(realloc)

内容
函数名称realloc
所需头文件#include <stdlib.h>
函数原型void *realloc(void *ptr, size_t size)
函数功能尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小
函数传入值ptr – 指针指向一个要重新分配内存的内存块
size – 内存块的新的大小,以字节为单位
函数返回值返回一个指针 ,指向重新分配大小的内存

对于realloc,就是已经通过malloc、calloc或者realloc分配的内存空间,重新进行分配的函数

一般形式

 #include <stdlib.h>
 void *realloc(void *ptr, size_t size)

举个栗子

同样这里来修改一下malloc的栗子

在这里插入图片描述

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
	char *s1;
	char *s2 = "hello!";
	char *s3 = "Hello World!";

	s1 = (char *)malloc(sizeof(s2));
	strcpy(s1,s2);
	printf("s = %s\n",s1);

	s1 = (char *)realloc(s1,sizeof(s3));
	strcpy(s1,s3);
	printf("s = %s\n",s1);

	free(s1);

	return 0;
}

在这里插入图片描述

内存处理函数

在这里插入图片描述

使用一个常量字节填充内存空间(memset)

内容
函数名称memset
所需头文件#include <string.h>
函数原型void *memset(void *str, int c, size_t n)
函数功能复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符
函数传入值str :指向要填充的内存块
c:要被设置的值,以int形式传递
n:要被设置为该值的字节数
函数返回值该值返回一个指向存储区 str 的指针

一般形式

#include <string.h>
void *memset(void *str, int c, size_t n) 

举个栗子

在这里插入图片描述

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
	char *s1;
	char *s2 = "hello!";

	s1 = (char *)malloc(sizeof(s2));
	strcpy(s1,s2);
	printf("s = %s\n",s1);

	memset(s5,'*',5);
	printf("s = %s\n",s1);

	free(s1);

	return 0;
}

在这里插入图片描述

拷贝内存空间(memcpy - memmove)

内容
函数名称memcpy
所需头文件#include <string.h>
函数原型void *memcpy(void *str1, const void *str2, size_t n)
函数功能从存储区 str2 复制 n 个字符到存储区 str1
函数传入值n:要被复制的字节数
str1:指向用于存储复制内容的目标数组
str2:指向要复制的数据源
函数返回值该函数返回一个指向目标存储区 str1 的指针

一般形式

#include <string.h>
void *memcpy(void *str1, const void *str2, size_t n)

举个栗子

在这里插入图片描述

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
	char *s1;
	char *s2 = "hello world!";

	s1 = (char *)malloc(sizeof(s2));
	
	memcpy(s1,s2+6,5);
	s1[5]='\0';

	printf("s = %s\n",s1);

	free(s1);

	return 0;
}

在这里插入图片描述

内容
函数名称memmove
所需头文件#include <string.h>
函数原型void *memmove(void *str1, const void *str2, size_t n)
函数功能从 str2 复制 n 个字符到 str1,如果目标区域和源区域有重叠的话,memmove() 能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,复制后源区域的内容会被更改
函数传入值str1:指向用于存储复制内容的目标数组
str2:指向要复制的数据源
n:要被复制的字节数
函数返回值该函数返回一个指向目标存储区 str1 的指针

一般形式

#include <string.h>
void *memmove(void *str1, const void *str2, size_t n)

举个栗子

在这里插入图片描述

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
	char *s1;
	char *s2 = "hello world!";

	s1 = (char *)malloc(sizeof(s2));
	
	memmove(s1,s2+6,5);
	s1[5]='\0';

	printf("s = %s\n",s1);

	free(s1);

	return 0;
}

在这里插入图片描述

比较内存空间(memcmp)

内容
函数名称memcmp
所需头文件#include <string.h>
函数原型int memcmp(const void *str1, const void *str2, size_t n))
函数功能把存储区 str1 和存储区 str2 的前 n 个字节进行比较
函数传入值str1:指向内存块的指针
str2:指向内存块的指针
n:要被比较的字节数
函数返回值如果返回值 < 0,则表示 str1 小于 str2
如果返回值 > 0,则表示 str2 小于 str1
如果返回值 = 0,则表示 str1 等于 str2

一般形式

#include <string.h>
int memcmp(const void *str1, const void *str2, size_t n))  

举个栗子

在这里插入图片描述

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
	char *s1;
	char *s2 = "hello!";
	char *s3 = "Hello World!";
	int r;

	s1 = (char *)malloc(sizeof(s2));
	strcpy(s1,s2);

	r = memcmp(s1,s3,5);

	printf("r = %d\n",r);

	free(s1);

	return 0;
}

在这里插入图片描述

在内存空间中搜索一个字符(memchr)

内容
函数名称memchr
所需头文件#include <string.h>
函数原型void *memchr(const void *str, int c, size_t n)
函数功能在参数 str 所指向的字符串的前 n 个字节中搜索第一次出现字符 c(一个无符号字符)的位置
函数传入值str:指向要执行搜索的内存块
c:以int形式传递的值,传递需要查找的字符的值
n:要被分析的字节数
函数返回值该函数返回一个指向匹配字节的指针,如果在给定的内存区域未出现字符,则返回 NULL

一般形式

 #include <string.h>
 void *memchr(const void *str, int c, size_t n)

举个栗子

在这里插入图片描述

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
	char *s1;
	char s2[] = "Hello World!";

	s1 = (char *)memchr(s2,'W',strlen(s2));
	printf("s = %s\n",s1);


	return 0;
}

在这里插入图片描述

更多内容

Linux-C P1 Linux基础与Vi操作

Linux-C P2 数据类型

Linux-C P3 运算符和表达式

Linux-C P4 输入输出

Linux-C P5 控制语句

Linux-C P6 数组

Linux-C P7 指针

Linux-C P8 函数

Linux-C P10 结构体细讲

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

CagePan

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

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

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

打赏作者

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

抵扣说明:

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

余额充值