❤️C语言动态内存管理库函数介绍❤️(下)

简介: 大家好!在实现动态通讯录的时候,我用到了malloc 和realloc动态申请内存,所以今天我想来和大家分享有关动态内存管理函数与柔性数组的相关知识。

💐2.C语言动态内存管理库函数应用



🌿🌿2.1常见相关笔试题


💐题目一:请问运行Test 函数会有什么样的结果?

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

💐分析:程序运行极大可能打印随机值


由于p是一个局部变量,因此当其出了函数GetMemory之后就会销毁,虽然在销毁之前将地址返回给str,但str维护的是一块野指针,因此str就会非法访问内存.

这类问题属于返回栈空间地址的问题。栈空间的地址不能轻易返回,因为一旦出了作用域之后就被销毁了。


💐题目二:请问运行Test 函数会有什么样的结果?

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

💐分析:程序运行打印随机值


函数GetMemory的形参为char* p,p为该函数的局部变量,作用域在函数内部,出了函数该变量就被销毁了,并且没有对申请好的内存进行释放。所以参数str传入函数GetMemory后,其值不会改变,仍为NULL,空地址是不能被用户访问修改的,因此程序崩溃。


💐题目三:请问运行Test 函数会有什么样的结果?

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

💐分析:该程序虽然会输出hello,但是是存在内存泄漏的,因为最后并没有释放申请的内存。

正确写法应该是:

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

💐题目四:请问运行Test 函数会有什么样的结果?

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

💐分析:由于free完没有置为NULL空指针,因此错误。

正确写法:

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


🌿🌿2.2C/C++语言中的内存开辟


2f37a9c5d03949b5ba9a79bab5561453.png

栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行 结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但 是分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、 返回地址等。


堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分

配方式类似于链表。


数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。


代码段:存放函数体(类成员函数和全局函数)的二进制代码。


🌿🌿2.3柔性数组


也许你从来没有听说过柔性数组(flexible array)这个概念,但是它确实是存在的。

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


🌺🌺🌺2.3.1柔性数组特点与使用


typedef struct st_type
{
 int i;
 int a[];  //柔性数组成员
}type_a;

💐注意事项:

  1. 结构中的柔性数组成员前面必须至少一个其他成员。
  2. sizeof 返回的这种结构大小不包括柔性数组的内存。
  3. 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大
    小,以适应柔性数组的预期大小。

💐举个栗子:

typedef struct st_type
{
 int i;
 int a[0];//柔性数组成员
}type_a;
printf("%d\n", sizeof(type_a));//输出的是4

这里输出的是4,不包括柔性数组的大小。


💐为柔性数组开辟空间

int i = 0;
type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
//业务处理
p->i = 100;
for(i=0; i<100; i++) {
 p->a[i] = i; }
free(p);

这里使用malloc为柔性数组开辟空间,100*sizeof(int)就是开辟了柔性数组的空间。

这样柔性数组成员a,相当于获得了100个整型元素的连续空间。


🌺🌺🌺2.3.2柔性数组的优点


💐第一个好处是:方便内存释放


如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回 给用户。

用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这事。如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。


💐 第二个好处是:这样有利于访问速度.


连续的内存有益于提高访问速度,也有益于减少内存碎片。


相关文章
|
2月前
|
存储 C语言
`scanf`是C语言中用于按格式读取标准输入的函数
`scanf`是C语言中用于按格式读取标准输入的函数,通过格式字符串解析输入并存入指定变量。需注意输入格式严格匹配,并建议检查返回值以确保读取成功,提升程序健壮性。
892 0
|
4月前
|
安全 C语言
C语言中的字符、字符串及内存操作函数详细讲解
通过这些函数的正确使用,可以有效管理字符串和内存操作,它们是C语言编程中不可或缺的工具。
294 15
|
9月前
|
人工智能 Java 程序员
一文彻底搞清楚C语言的函数
本文介绍C语言函数:函数是程序模块化的工具,由函数头和函数体组成,涵盖定义、调用、参数传递及声明等内容。值传递确保实参不受影响,函数声明增强代码可读性。君志所向,一往无前!
302 1
一文彻底搞清楚C语言的函数
|
10月前
|
存储 C语言
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
472 16
|
10月前
|
存储 编译器 C语言
【C语言程序设计——函数】分数数列求和2(头歌实践教学平台习题)【合集】
函数首部:按照 C 语言语法,函数的定义首部表明这是一个自定义函数,函数名为fun,它接收一个整型参数n,用于指定要求阶乘的那个数,并且函数的返回值类型为float(在实际中如果阶乘结果数值较大,用float可能会有精度损失,也可以考虑使用double等更合适的数据类型,这里以float为例)。例如:// 函数体代码将放在这里函数体内部变量定义:在函数体中,首先需要定义一些变量来辅助完成阶乘的计算。比如需要定义一个变量(通常为float或double类型,这里假设用float。
318 3
|
10月前
|
存储 算法 安全
【C语言程序设计——函数】分数数列求和1(头歌实践教学平台习题)【合集】
if 语句是最基础的形式,当条件为真时执行其内部的语句块;switch 语句则适用于针对一个表达式的多个固定值进行判断,根据表达式的值与各个 case 后的常量值匹配情况,执行相应 case 分支下的语句,直到遇到 break 语句跳出 switch 结构,若没有匹配值则执行 default 分支(可选)。例如,在判断一个数是否大于 10 的场景中,条件表达式为 “num> 10”,这里的 “num” 是程序中的变量,通过比较其值与 10 的大小关系来确定条件的真假。常量的值必须是唯一的,且在同一个。
269 2
|
10月前
|
存储 编译器 C语言
【C语言程序设计——函数】回文数判定(头歌实践教学平台习题)【合集】
算术运算于 C 语言仿若精密 “齿轮组”,驱动着数值处理流程。编写函数求区间[100,500]中所有的回文数,要求每行打印10个数。根据提示在右侧编辑器Begin--End之间的区域内补充必要的代码。如果操作数是浮点数,在 C 语言中是不允许直接进行。的结果是 -1,因为 -7 除以 3 商为 -2,余数为 -1;注意:每一个数据输出格式为 printf("%4d", i);的结果是 1,因为 7 除以 -3 商为 -2,余数为 1。取余运算要求两个操作数必须是整数类型,包括。开始你的任务吧,祝你成功!
181 1
|
4月前
|
存储
阿里云轻量应用服务器收费标准价格表:200Mbps带宽、CPU内存及存储配置详解
阿里云香港轻量应用服务器,200Mbps带宽,免备案,支持多IP及国际线路,月租25元起,年付享8.5折优惠,适用于网站、应用等多种场景。
1366 0
|
4月前
|
存储 缓存 NoSQL
内存管理基础:数据结构的存储方式
数据结构在内存中的存储方式主要包括连续存储、链式存储、索引存储和散列存储。连续存储如数组,数据元素按顺序连续存放,访问速度快但扩展性差;链式存储如链表,通过指针连接分散的节点,便于插入删除但访问效率低;索引存储通过索引表提高查找效率,常用于数据库系统;散列存储如哈希表,通过哈希函数实现快速存取,但需处理冲突。不同场景下应根据访问模式、数据规模和操作频率选择合适的存储结构,甚至结合多种方式以达到最优性能。掌握这些存储机制是构建高效程序和理解高级数据结构的基础。
406 0

热门文章

最新文章