C的动态内存管理

动态内存分配

使用malloc类的函数分配内存

用这些内存支持应用程序

用free函数释放内存

实例代码

    int *ptr = (int *)malloc(sizeof(int));//动态分配内存,大小为一个int
	*pi = 5;
	cout << *pi << endl;
	free(pi);//释放内存

图片说明 (主函数也是函数,所以在栈里创建了指针,并指向在堆中开辟的内存)

内存泄漏

如果不再使用已分配的内存却没有将其释放将导致内存泄漏

丢失内存地址

没有调用free函数

丢失地址

int main() 
{	
	int *pi = (int *)malloc(sizeof(int));
	*pi = 5;
	pi = (int *)malloc(sizeof(int));//又在堆区开辟了空间,并让指针指向它 
	//这时候,我们无法找到第一次堆区开辟的空间了,我们也无法释放它了,这造成了内存泄漏 
	
	return 0;
} 

当pi被赋值为一个新地址时,它丢失了原指向的内存地址,第一次分配的地址丢失了

int main() 
{
	char* name = (char*)malloc(strlen("Susan") + 1);
	strcpy_s(name, strlen("Susan") + 1,"Susan");
	while (*name != 0) {
		cout << *name;
		name++;//改变了name的值,找不到原来开辟内存的初始地址
	}

	free(name);//free之后,显示异常

	return 0;
}

我们改变了name的值,而name是一个地址,它现在找不到原来开辟内存的初始地址,我们想要释放掉原来开辟的内存,需要分配内存的起始地址(free之后,显示异常)

如图所示,指针指向了末尾的地址,但我们需要分配内存的起始地址

没有调用free函数

int main() 
{
	int* ptr = (int*)malloc(sizeof(int));
	*ptr = 10;
	//free(ptr);注释掉了free函数

	return 0;
}

注释掉了free函数,编译器不会主动释放我们所开辟的内存

动态内存分配函数

各类动态内存分配函数的作用

malloc函数

    int *ptr = (int *)malloc(sizeof(int));//动态分配内存,大小为一个int

malloc的操作 

  • 从堆上分配内存
  • 内存不会被修改或是清空
  • 返回首字节的地址 

最好加上强制类型转换

  • 便于理解函数的用意
  • 代码可以和C++兼容,它需要显示的强制类型转换

分配内存失败

  • 如果声明一个指针,但没有在使用之前为它指向的地址分配内存,那么内存通常包含垃圾,这往往会导致一个无效内存引用的错误
int *p;
...
cout << *p << endl;

如代码所示,我们只是声明了一个指针,却没有为它初始化,不知道它指向何处。而我们使用malloc函数是会为指针返回我们所开辟的内存的首地址的 

如图所示,栈内的指针不知其指向何处

没有给malloc传递正确的参数

  • 申请空间的时候要准确说明,这里推荐使用sizeof操作符,因为每个系统的某些类型的字节数可能不一样,手动计算大小容易分配错误。明确指出类型避免出错
double* ptr = (double*)malloc(10 * sizeof(double));

申请十个double类型大小的存储空间 

calloc函数

原型

void *calloc(size_t numElements, size_t elementSize);

calloc函数在分配的同时会清空内存(将其设置为二进制0)

该函数根据两个参数的乘积分配内存,无法分配返回空指针

	int *ptr = (int *)calloc(5,sizeof(int));// 5 * 4 = 20 (字节)

等价于 

int main() {

	int *ptr = (int *)malloc(sizeof(int));
	memset(ptr,0,5*sizeof(int)); 
	for (int i=0; i<5; i++)
	cout << *(ptr+i) << " "; // 0 0 0 0 0
	
	return 0;
} 

realloc函数

我们可能时不时需要增加或减少为指针分配的内存,realloc函数会重新分配内存

原型

void *realloc(void *ptr, size_t size);

 该函数接受两个参数,第一个是指向原内存块的指针,第二个是请求的大小。会重新找一个更大的块复制原来内存块的数据,再返回新块的指针

 realloc函数的行为

案例(比原内存块小)

int main() 
{
	char* string1;
	char* string2;
	string1 = (char*)malloc(16);
	strcpy(string1, "0123456789AB");

	string2 = (char *)realloc(string1, 8);//分配更小的空间
	printf("string1 Value: %p [%s]\n", string1, string1);
	printf("string2 Value: %p [%s]\n", string2, string2);
	
	return 0;
}

 输出

string1 Value: 0000000000ae1510 [0123456789AB]
string2 Value: 0000000000ae1510 [0123456789AB]

它没有选择新的空间,而是在原始的内存块,但是我们限制了8个字节,却可以继续访问到后面的数据,或许我们可以将NULL赋值给后面的地址以停止访问。

  

以上是内存的分配情况,我们虽然指定了8个字节的限制,但是因为我们没有找新的内存块,用的是以往的,所以哪怕是越界却依旧可以访问后面的字符 

alloca函数和变长数组

alloca函数在函数的栈帧上分配内存,函数返回后会自动释放内存。若底层的运行时系统不急于栈,alloca函数会很难实现,所以这个函数是不标准的,避免使用。

C99引入了变长数组(VLA),允许函数内部生命和创建其长度由变量决定的数组

void array(int n)
{
	char* buffer[n];
}

用free函数释放内存

int main() 
{
	int* ptr = (int*)malloc(sizeof(int));
	*ptr = 10;
	cout << *ptr << endl;
	free(ptr);

	return 0;
}

free释放指针参数所指向的内存,这块内存被返还给堆。尽管指针仍然指向这块区域,但是我们应该将它看作只想垃圾数据 

 

迷途指针也即我们所说的悬空指针,为了安全起见,一般要把该指针赋为NULL

 将已释放的指针赋值为NULL

已释放的指针仍然可能造成问题,如果我们试图引用一个已释放的指针,其行为将是未定义的

ptr = NULL;

重复释放

如果我们试图重复释放同一块内存,会发生异常

C++深拷贝浅拷贝大家应该都有印象

迷途指针(悬空指针)

如果内存已经释放,而指针还在引用原始内存,这样的指针称为迷途指针(悬空指针),迷途指针没有指向有效对象,也被称为过早释放

造成问题

  • 如果访问内存,则行为不可预期
  • 如果内存不可访问,则是段错误
  • 潜在的安全隐患

一般犯错情况

  • 访问已经释放的内存
  • 返回的指针指向的是上次函数调用中的自动变量

迷途指针案例

int main() 
{
	int* ptr = (int*)malloc(sizeof(int));
	*ptr = 10;
	cout << *ptr << endl;
	free(ptr);

	return 0;
}

此时的ptr就是迷途指针,如果我们使用指针对此内存块进行访问,将会出现不可预估的后果

int main() 
{
	int* ptr = (int*)malloc(sizeof(int));
	int* ptr2 = ptr;
	*ptr = 10;
	cout << *ptr << endl;
	free(ptr);
	

	return 0;
}

 

此时ptr2是ptr的指针别名,它也是迷途指针,我们经常赋值好几个指针相同的地址,这很容易造成许多的迷途指针 

处理迷途指针 

释放指针后,将其置为NULL,后续使用这个指针会终止程序,但是赋值只影响一个迷途指针,如果存在多个指针公用的情况,很难处理

写一个特殊的函数代替free函数

有些系统会在释放后覆写数据,在不抛出异常的情况下,程序员看到这些值就会明白程序可能在访问已释放的内存

用第三方工具检测迷途指针和其他问题

动态分配内存技术

C的垃圾回收

有些非标准的技术可以用来解决部分问题i,可以自动的释放内存。内存不再使用之后会被收集起来以备后续使用,释放的内存成为垃圾,所以称为垃圾回收

垃圾回收意义

  • 程序员不需要想着何时释放内存

资源获取及初始化

可以用来解决C++中资源的分配和释放,即使有异常发生,这种技术也可以保证资源的初始化和后续的释放。

使用异常处理函数

不是很懂。。。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值