C语言之内存管理

内存管理

计算机中的内存是分区来管理的,程序和程序之间的内存是独立的,不能互相访问,比如QQ和浏览器分别所占的内存区域是不能相互访问的。

程序内存结构

在这里插入图片描述

内存分配方式

​ 静态分配:代码段和数据段在编译器编译的时候分配空间

​ 动态分配:栈区由系统分配,堆区由程序员调用Malloc等函数进行分配

堆区内存管理函数

malloc free

#include <stdlib.h>
函数原型:
void *malloc(size_t size)

	//分配变量
    int *p1=(int*)malloc(sizeof(int)/sizeof(char));
    if(NULL==p1)
    {
        printf("malloc failed !!!\n");
    }
    free(p1);
	//分配一维数组
	int *p2=(int*)malloc(sizeof(int)/sizeof(char)*10);
	if(NULL==p2)
	{
		printf("malloc failed !!!\n");
	}
	free(p2);
	
	//分配二维数组
	int (*p3)[10]=(int*[10])malloc(sizeof(int)/sizeof(char)*5*10);
	if(NULL==p3)
	{
		printf("malloc failed !!!\n");
	}
	free(p3);
	
	//分配不连续的二维数组   =====>哈希
	int* *p4=(int**)malloc(sizeof(int*)/sizeof(char)*3);
	if(NULL==p4)
	{
		printf("malloc failed !!!\n");
	}
	free(p4);
	
	int i;
	for(i=0;i<3;i++)
	{
		p4[i]=(int*)malloc(sizeof(int)/sizeof(char)*4);
		if(NULL==p4[i])
		{
		printf("malloc failed !!!\n");
		}
	}
	
	for(i=0;i<3;i++)
	{
		free(p[i]);
	}	
	free(p4);

realloc

void *realloc(void *ptr, size_t size)
realloc()函数用来从堆上分配内存,当需要扩大一块内存空间时,realloc()试图直接从堆上当前内存段后面的字节中获得更多的内存空间,如果能够满足,则返回原指针;如果当前内存段后面的空闲字节不够,那么就使用堆上第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,而将原来的数据块释放掉。如果内存不足,重新申请空间失败,则返回NULL
int *ptr1=(int * )realloc(ptr, new_amount);
if(NULL == ptr1) return;
ptr = ptr1;
注: 用新的指针接收realloc返回值,分配成功,再将其赋给原指针

calloc

void *calloc(size_t nmemb, size_t size)
{
void *p;
size_t total;
total = nmemb * size;
p = malloc(total);
if (p != NULL)
​ memset(p, ‘\0’, total);
return p;
}
calloc是malloc函数的简单包装,它的主要优点是把动态分配的内存进行初始化,全部清零。其 操作及语法类似malloc()函数。

memset

原型:void *memset(void *s, int c, size_t n);
将s中当前位置后面的n个字节 用 c 替换并返回 s

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

int main()
{
	char a[]="ahfkhasdf";
	char*p=a;
	
	printf("%s\n",a);
	
	//字符串清零
	p=(char*)memset(p,0,sizeof(a)/sizeof(a[0]));
	
	printf("%s\n",a);
	
	return 0;
}

堆栈的区别

1、管理方式不同栈编译器自动管理,无需程序员手工控制;而堆空间的申请释放工作由程序员控制,容易产生内存泄漏。
2、空间大小不同栈是向低地址扩展的数据结构,是一块连续的内存区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,当申请的空间超过栈的剩余空间时,将提示溢出。因此,用户能从栈获得的空间较小。
堆是向高地址扩展的数据结构,是不连续的内存区域。因为系统是用链表来存储空闲内存地址的,且链表的遍历方向是由低地址向高地址。由此可见,堆获得的空间较灵活,也较大。栈中元素都是一一对应的,不会存在一个内存块从栈中间弹出的情况。
3、是否产生碎片对于堆来讲,频繁的malloc/free(new/delete)势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低(虽然程序在退出后操作系统会对内存进行回收管理)。对于栈来讲,则不会存在这个问题。
4、增长方向不同堆的增长方向是向上的,即向着内存地址增加的方向;栈的增长方向是向下的,即向着内存地址减小的方向
5、分配方式不同堆都是程序中由malloc()函数动态申请分配并由free()函数释放的;栈的分配和释放是由编译器完成的,栈的动态分配由alloca()函数完成,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行申请和释放的,无需手工实现。
6、分配效率不同栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行。堆则是C函数库提供的,它的机制很复杂,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大的空间,如果没有足够大的空间(可能是由于内存碎片太多),就有需要操作系统来重新整理内存空间,这样就有机会分到足够大小的内存,然后返回。显然,堆的效率比栈要低得多。

常见的内存的错误(Segmentation fault )

| 1、 | 指针没有指向一块合法的内存 | 定义了指针变量,但是没有为指针分配内存,即指针没有指向一块合法的内存。浅显的例子就不举了,这里举几个比较隐蔽的例子。

struct student
{
char * name;
int score;
}stu,*pstu;
int main()
{
strcpy(stu.name,“Jimy”);
stu.score = 99;
return0;
}

很多初学者犯了这个错误还不知道是怎么回事。这里定义了结构体变量 stu,但是他没想到这个结构体内部char*name 这成员在定义结构体变量 stu 时,只是给 name 这个指针变量本身分配了 4 个字节。 name 指针并没有指向一个合法的地址,这时候其内部存的只是一些乱码。所以在调用 strcpy 函数时,会将字符串"Jimy"往乱码所指的内存上拷贝,而这块内存 name 指针根本就无权访问,导致出错。解决的办法是为 name 指针 malloc 一块空间。
2、


char *p1 = “abcdefg”;

char *p2 = (char *)malloc(sizeof(char)*strlen(p1));

strcpy(p2,p1); |
| 3、 | 内存分配成功但未初始化 | |
| 4、 | 内存越界 | 内存分配成功,且已经初始化,但是操作越过了内存的边界。这种错误经常是由于操作数组或指针时出现“多1”或“少 1”。 |
| 5、 | 内存泄露 | 会产生泄漏的内存就是堆上的内存(这里不讨论资源或句柄等泄漏情况),也就是说由malloc 系列函数或new 操作符分配的内存。如果用完之后没有及时 free 或 delete,这块内存就无法释放,直到整个程序终止。 |
| 6、 | 内存释放之后 | 释放完块内存之后,没有把指针置 NULL,这个指针就成为了“野指针”,也有书叫“悬垂指针”。这是很危险的,而且也是经常出错的地方。所以一定要记住一条: |
| 7、 | 内存已经释放了,但是继续通过指针来使用 | 第一种:就是上面所说的, free( p)之后,继续通过 p 指针来访问内存。解决的办法就是给 p 置NULL。

第二种:函数返回栈内存。这是初学者最容易犯的错误。比如在函数内部定义了一个数组,却用 return 语句返回指向该数组的指针。解决的办法就是弄明白栈上变量的生命周期。(解决:将局部变量转为静态变量,这样,变量就存入到数据段中,而不是栈上)
第三种:内存使用太复杂,弄不清到底哪块内存被释放,哪块没有被释放。解决的办法是重新设计程序,改善对象之间的调用关系。 |
| 8、 | 栈溢出 | 栈上空间比较小,堆区空间很大

自测:
windows 2m
linux 8m
尽量不要在栈上(局部变量)定义很大的内存结构,例如数组
通过malloc等函数进行堆上空间分配 |

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值