动态内存管理、柔性数组

这篇具有很好参考价值的文章主要介绍了动态内存管理、柔性数组。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

动态内存分配的由来

我们已经掌握的内存开辟的方式:

int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间
  1. 上面开辟的空间大小是固定不可变的
  2. 数组申请的大小也是确定了之后就不能改变

这里就引入了动态内存的开辟,可以根据内存的需要进行更改 

动态内存函数的介绍

malloc和free

 void* malloc (size_t size);
  1. malloc的功能是申请size个连续可用size_t类型的字节空间,并返回指向这块空间的void*类型的指针 
  2. 如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查。
  3. 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使⽤的时候使⽤者⾃⼰来决定。
  4.  如果参数 size 为0,malloc的⾏为是标准是未定义的,取决于编译器。

在稍微了解了malloc后还不能直接上代码,因为动态内存申请后需要进行释放,我们先学习一下free 

free

void free(void* ptr);
  1.  free函数⽤来释放动态开辟的内存。
  2. 如果参数 ptr 指向的空间不是动态开辟的,那free函数的⾏为是未定义的。
  3. 如果参数 ptr 是NULL指针,则函数什么事都不做。
  4. malloc和free都声明在 stdlib.h 头⽂件中。

 看一段代码

#include<stdio.h>
#include<stdlib.h>
int main()
{
	//开辟40个字节/10个整形大小空间
	//int* p = (int *)malloc(40);
	int* p = (int*)malloc(10*sizeof(int));

	//检查空间是否开辟成功
	if (p == NULL)
	{
		perror("malloc");
		return 1;//异常退出
	}

	//打印出来
	for (int i = 0; i < 10; i++)
	{
		printf("%d\n", *(p + i));
	}

    free(p);//释放p指向的动态内存
    p=NULL;//内存释放掉了但是还是指向这块空间

	return 0;
}

结果如下 

动态内存管理、柔性数组,c语言,开发语言,学习方法,程序人生,改行学it,柔性数组,c++

  1. 发现结果是随机值
  2. 调试查看p的地址,发现free前后p的地址并没有发生改变,p还是指向那个地址,所以释放后这里p就是野指针,在进行解引用就是非法访问了,结论就是free之后我们还需要手动将p置为空指针

对于melloc和free总结为

1.malloc为你申请一定大小的空间,成功则返回void*的指针,失败则返回空指针;

2.由于返回的是void*,所以需要强制转换一下才可以使用;

3.在使用完之后要使用free手动释放空间;

4.释放后要将旧指针置为空指针;

calloc

void* calloc (size_t num, size_t size);
  1. 函数的功能是为 num 个⼤⼩为 size 的元素开辟⼀块空间,并且把空间的每个字节初始化为0。malloc是直接申请所有字节。
  2. calloc与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0,所以如果申请内存空间要求初始化,那么使用calloc是很好的选择。

举个例子

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* p = (int*)calloc(10, 4);
	if (p == NULL)
	{
		perror("calloc");
		return 1;
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d\n", *(p + i));
	}
	free(p);
	p = NULL;
	return 0;
}

 程序结果如下

动态内存管理、柔性数组,c语言,开发语言,学习方法,程序人生,改行学it,柔性数组,c++

realloc

void* realloc (void* ptr, size_t size);
  1.  如果在用malloc或者calloc时,发现空间大了或者小了,为了合理分配内存会使用realloc来修改内存大小。
  2. ptr是需要调整的动态内存首元素地址,size是调整后新的大小。
  3. 返回是void*类型,会返回修改后的内存起始地址。
  4. 需要注意接受修改后的地址不能用原来的ptr接受,如果申请失败会把修改之前的数据也丢失变成NULL。需要创建一个新的指针变量
  5. realloc在调整内存空间的是存在两种情况:
    ◦ 情况1:原有空间之后有⾜够⼤的空间,直接在后面加上size个字节,返回指向原有空间起始位置的指针。
    ◦ 情况2:原有空间之后没有⾜够⼤的空间,会在内存中申请一块新的地方,将旧空间里的数据复制到新的空间,并在新空间的末尾加上size个字节,返回指向新空间起始位置的指针。(可以通过调试修改size大小对比发现区别)

 举个例子

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

int main()
{
	//开辟10个整型空间
	int* p = (int*)calloc(10, sizeof(int));
	//如果开辟失败打印错误信息
	if (p == NULL)
	{
		perror("calloc");
        return 1;
	}
	//假设我们要扩容到20个整型空间
	int* str = (int*)realloc(p, 20 * sizeof(int));
    //执行任务
	if (str != NULL)
	{
    //不为空指针才使用,不然会内存泄漏
		p = str;
		str = NULL;

	}
	free(p);
	p = NULL;
	return 0;
}

关于realloc还有一点需要补充的是如果给realloc传递的指针为NULL,那么此时realloc的功能和malloc一样。此时realloc相当于malloc。

例如:

int* p=(int* )realloc(NULL,10*sizeof(int));

 文章来源地址https://www.toymoban.com/news/detail-835215.html

典型的动态内存错误

对NULL指针解应用

void test()
{
    int *p = (int *)malloc(INT_MAX/4);
    *p = 20;//如果p的值是NULL,就会有问题
    free(p);
}

对动态开辟空间越界访问

void test()
{
    int i = 0;
    int *p = (int *)malloc(10*sizeof(int));
    if(NULL == p)
    {
        exit(EXIT_FAILURE);
    }
    for(i=0; i<=10; i++)
    {
        *(p+i) = i;//当i是10的时候越界访问
    }
    free(p);
}

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

void test()
{
    int a = 10;
    int *p = &a;
    free(p);//error
}

使用free释放部分动态空间

void test()
{
    int *p = (int *)malloc(100);
    p++;
    free(p);//p不再指向动态内存的起始位置
}

对一块动态内存多次释放        

void test()
{
    int *p = (int *)malloc(100);
    free(p);
    free(p);//重复释放
}

忘记释放动态内存

void test()
{
    int *p = (int *)malloc(100);
    if(NULL != p)
    {
        *p = 20;
    }
}
int main()
{
    test();
    while(1);
}

动态内存经典笔试题分析

题目1

void GetMemory(char *p)
{
    p = (char *)malloc(100);
}
void Test(void)
{
    char *str = NULL;
    GetMemory(str);
    strcpy(str, "hello world");
    printf(str);
}

这段代码中的问题是在GetMemory函数中,试图分配内存给指针p,但它实际上并未改变传入的指针str的值。因为C语言中的函数参数传递是按值传递的,所以当你传递一个指针给函数时,实际上是传递了这个指针的拷贝。这意味着在GetMemory函数内部,你修改的是拷贝的指针,而不是原始的str指针

为了解决这个问题,你可以使用指针的指针。这样,你就可以在函数内部修改指针的值,从而影响到函数外部的指针。以下是修改后的代码:

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

void GetMemory(char **p)
{
    *p = (char *)malloc(100);
}

void Test(void)
{
    char *str = NULL;
    GetMemory(&str); // 注意这里使用了 & 运算符来传递 str 的地址
    strcpy(str, "hello world");
    printf("%s\n", str);
    free(str); // 不要忘记释放内存!
}

int main() 
{
    Test();
    return 0;
}
 

题目2

char *GetMemory(void)
{
    char p[] = "hello world";
    return p;
}
void Test(void)
{
    char *str = NULL;
    str = GetMemory();
    printf(str);
}

 

GetMemory函数中,创建了一个字符数组p,并试图返回其地址。然而这个数组是在函数内部创建的,所以当函数返回时,该数组的内存将被释放,返回的指针将变得无效。这会导致未定义的行为。所以str接收到的是野指针修改就是在static char p[]="hello world";延长生命周期。

题目3

void GetMemory(char **p, int num)
{
    *p = (char *)malloc(num);
}
void Test(void)
{
    char *str = NULL;
    GetMemory(&str, 100);
    strcpy(str, "hello");
    printf(str);
}

题目4

void Test(void)
{
    char *str = (char *) malloc(100);
    strcpy(str, "hello");
    free(str);
    if(str != NULL)
    {
        strcpy(str, "world");
        printf(str);
    }
}

柔性数组

C99中,结构中的最后⼀个元素允许是未知⼤⼩的数组,这就叫做【柔性数组】成员。

typedef struct st_type
{
    int i;
    int a[];//或者a[0] 柔性数组成员
}type_a;
int main()
{
    printf("%d\n", sizeof(type_a));//输出的是4
    //sizeof返回的这种结构⼤⼩不包括柔性数组的内存。
    return 0;
}

柔性数组的特点:
• 结构中的柔性数组成员前⾯必须⾄少⼀个其他成员。
• sizeof返回的这种结构⼤⼩不包括柔性数组的内存。
• 包含柔性数组成员的结构⽤malloc()函数进⾏内存的动态分配,并且分配的内存应该⼤于结    构的⼤⼩,以适应柔性数组的预期⼤⼩。

柔性数组的使用

int main()
{
	type_a* p = (type_a*)malloc(sizeof(type_a) + sizeof(int) * 10);
//这样相当于在原来基础上加了40个字节在柔性数组上
	p->a = 10;
    //业务处理
	for (int i = 0; i < 10; i++)
	{
		p->arr[i] = i;
	}
	printf("%d \n", p->a);
    
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", p->arr[i]);
	}
	return 0;
}

 柔性数组的优势

typedef struct st_type
{
	int a;
	int * arr;
}type_a;

int main()
{
	type_a* p = (type_a*)malloc(sizeof(int));
	p->a = 10;
	p->arr = (int*)malloc(10*sizeof(int));

	for (int i = 0; i < 10; i++)
	{
		p->arr[i] = i;
	}
	printf("%d \n", p->a);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", p->arr[i]);
	}
    //释放空间
    free(p->p_a);
    p->p_a = NULL;
    free(p);
    p = NULL;
	return 0;
}

 第⼀个好处是:⽅便内存释放
如果我们的代码是在⼀个给别⼈⽤的函数中,你在⾥⾯做了⼆次内存分配,并把整个结构体返回给⽤⼾。⽤⼾调⽤free可以释放结构体,但是⽤⼾并不知道这个结构体内的成员也需要free,所以你不能指望⽤⼾来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存⼀次性分配好了,并返回给⽤⼾⼀个结构体指针,⽤⼾做⼀次free就可以把所有的内存也给释放掉。
第⼆个好处是:这样有利于访问速度
连续的内存有益于提⾼访问速度,也有益于减少内存碎⽚。(其实,我个⼈觉得也没多⾼了,反正你跑不了要⽤做偏移量的加法来寻址

c/c++中内存函数的分配

动态内存管理、柔性数组,c语言,开发语言,学习方法,程序人生,改行学it,柔性数组,c++

C/C++程序内存分配的⼏个区域:
1. 栈区(stack):在执⾏函数时,函数内局部变量的存储单元都可以在栈上创建,函数执⾏结束时这些存储单元⾃动被释放。栈内存分配运算内置于处理器的指令集中,效率很⾼,但是分配的内存容量有限。栈区主要存放运⾏函数⽽分配的局部变量、函数参数、返回数据、返回地址等。
2. 堆区(heap):⼀般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。分配⽅式类似于链表。
3. 数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
4. 代码段:存放函数体(类成员函数和全局函数)的⼆进制代码。 

 

到了这里,关于动态内存管理、柔性数组的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 超详细——动态内存分配+柔性数组

    ☃️个人主页:fighting小泽 🌸作者简介:目前正在学习C语言和数据结构 🌼博客专栏:C语言学习 🏵️欢迎关注:评论👊🏻点赞👍🏻留言💪🏻 我们已经学会的内存开辟方式有:创建一个变量,创建一个数组 我们创建一个整形变量就会申请4个字节,创建个数组就会申请

    2023年04月15日
    浏览(29)
  • 柔性数组和C语言内存划分

    也许你从来没有听说过 柔性数组 (flexible array)这个概念,但是它确实是存在的。 C99 中,结构中的最后⼀个元素允许是未知大小的数组,这就叫做『柔性数组』成员。 例如: 有些编译器会报错⽆法编译可以改成: 1.1 柔性数组的特点: 结构中的柔性数组成员前面必须至少⼀

    2024年01月22日
    浏览(27)
  • 【C语言】还有柔性数组?

    也许你从来没有听说过柔性数组(flexible array)这个概念,但是它确实是存在的。C99中, 结构中的最后⼀个元素允许是未知⼤⼩的数组 ,这就叫做『柔性数组』成员。 欢迎关注个人主页:逸狼 创造不易,可以点点赞吗~ 如有错误,欢迎指出~ 目录 前言 柔性数组 柔性数组的特

    2024年03月10日
    浏览(28)
  • 【C进阶】分析 C/C++程序的内存开辟与柔性数组(内有干货)

            本文是对于动态内存管理知识后续的补充,以及加深对其的理解。对于动态内存管理涉及的大部分知识在这篇文章中 ---- 【C进阶】 动态内存管理_Dream_Chaser~的博客-CSDN博客         本文涉及的知识内容主要在两方面: 简单解析C/C++程序的内存开辟 分析柔性数组

    2024年02月09日
    浏览(28)
  • C语言探索旅程之【柔性数组】

    目录 1. 柔性数组的定义 2. 柔性数组的使用 3. 柔性数组的注意事项 4. 柔性数组的优点 5. 柔性数组的应用场景 当我们谈到C语言中的柔性数组时,我们指的是一种特殊的数组,其大小在运行时动态确定,而不是在编译时确定。柔性数组是C语言中一种非常有用且灵活的特性,特

    2024年03月22日
    浏览(64)
  • C语言 — 动态内存管理(动态内存函数)

    本期分为三篇介绍动态内存管理相关内容,关注博主了解更多 博主博客链接:https://blog.csdn.net/m0_74014525 本期介绍动态内存函数,函数如何使用、函数格式、在使用在所需要的注意点及C/C++程序的内存开辟区域 第一篇:C语言 — 动态内存管理(动态内存函数) 第二篇:C语言

    2024年02月14日
    浏览(33)
  • C语言柔性数组详解:让你的程序更灵活

    仔细观察下面的代码,有没有看出哪里不对劲? 还有另外一种写法: 你应该一眼就看到了,结构体的最后一个成员数组的写法是 int arr[]; 或者是 int arr[0] ,这两种写法是等价的,意思是这个数组的大小是 不确定的、未知的、可以变化的 。 C99允许这种特殊的结构体存在。这

    2024年02月09日
    浏览(32)
  • 【c语言】重温一下动态内存,int数组过大会造成栈错误

    项目场景:互助群同学在刷题的过程中,遇到的一个题目,需要申请一个很大数组,于是这个同学就写了 int[1000000] ,其实这样写也没有错,可是运行后却显示栈错误。于是就找到我来请教,我想就这个问题延申一下,在谈谈栈空间,堆空间等。 这里抛开逻辑不谈,在申请in

    2024年02月04日
    浏览(26)
  • 椋鸟C语言笔记#31:结构体(联合体)嵌套、柔性数组

    萌新的学习笔记,写错了恳请斧正。 目录 结构体(联合体)嵌套 嵌套时内嵌结构体(联合体)不创建变量(匿名) 嵌套时内嵌结构体(联合体)创建变量(非匿名) 嵌套初始化 柔性数组 柔性数组的使用 结构体(联合体)嵌套 结构体、联合体可以嵌套,也就是说结构体(

    2024年02月02日
    浏览(36)
  • 「探索C语言内存:动态内存管理解析」

    🌠先赞后看,不足指正!🌠 🎈这将对我有很大的帮助!🎈 📝所属专栏:C语言知识 📝阿哇旭的主页:Awas-Home page 目录   引言 1. 静态内存 2. 动态内存 2.1 动态内存开辟函数 2.1.1 malloc函数 2.1.2 calloc函数 2.1.3 realloc函数 2.2 动态内存释放函数 2.2.1 free函数 3. 动态内存的常见

    2024年04月28日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包