前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C语言进阶篇-01内存分配原理

C语言进阶篇-01内存分配原理

作者头像
莫浅子
发布2023-10-17 15:55:08
1880
发布2023-10-17 15:55:08
举报

C语言内存分配中,主要重点讲解栈区和堆区

栈区

栈是一种先进后出的内存结构,由编译器自动分配释放,存放函数的参数值、返回值、局部变量等。在程序运行过程中实时加载和释放,因此,局部变量的生存周期为申请到释放该段栈空间。

栈区内容会在调用完自动释放

代码语言:javascript
复制
int * myFunc()
{
	int a = 10;
	return &a;
}

void test01()
{
	//局部变量a早已被释放,因此我们没有权限操作这块内存空间
	int * p = myFunc();
	printf("*p = %d\n", *p);
	printf("*p = %d\n", *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("%s\n", p);
}

要解决这个问题,您可以将 str 定义成一个静态变量或动态分配内存,这样可以在函数返回后仍然存在。以下是一个使用静态变量的示例:

代码语言:javascript
复制
char* getString()
{
    static char str[] = "hello world";
    return str;
}

void test02()
{
    char* p = NULL;
    p = getString();
    printf("%s\n", 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("%s\n", 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("%d\n", 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("%s\n", 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() 函数中分配的内存。修改后的代码如下:

代码语言: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("%s\n", p);
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-06-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 栈区
  • 堆区
    • 堆区注意事项
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档