最后更新:2021-11-18 09:58:26 手机定位技术交流文章
目录
一、内存划分
二、四大函数
①malloc
②free
③calloc
④realloc
三、易错分析
问题一:
问题二:
问题三:
问题四:
问题五:
问题六:
四、经典面试题
面试题一:
面试题二:
面试题三:
五、柔性数组
1.前言
2.特点
3.优势
为了了解动态记忆管理,必须首先了解业务C对主要类型的记忆分类:
栈区 1 当您执行函数时,您可以在堆叠的函数中建立所有本地变量的存储单位,并在功能执行结束时自动释放存储单位。
虽然在两个堆叠的内存分布操作中将指示集中在处理器中非常高效,但分配的内存容量受到限制。 (溢出问题)
3个本地变量、功能参数、返回的数据、返回的地址等等,分配给运行功能的仓库储存。
从4到4的上升意味着要求仓库提供地址的数量减少。
堆区 1 通常由程序员放行,但如果程序员不放行,可在过程结束时检索。
在堆叠上,打开动态内存。
值为3或以上,表示要求从堆叠中找到的地址数量增加。
数据段(静态区) 1 包括全球变量和静态数据。
操作完成后 系统会释放你
代码段 1 储存库函数的二元代码(分类成员函数和全球函数)。
2 其中的数据仅可查看,不能更改
(上图来源比特科技

激活: 请求在堆叠区域中设置一个大小为_ t 大小( unit bytes) 的空间。 成功返回动态空间打开地址, 无法返回空 NULL 。
注意一个。 大小没有定义, 结果取决于编译者的处理 。
因为Malloc提供了无效的* 值, 最好先强制转换类型, 然后再用指针接收转换类型 。
三. 解决记忆开启失败并返回到空指针上。
#include
int main()
{
int*p = (int *)malloc(40);
return 0;
}
你看,我们放松的动态已经打开了一个特定比例的空间。 但是,你是否怀疑打开空间的计算机记忆没有缩小和缩小? 此外,在我们的程序结束时,动态区域被自动回收,我们可以选择主动 — — 使用自由功能。

激活: 动态空间释放( 阻塞是动态空间指针)
注意一个。 Free (NULL) 表示该函数不执行任何操作 。
二. 自由功能不能用于释放非动力空间。
3。 Free is only a free space, not a clear pointer, 暗示你和你的爱人分手了, 但仍记得她的电话号码。 如果此时使用这个提示, 就会犯非法存取记忆的错误 。 因此, 指示器应该是空的, 并及时被完全忽略 。
#include
#include
int main()
{
int i = 0;
int*p = (int *)malloc(10*sizeof(int));
assert(p);//监测是否开辟成功
for (i = 0; i < 10; i++)
{
p[i] = i;//或者为*(p+i),但不可以是p++。
}
for (i = 0; i < 10; i++)
{
printf("%d ",p[i]);
}
free(p);
p = NULL;//最好置为NULL
return 0;
}
马洛克和自由的结合完善了动态记忆开启进程。 但是,不仅马洛克有责任创建记录和档案管理;卡洛克也可以发挥作用。

激活 : 请求从堆叠区获取一个空间以存储 num 大小大小的项目。 成功时返回指针到开放空间, 失败时返回 NULL 。
当 Calloc 打开空间时, 它会将每个元素初始化为零 。 因此, Malloc 打开空间更有效率, Calloc 会自动指定一个零值, 每个值都有自己的技能 。
Calloc 打开初始值为 0 的值 。
Malloc 开放 - 尚未开始

了解Malloc 和 Calloc 等动态函数, 如果您没有足够的空间, 您可能会想知道该怎么办 。 别担心, C 以动态打开空间的大小来提供真环函数 。

激活: 将动态内存扩大至大小( 单位字节) 。
(a) 特别努力理解两种情况,即对地平线进行重新缩放:
情况一:

如果原始空间足够,则在原始内存之后立即添加该空间,原空间中的数据不作改动。
情况二:

当原始空间之后空间不足时,通过在要使用的堆叠空间上再找到一个适当大小的连续空间并复制原始内容来扩展空间。这种方法产生一个新的内存地址。
马洛克在方向上创造了空间

在 p1 指针空间之后, 重新分配它 。

在重新分配后,有一个空间可以指向。
根据上述调查,可以得出以下结论(在空间有限的情况下):
一. 将新地址转回新空间。
之前的空格内容将复制到新创建的地方
三 原始空间的内容被揭发了
当输入的指针是空的时, realloc 执行与 Malloc 相同的目的 。
对任何缺陷或缺陷审查下列守则。
#include
#include
//缺陷版
int main()
{
int i = 0;
int*p = (int *)malloc(5*sizeof(int));
for (i = 0; i < 5; i++)
{
p[i] = i;
}
p = realloc(p, 1000000);//ok?
free(p);
p = NULL;
}
//完善版
int main()
{
int i = 0;
int*p = (int *)malloc(5*sizeof(int));
assert(p);
for (i = 0; i < 5; i++)
{
p[i] = i;
}
int *p1 = (int *)realloc(p, 1000);
if(p1!=NULL)
{
p=p1;//为了程序的连贯性,最好都赋值给最开始使用的p
}
free(p);
p = NULL;
}
第一,无论你使用马洛克还是地环,你都必须小心记忆,以避免无法返回NULL,因此测试是必要的。
“2p = realloc (p, 100万);”如果原始地址没有被发现,妻子和士兵将遭受损失。
void test()
{
int i = 0;
int *p = (int *)malloc(10 * sizeof(int));
if (NULL == p)
{
exit(EXIT_FAILURE);
}
for (i = 0; i <= 10; i++)//ok?
{
*(p + i) = i;
}
free(p);
}
1.注意越界访问的问题,"i<10"才不会让程序崩溃
void test()
{
int a = 10;
int *p = &a;
free(p);//ok?
}
第一,你不能自由使用来释放非动力记忆。
void test()
{
int *p = (int *)malloc(100);
p++;//ok?
free(p);
}
无法修改 p 地址; 否则, 当空格空闲时, 部分空间错误可以释放 。
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);
}
一. 应用程序似乎犯了重复释放错误。
二. 问题在这个短代码中是显而易见的;但是,我们怎样才能在大代码中防止它?
其中一个是造成差距的,另一个是缩小差距的。
2个获得了将针头标记为NULL的做法,因此即使针头是免费的(NULL),也没有发生任何情况。
void test()
{
int *p = (int *)malloc(100);
if (NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while (1);
}
一. 在使用上述软件时,任务经理表明,CPU占用的空间一直在扩大(计算机有一个保护机制,而且在某种程度上已经停止增长),腾出了空间,但没有释放我们的剩余部分,这也被称为内存泄漏。
因此,必须正确释放动态内存。
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
int main()
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
return 0;
}
手术的结果是什么?
分析 : 新来者通常会成为这个错误的受害者, 假设 str 成功指向开放空间。 然而, 当你想一想, Str 能够真正完成我们所相信的任务 。 在这里, 我们把地址操作和交叉参照操作混在一起, p 只是一个字符串的临时副本, 改变 p 对字符串没有影响 。 当然, 上面的代码也没有回溯值, 也不存在任何错误 愿意释放记忆 。
下次面试时,我们会更深入地进行
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
int main()
{
char *str = NULL;
str = (char*)GetMemory();
printf(str);
return 0;
}
这个计划的结果是什么?
分析 : 如果您相信 GetMemory 方法返回了 p 地址, 您是对的 。 尽管如此, p 地址目前不再被称为“ elloworld ” 。 p [ ] 数组是按函数构造的, 正如文章前面讨论的内存布局一样, 而函数所需的本地变量位于存储区域, 而存储区域的变量具有此函数完成后的属性, 变量将被销毁, 因此p 只保留当前地址, 不再被称为“ elloworld ” 。 如果 字符str 此时被删除引用, 非法访问内存时会发生错误 。
int main(void)
{
char *str = (char *)malloc(100);
strcpy(str, "hello");
free(str);
if (str != NULL)
{
strcpy(str, "world");
printf(str);
}
return 0;
}
这个节目的结果是什么?“世界”应该印出来。
分析:我很好奇是否有任何小伙伴认为没有打印出来, 如果是这样的话, 我相信对自由的作用有一个误解。 自由只是释放了堆积物的记忆, 它没有给Stret指向的地方指定空的指针。
改变一个面试程序的方法:
我们怎样才能纠正他,在分析上述三个访谈问题并感觉你更能理解动态记忆之后,又回到第一个问题?在访谈问题1中代码的失败是由于P和Stre没有被排练,作为破解的手段,我们有两个想法:Address operation 2. return the address of p.
//采用传址操作
void GetMemory(char **p)
{
*p = (char *)malloc(100);
}
int main()
{
char *str = NULL;
GetMemory(&str);
if (str != NULL)
{
strcpy(str, "hello world");
printf(str);
}
free(str);
str = NULL;
return 0;
}
//返回p的地址
char* GetMemory(char *p)
{
p = (char *)malloc(100);
return p;
}
int main()
{
char *str = NULL;
str=(char *)GetMemory(str);
if (str != NULL)
{
strcpy(str, "hello world");
printf(str);
}
free(str);
str = NULL;
return 0;
}
数组的长度将被动态更改。 以下代码可以接受吗?
const int n = 0;
int main()
{
int arr[n] = { 0 };
return 0;
}
显然不是,必须是一个常数,即使与修饰者是一个变数,但这个变数不能改变。我们能否使用指向动态空间的指针?这种方法是可行的,但在这里我们将提出一个更好的解决办法——灵活的阵列(在C99中是新的)。然后我们将比较两者。
一. 结构的灵活数组元素必须是最终的。
2. 此结构提供的大小( 支架 s) 不包括柔性阵列的大小 。
三. 包含灵活阵列组件的结构采用Malloc () 方法进行动态内存分布,分配的内存应大于结构,以适应灵活阵列的预测大小。
使用示范:
struct s
{
int n;
int arr[0];
};
int main()
{
int i = 0;
struct s*p = (struct s*)malloc(sizeof(struct s)+40);//注意struct的大小不包括动态开辟的数组
if (p != NULL)
{
for (i = 0; i < 10; i++)
{
p->arr[i] = i;
}
}
free(p);
p = NULL;
return 0;
}
让我们看看我们能不能用指示器来完成它
#include
struct s
{
int n;
int *arr;
};
int main()
{
struct s*ps =(struct s*) malloc(sizeof(struct s));
ps->n=10;
if (ps != NULL)
{
ps->arr = (struct s*)malloc(ps->n*sizeof(int));//用arr接收开辟空间的地址
if (ps->arr != NULL)
{
int i = 0;
for (i = 0; i < 10; i++)
{
ps->arr[i] = i;
}
}
}
free(ps->arr);
ps->arr = NULL;
free(ps);
ps = NULL;
return 0;
}
相对于带指针的阵列动态构造,灵活阵列有三个优点:
第一,容易腾出空间。
如果我们的代码是用于他人的函数, 您可以在内部执行二级的记忆分配, 并将完整的结构还给用户。 用户可以自由调用该结构, 但用户并不知道该结构的成员也需要自由, 所以您不能指望用户能够了解它。 所以, 如果我们一劳永逸地分配该结构的内存和其成员的内存, 并且向用户提供结构体参考, 用户可以释放所有的内存 。
2.减少内存碎片
带有指针的结构和结构在指针空间上不连贯,在这段空间里,往往会浪费内存碎片。
3.提高访问速度
在计算机中CPU读取速度 硬盘<内存<高速缓存<寄存器
计算机在阅读数据时坚持“ 地点原则 ”, 该原则指出, 由于下一次访问记忆的机会有80%接近目前的记忆, 登记册将预读周围的数据, 如果数据不是连续的, 则表示内存失败, 登记册将从高速缓存到内存甚至硬盘, 直到找到它。 速度本来就缓慢 。
本文由 在线网速测试 整理编辑,转载请注明出处。