C语言内存分配中,主要重点讲解栈区和堆区
栈区
栈是一种先进后出的内存结构,由编译器自动分配释放,存放函数的参数值、返回值、局部变量等。在程序运行过程中实时加载和释放,因此,局部变量的生存周期为申请到释放该段栈空间。
栈区内容会在调用完自动释放
代码语言:javascript复制int * myFunc()
{
int a = 10;
return &a;
}
void test01()
{
//局部变量a早已被释放,因此我们没有权限操作这块内存空间
int * p = myFunc();
printf("*p = %dn", *p);
printf("*p = %dn", *p);
}
所以上面代码在打印结果的时候无法输出正确的地址,可能第一次会打印正确,那也只是系统优化了,第二次输出还是会表现错误
下面看另一种情况
在下面的代码中,变量 str
是在 getString()
函数的栈帧中分配的局部变量,其生命周期仅限于函数调用过程中。因此,当函数返回时,str
将被销毁,其内存地址也将被回收。
当我们在 test02()
函数中调用 getString()
函数并将其返回值赋给指针 p
时,p
指向的是一个已经被销毁的字符串。这样的指针被称为“悬挂指针”,使用它将导致未定义的行为。
str会指向常量区"hello world" 的地址,但是随着而后将会被销毁
代码语言:javascript复制char* getString()
{
char str[] = "hello world";
return str;
}
void test02()
{
char* p = NULL;
p = getString();
printf("%sn", p);
}
要解决这个问题,您可以将 str
定义成一个静态变量或动态分配内存,这样可以在函数返回后仍然存在。以下是一个使用静态变量的示例:
char* getString()
{
static char str[] = "hello world";
return str;
}
void test02()
{
char* p = NULL;
p = getString();
printf("%sn", p);
}
或者您也可以使用动态分配内存的方式:
代码语言:javascript复制char* getString()
{
char* str = (char*)malloc(sizeof(char) * 12);
strcpy(str, "hello world");
return str;
}
void test02()
{
char* p = NULL;
p = getString();
printf("%sn", p);
free(p); // 记得释放内存
}
堆区
堆是一个大容器,它的容量要远远大于栈,但没有栈那样先进后出的顺序。用于动态内存分配。堆在内存中位于BSS区和栈区之间。一般由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。
下面用一个代码,给直观的感受:由于分配了内存,所以不会被自然释放。
代码语言:javascript复制int * getSpace()
{
int * p = malloc(sizeof(int)* 5);
if (p == NULL)
{
return;
}
for (int i = 0; i < 5; i )
{
p[i] = i 100;
}
return p;
}
void test01()
{
int * p = getSpace();
for (int i = 0; i < 5; i )
{
printf("%dn", p[i]);
}
//手动开辟 手动释放
free(p);
p = NULL;
}
堆区注意事项
我们使用了堆的操作,但是不能打印出hello world,
代码语言:javascript复制void allocateSpace(char * pp)
{
char * temp = malloc(100);
memset(temp, 0, 100);
strcpy(temp, "hello world");
pp = temp;
}
void test02()
{
char * p = NULL;
allocateSpace(p);
printf("%sn", p);
}
在 allocateSpace()
函数中,我们在堆上分配了一段内存,并将 "hello world" 写入该内存。然后,我们将 temp
指向的内存地址赋给了 pp
,但 pp
是一个指向 test02()
函数中局部变量 p
的指针,我们并没有改变 p
的指向,而是改变了 pp
的指向,因此 test02()
函数中的 p
指针仍然是 NULL。
在 test02()
函数中,我们尝试打印 p
指针所指向的字符串,但由于 p
仍然是 NULL,因此打印的结果也是不确定的,有可能是一个空字符串,也有可能是其他未定义的内容。
在pp = temp之后,pp的地址为hello world的地址,但是没有影响到char *p
为了解决这个问题,我们应该使用指向指针的指针,这样可以修改 test02()
函数中 p
指针的指向,使其指向 allocateSpace()
函数中分配的内存。修改后的代码如下:
void allocateSpace(char **pp)
{
char *temp = malloc(100);
memset(temp, 0, 100);
strcpy(temp, "hello world");
*pp = temp;
}
void test02()
{
char *p = NULL;
allocateSpace(&p);
printf("%sn", p);
}