zoukankan      html  css  js  c++  java
  • 动态内存分配

    动态分配内存的意义

    定义数组的长度的时候,必须指定长度,这是在编译期间就要确定的。

    当我们无法在编译期间确定到底需要多大的内存块,此时就无法定义数组的大小:

    • 实际使用的元素数量超过了声明的长度,程序无法处理。
    • 如果程序实际使用的元素数量较少,巨型数组会造成内存空间浪费。

    此时就需要在运行的时候根据实际的情况(比如根据输入的数据的大小),来动态的申请内存空间,然后让指针指向这块新申请的内存。

    malloc和free

    malloc和free是库函数,不是系统调用

    #include "stdlib.h"
    
    void * malloc(size_t size);
    void free(void* pointer);
    

    关于malloc

    • malloc所分配的是一块连续的内存,参数size是所分配的内存字节数。
    • malloc的返回值是void* ,具体使用的时候需要做强制类型转换。
    • 当请求的动态内存无法满足的时候,malloc返回NULL,对每个从malloc返回指针都进行检查,确保它不是NULLL。
    • malloc申请的动态内存中的数据是随机值,不会被初始化为0。
    • malloc 实际分配的内存可能有会比请求的多。

    关于free

    • free的参数要么是NULL,要么是从malloc、callocrealloc返回的值。
    • free用于将申请的动态内存归还给系统。
    • 当 free 的参数为 NULL 时,函数直接返回。

    calloc与realloc

    #include "stdlib.h"
    
    void* `calloc`(size_t num,size_t size);
    void* `realloc`(void* pointer,size_t new_size);
    

    关于calloc

    • calloc函数的参数包含了所需元素的数量以及每个元素的字节数,根据这些值可以计算出一共所需的内存大小。
    • calloc会将申请的内存空间初始化为0。
    • 如果指向把值存储到数组中,这种操作会浪费一定的时间。

    关于realloc

    • realloc用于修改原先已经分配的内存块大小。
    • realloc的第一个参数pointer为NULL时,realloc相当于malloc。
    • realloc用于缩小一个内存块时,该内存块的尾部的部分将会被拿掉。realloc用于扩大内存块时,原有内容依然保留,新增加的内存块添加到原先内存块的后面,新内存并未以任何方式初始化。
    • 如果原先的内存块无法改变大小,realloc将会分配一个正确大小的内存,并把原有的内容拷贝到新的内存块上,因此realloc之后就不能使用指向旧内存的指针,而应该使用realloc返回的指针。

    常见的动态内存分配错误

    对NULL指针解引用操作

    int main(void)
    {
    	int *p = (int *)malloc(sizeof(int));
    
    	*p = 20;	//这里没有判断返回值
    				//如果返回值为NULL则会出现错误
    	free(p);
    
    	system("pause");
    	return 0;
    }
    

    对动态开辟的空间越界访问

    int main(void)
    {   
    	int *p = (int *)malloc(10 * sizeof(int));
    	if (p == NULL)
    	{
    		exit(EXIT_FAILURE);
    	}
    
    	int i = 0;
    	for (i = 0; i <= 10; i++)  // 访问越界
    	{
    		p[i] = i;
    	}
    
    	free(p);
    	p = NULL;
    	
        system("pause");
    	return 0;
    }
    

    对非动态开辟的内存使用free释放

    int main(void)
    {
    	int a = 10;
    	int *p = &a;
    	free(p);  // p不是动态申请的内存
    
    	system("pause");
    	return 0;
    }
    

    使用free释放一块动态开辟内存的一部分

    int main(void)
    {
    	int *p = (int *)malloc(10 * sizeof(int));
    
    	if (p == NULL)
    	{
    		exit(EXIT_FAILURE);
    	}
    
    	p++;
    	free(p);  // 释放部分动态申请的内存
    
    	system("pause");
    	return 0;
    }
    

    对同一块内存多次释放

    int main(void)
    {
    	int *p = (int *)malloc(sizeof(int));
    
    	if (p == NULL)
    	{
    		exit(EXIT_FAILURE);
    	}
    
    	free(p);
    	free(p);  // 第二次释放
    
    	system("pause");
    	return 0;
    }
    

    动态开辟的内存忘记释放(内存泄漏)

    int main(void)
    {
    	int *p = (int *)malloc(sizeof(int) * 10);
    
    	if (p == NULL)
    	{
    		exit(EXIT_FAILURE);
    	}
    
    	system("pause");
    	return 0;
    }
    

    编写内存泄漏检查模块

    头文件 mleak.h

    #pragma once
    
    #include "stdio.h"
    #include "malloc.h"
    
    #define MALLOC(n) mallocEx(n,__FILE__,__LINE__)
    #define FREE(p)   freeEx(p)
    
    void *mallocEx(size_t n, const char *file, const int line);
    void freeEx(void *p);
    void PRINT_LEAK_INFO();
    
    

    实现 mleak.c

    #include "mleak.h"
    
    #define MAXSIZE 256
    typedef struct
    {
    	void * pointer;
    	int size;
    	const char* file;
    	int line;	
    }MallocItem;
    
    static MallocItem g_record[MAXSIZE];
    
    void *mallocEx(size_t n, const char *file, const int line)
    {
    	void * p = malloc(n);
    	int i = 0;
    	if (p != NULL)
    	{
    		for (i = 0; i < MAXSIZE; i++)
    		{
    			if (g_record[i].pointer == NULL)
    			{
    				g_record[i].pointer = p;
    				g_record[i].size = n;
    				g_record[i].file = file;
    				g_record[i].line = line;
    				break;
    			}
    		}
    	}
    
    	return p;
    }
    
    void freeEx(void *p)
    {
    	if (p != NULL)
    	{
    		int i = 0;
    		for (i = 0; i < MAXSIZE; i++)
    		{
    			if (g_record[i].pointer == p)
    			{
    				g_record[i].pointer = NULL;
    				g_record[i].size = 0;
    				g_record[i].file = NULL;
    				g_record[i].line = 0;
    				break;
    			}
    		}
    	}
    }
    
    void PRINT_LEAK_INFO()
    {	int i = 0;
    	printf("Potenital Memory Leak Info:
    ");
    	for (i=0; i < MAXSIZE; i++)
    	{
    		if (g_record[i].pointer != NULL)
    		{
    			printf("Address:%p, size:%d, Location:%s:%d
    ",
    				g_record[i].pointer,
    				g_record[i].size,
    				g_record[i].file,
    				g_record[i].line);
    		}
    	}
    }
    
    

    测试

    #include "stdio.h"
    #include "stdlib.h"
    #include "mleak.h"
    
    void f()
    {
    	MALLOC(100);
    }
    
    int main()
    {
    	int* p = (int*)MALLOC(3 * sizeof(int));
    
    	f();
    
    	p[0] = 1;
    	p[1] = 2;
    	p[2] = 3;
    
    	FREE(p);
    
    	PRINT_LEAK_INFO();
    
    	return 0;
    }
    
    

    结果:

  • 相关阅读:
    快速学习javaSE基础4---面向对象的编程
    eclipse 小技巧,设置输入任何字母都有快捷提示.
    快速学习javaSE基础3-java必须了解的基本语法2(熟记)
    快速学习javaSE基础3-java必须了解的基本语法1(熟记)
    数据可视化之导入数据
    pandas读取Excel文件
    pandas进阶
    给定地球上两点的经纬度,求两点之间沿地球表面最短的弧线距离
    06异常
    05设计模式
  • 原文地址:https://www.cnblogs.com/chay/p/11521379.html
Copyright © 2011-2022 走看看