动态内存管理 【自用整理】

动态内存管理

栈区局部变量、函数的形式参数
堆区动态内存分配 mallocfreerealloccalloc
静态区全局变量、 静态变量 static int a = 10;

mallocfree

Return Value

malloc开辟失败,则放回空指针,所以返回值一定要做检查。

malloc returns a void pointer to the allocated space, or NULL if there is insufficient memory available. To return a pointer to a type other than void, use a type cast on the return value. The storage space pointed to by the return value is guaranteed to be suitably aligned for storage of any type of object. If size is 0, **malloc **allocates a zero-length item in the heap and returns a valid pointer to that item. Always check the return from malloc, even if the amount of memory requested is small.

向内存申请10个整型的空间 — 动态内存开辟

int* p = (int*)malloc(10*sizeof(int));

开辟失败 — 打印错误原因 的一个方式

万一 malloc 失败了, p就被赋值成NULL

所以要进行合理性的判断。

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

if(p==NULL)
{
    printf("%s\n",strerror(errno));
}

开辟成功

else
{
	//正常使用空间
    int i = 0;
    for(i=0;i<10;i++)
    {
        *(p+i) i;
    }
    for(i=0;i<10;i++)
        printf("%d  ",*(p+i));
}

当动态申请的空间不再使用的时候,就应该换给操作系统。

因为p还记得这块内存空间的起始地址,还能找到这块空间。

free(p);
p = NULL

calloc

Allocates an array in memory with elements initialized to 0.

为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0

void *calloc( size_t num, size_t size );
num

Number of elements

size

Length in bytes of each element

栗子

int* p = (int*)calloc(10,sizeof(int));

开辟失败 — 返回空指针

if(p==NULL)
{
	printf("%s\n",strerror(errno));
}

free 函数用来释放动态开辟的空间

free(p);
p = NULL;

realloc

Reallocate memory blocks.

调整动态开辟内存空间的大小。

void *realloc( void *memblock, size_t size );
int main()
{
    int* p = (int*)malloc(20);
    if(!p)
        printf("%S\n",strerror(errno));
    else
    {
		int i  = 0;
        for(i=0;i<5;i++)
        {
			*(p+i) = i;
        }
    }
    //只是在使用malloc开辟的20个字节的空间
    //假设 20个字节无法满足使用要求
    // 希望能够有 40个字节的空间
    // 这里可以使用 realloc 来调整动态分配的内存
    
    int* ptr = realloc(p,INT_MAX);
    
    if(ptr != NULL)
    {
		p = ptr;
        int i = 0;
        for(i = 5; i<10; i++)
        {
            *(p+i) = i;
        }
        
        for(i = 0; i <10;i++)
        {
			printf("%d\n", *(p+i));
        }
    }
    
    //释放内存
    free(p);
    p = NULL;
    return 0;
}

realloc 函数 使用注意事项

  1. 原先开辟的空间后面有足够多的内存空间,可以追加则直接追加,后返回原有的地址
  2. 原来开辟的空间没有足够多的内存空间可以追加,则realloc函数会重新找一块新的内存区域,开辟一块满足需求的空间,并且把原来内存中的数据拷贝下来,释放旧的内存空间,最后返回新开辟的内存空间地址。
  3. 得用一个新的变量接受realloc函数的返回值

追加失败

if(ptr == NULL)
{
    
}

追加成功

if(ptr != NULL)
{
	p = ptr;
    // badabadabada......
}

realloc直接开辟内存空间

int* p = (int*)realloc(NULL40);

常见动态内存错误

1.对空指针NULL进行解引用操作

办事前先看看有没有安全检查措施。

2.对动态开辟内存的越界访问

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

free的是堆区上的内存空间。

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

free只能从开辟的起始位置开始释放。

5.对同一块动态开辟的内存多次释放

6.动态开辟空间忘记释放 — 内存泄漏

法一:(二级指针)指针变量的地址传递,以一个二级指针的形式参数接收。

法二:(修改返回值)上司临死之前把卧底的身份返回return回来,采用return一级指针的方式

他开辟的空间没有销毁。

函数结束的时候,栈空间的地址被操作系统回收 ------- 返回栈空间地址的错误。

堆区开辟的空间没有free仍然存在,不销毁 ------- 返回堆空间,只有free能回收。

free释放str指向的空间后,并不会把str置为NULL,释放后需要把str=NULL。

C/C++内存开辟

内核空间(用户代码不能读写)
栈(向下增长) ↓ 局部变量、函数参数、返回数据、返回地址 【临时的】
内存映射段(文件映射、动态库、匿名映射)
堆(向上增长) ↑一般由程序员分配释放,若程序员不释放,程序结束后由OS回收。
数据段【静态区】(全局数据、静态数据)程序结束后由OS释放
代码段(可执行代码/只读常量) 比如常量字符串不能修改

假设买了4个G内存的电脑,有2个G留给内核使用。

柔性数组 arr[] 或 arr[0]

C99中,结构体中的最后一个元素允许是未知大小的数组,这就叫做“柔性数组”的成员。

struct S
{
	int n;
	int arr[]; //未知大小的-柔性数组成员-数组的大小是可以调整的
};

使用1 柔性数组,将int的变量和柔性数组开辟在一条连续的空间上。

struct S* ps = (struct S*)malloc(sizeof(S) + 5 * sizeof(int));

使用2 动态分配内存

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

struct S
{
	int n;
	int* arr; //柔性数组
};

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	ps->arr = (int*)malloc(5 * sizeof(int));
	int i = 0;
	for (i = 0; i < 5; i++)
		ps->arr[i] = i;
	for (i = 0; i < 5; i++)
		printf("%d ", ps->arr[i]);
	printf("\n");
	//调整大小
	int* ptr = realloc(ps->arr, 10 * sizeof(int));
	if (ptr != NULL)
		ps->arr = ptr;
	for (i = 5; i < 10; i++)
		ps->arr[i] = i;
	for (i = 0; i < 10; i++)
		printf("%d  ", ps->arr[i]);
	//释放内存
	free(ps->arr);
	ps->arr = NULL;
	free(ps);
	ps->arr = NULL;
	return 0;
}

动态内存分配中mallocfree成对使用。

使用2:(即使用动态内存分配,不使用柔性数组)

1.两次使用malloc,容易出错,free有先后区别。

2.动态分配内存,内存碎片多,内存利用率低。

3.两次malloc开辟的区域在内存中使不连续存在的。

而使用1中,内存是连续的,访问效率更高。

存储金字塔结构 (越往上访问速度越快)
寄存器
高速缓存 cache
内存
硬盘

CPU访问速度快,但是内存跟不上。所以内存中的数据放到cache里,cache里的数据放寄存器里。使用1符合局部性原理,使用2数据不连续,寄存器拿数据拿不到,跑去cache里拿,cache没有就去内存里拿,命中率、访问效率都低。

柔性数组的优点

方便内存释放(释放一次就够了)、利于访问速度(连续)。

柔性数组的特点

1.结构中的柔性数组成员前面必须至少一个其他成员

2.sizeof返回的这种结构大小不包括柔性数组的内存

3.包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔型数组的预期大小。

#include <stdio.h>
struct S
{
	int n;
	int arr[];
};

int main()
{
	printf("%d\n", sizeof(struct S));   // 4
	struct S* p = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	// | n |  arr                                |
	p->n = 100;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		p->arr[i] = i;
	}
	for (i = 0; i < 10; i++)
		printf("%d ", p->arr[i]);

	//利用realloc调整大小
	free(p);
	p = NULL;
	return 0;
}

C语言结构体里的成员数组和指针

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

JoyCheung-

赏颗糖吃吧~~~

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

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

打赏作者

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

抵扣说明:

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

余额充值