问:
栈区堆区静态区的大小是固定的吗?如果栈区满了,会向后2者借位置吗?
ChatAI答:
栈区、堆区和静态区的大小通常是由操作系统或编译器预定义的,不是固定的。这些区域的大小通常受到多种因素的影响,如系统物理内存大小、进程虚拟地址空间的大小、编译器和操作系统的实现等等。
当栈区满了时,是不能向堆区或静态区借位置的。栈区和堆区是两个独立的内存区域,它们使用不同的指针来管理内存空间。当栈区满了,意味着当前栈帧中的内存空间已经全部使用完毕,如果需要分配更多的内存,需要在栈上创建一个新的栈帧来继续分配内存。但是,由于栈区的大小通常是有限的,当创建的栈帧数量过多或栈帧过大时,就会出现栈溢出的情况,导致程序崩溃。
另一方面,堆区和静态区可以被程序共享使用,甚至可以跨越多个栈帧和函数调用。堆区由malloc、realloc、calloc等函数动态分配,当内存不够时,它可以通过请求更多的虚拟内存来扩展堆区的大小。静态区的大小在编译时就已经确定,通常包括全局变量、静态变量、常量等,而且静态区的大小不会随着程序运行而改变。
总的来说,栈区、堆区和静态区的大小通常是由各种因素共同决定的,并不是固定的。程序在运行时会预先将这些区域划分好,而当其中的某个区域已经满了时,就需要通过不同的方式来处理。
动态函数的介绍
malloc和free
C语言提供了一个动态内存开辟的函数:
void* malloc (size_t size);
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
。如果开辟成功,则返回一个指向开辟好空间的指针。
。如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
。返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
。如果参数size为0,malloc的行为是标准是未定义的,取决于编译器。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
//向内存申请10个整型的空间
int* p = (int*)malloc(10 * sizeof(int));
//int* p = (int*)malloc(INT_MAX);
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));
}
}
free(p);
p = NULL;
return 0;
}
C语言提供了一个另外的函数free,专门是用来做动态内存的释放和回收的,函数原型如下:
void free(void* ptr);
free函数用来释放动态开辟的内存。
。如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定位的。
。如果参数ptr是NULL指针,则函数什么事都不做。
calloc
C语言还提供了一个函数叫calloc,calloc函数也用来动态内存分配。原型如下:
void* calloc(size_t num,size_t size);
。函数的功能为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0。
。与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为全0.
问:
有calloc函数了还要malloc函数做什么?
ChatAI答:
calloc函数与malloc函数都是用来动态分配内存的函数,二者的不同之处在于内存分配和初始化的方式不同。
- malloc函数分配内存空间时,内存中的数据不会被初始化,需要手动初始化才能使用。如果不进行初始化操作,申请的内存空间中可能残留着之前的垃圾数据,这就会对程序产生潜在的安全隐患。
- 而calloc函数分配内存空间时,不仅会申请指定大小的内存,还会将其初始化为0。
因此,如果程序需要申请一块内存,并且希望该内存全部清零,那么就应该使用calloc函数;如果程序需要申请一块内存,但不需要初始化其中的数据,则需要使用malloc函数。
注意,calloc函数与malloc函数的返回值是相同的,它们都返回指向所分配内存的首字节的指针。需要注意的是,在使用calloc函数分配内存时,分配的空间大小第一个参数n和第二个参数size是传入的字节数的形式,例如calloc(n, size)中所请求的内存大小为 n*size 个字节。
总之,malloc函数和calloc函数都有各自的使用场景,根据实际情况选择使用。在申请内存时,应该根据需要进行初始化或者清零操作,确保程序的正确性和安全性。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main()
{
int * p = (int*)calloc(10 , sizeof(int));
if (p == NULL)
{
//打印错误原因的一个方式
printf("%s\n", strerror(errno));
}
else
{
//正常使用空间
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
}
free(p);
p = NULL;
return 0;
}
问:
如果malloc后又realloc将动态内存空间设置小了,再free,那么之前被改小的部分是在realloc的时候就释放了,还是要直到free的时候才释放?
ChatAI答:
如果使用realloc函数将先前申请的动态内存空间设置为比原先更小的大小,那么被裁剪的那一部分内存空间会立即被释放,即在realloc函数调用之后就被释放了。程序无法再访问被裁剪的那一部分内存空间,但是其内存空间并没有马上被OS收回,而是被保留在堆上,等待以后可能的内存分配请求。
当使用free函数释放指针所指的内存块时,其行为就与动态分配内存的方式是无关的。而是由malloc,calloc,realloc之类的函数所使用的堆管理器,根据内部的算法来决定如何回收内存块。因此,即使调用realloc函数将先前申请的内存缩小,被裁剪的那一部分内存空间也可能不会立即被OS收回,而可能暂时被保留在堆上,等待可能的内存分配请求。
在实际使用中,为了避免内存泄漏,建议在不需要使用先前申请的动态内存时,立即使用free函数将其释放,而不是让其保留在堆上。因此,如果在realloc函数之后又立即使用free函数释放指针所指的内存块,则被裁剪的那一部分内存空间应该能被立即回收,而不必等到程序结束或操作系统重启等时机。
realloc
realloc函数的出现让动态内存管理更加灵活。
为了合理的内存,我们会一定程度对内存的大小做灵活的调整。realloc函数可以做到对动态开辟内存大小的调整。函数原型如下:
void* realloc(void* ptr,size_t size);
。ptr是要调整的内存地址
。size调整之后新大小
。返回值为调整之后的内存起始位置
。这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间
。realloc在调整内存空间的是存在两种情况:
。情况1:原有空间之后有足够大的空间
问:realloc函数调整后返回的指针地址和先前malloc开辟返回的地址会不会发生改变?
ChatAI答:
realloc函数的作用是调整先前通过malloc或calloc函数动态分配的内存块的大小,并返回指向调整后内存块的新地址的指针。当内存块当前的大小不足以满足分配新内存时,realloc函数则会在其他地方重新申请新的内存块,并将先前内存块的数据复制到新的地址中,最后释放原先的内存块。这样做可以保证分配的新内存块的大小不会小于要求的大小,并且可以尽量减少内存的碎片。
如果调用realloc函数会导致内存大小实际上并没有发生变化,那么realloc函数就可能返回与先前内存块相同的地址。但是,调用realloc函数重新调整内存块大小时,返回的指针地址可能会发生改变。具体来说,如果realloc函数需要在其他地方重新申请内存块,则返回的指针地址将是新内存块的首地址;如果内存块可以直接调整大小,则返回的指针地址可能与先前的地址相同,但是不能保证这种情况始终发生。文章来源:https://www.toymoban.com/news/detail-459402.html
因此,为了保险起见,在使用realloc函数时应该始终将其返回的指针赋值给一个新的指针变量,并且在使用完毕后,应该使用新指针变量来访问内存块,而不是依赖先前的指针变量。这样可以避免内存访问错误和其它潜在的问题,并提高程序的可靠性和稳定性。文章来源地址https://www.toymoban.com/news/detail-459402.html
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main()
{
int * p = (int*)malloc(20);
if (p == NULL)
{
//打印错误原因的一个方式
printf("%s\n", strerror(errno));
}
else
{
//正常使用空间
int i = 0;
for (i = 0; i < 5; i++)
{
*(p + i) = 3 * i;
}
for (i = 0; i < 5; i++)
{
printf("%d ", *(p + i));
}
}
printf("\n");
//realloc使用的注意事项:
//1.如果p指向的空间之后有足够的内存空间可以追加,则直接追加,后返回p
//2.如果p指向的空间之后没有足够的内存空间可以追加,则realloc函数会重新找一个新的内存区域
//开辟一块满足需求的空间,并且把原来内存中的数据拷贝回来,释放旧的内存空间,最后返回新开辟
//的内存空间的地址
//3.得用一个新的变量来接收realloc函数的返回值(一般情况没什么,但若realloc申请失败返回了NULL,
//就会把原指针给改成NULL,这样原来的内存也找不到了。)
int* p2 = (int*)realloc(p, 40);
printf("%p %p\n", p, p2);
//free(p); !!!这是错误的多此一举
//p = NULL; realloc函数已经自动将p处理了
int i = 0;
for (i = 5; i < 10; i++)
{
*(p2 + i) = 3 * i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
free(p2);
p2 = NULL;
return 0;
}
到了这里,关于第五十三天学习记录:C语言进阶:动态内存管理Ⅰ的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!