请教一个 C 语言内存分配的问题

2018-05-27 12:39:47 +08:00
 whoami9894

C 语言的malloc()函数分配的是应该算是动态内存还是自动内存?我记得应该是动态内存,只要我没有 free,它就一直存在。但我在 void 函数中初始化一块内存,函数调用结束后这个指针也变成未定义了,难道这里分配的内存作用域只在函数内部吗

3242 次点击
所在节点    C
36 条回复
owenliang
2018-05-27 12:41:49 +08:00
好的
zwy100e72
2018-05-27 12:44:54 +08:00
你在函数内部调用 malloc 分配内存,但是没有把内存指针保留下来的话,函数退出后这部分内存没有释放,你也无法访问,那么就是内存泄漏了。
任何程序都应当避免内存泄漏。
adadada
2018-05-27 12:45:15 +08:00
第一个问题:动态内存。第二个问题:函数里声明的指针*变量*的作用域在函数体里,但是它指向的内存的不是,你可以把内存的地址返回出来,在函数之外继续用。
v2exchen
2018-05-27 12:48:16 +08:00
你是通过什么方式把内存地址返回的,是 return。还是传参的方式,如果是传参的方式需要用指针的指针
whoami9894
2018-05-27 12:49:54 +08:00
@zwy100e72
感谢回复
那应当函数返回分配的指针,然后 free 掉函数中的那个指针吗。但是函数中 return 和 free 的顺序该怎么写
whoami9894
2018-05-27 12:51:54 +08:00
@adadada
嗯那就是应该函数设返回值,返回那个指针指向的地址。那函数声明的指针需要显式的 free 吗
whoami9894
2018-05-27 12:53:37 +08:00
@v2exchen
我开始的错误理解是函数中分配的指针应该一直存在,所以函数没有返回值。现在懂了,应该返回分配的地址,函数中的指针不是一直存在
verrickt
2018-05-27 12:55:02 +08:00
malloc 分配的内存是在堆上的(进程地址空间的 data 区),需要显示 free

> 但我在 void 函数中初始化一块内存,函数调用结束后这个指针也变成未定义了

没理解你的意思,如果是这样
``` c
int* ub()
{
int i;
return &i
}
```
的话,i 实际是分配在栈上的局部变量,函数返回后栈帧弹出,试图访问&i 的已经是未定义行为(ub)了。

总结来说,搞明白地址在栈上还是堆上就可以判断栈帧回弹之后使用指针是不是 ub 了
whoami9894
2018-05-27 12:57:24 +08:00
@verrickt
```c
void get_cc(){
int* poi = (int*) malloc(sizeof(int*));
int ll = 1;
poi = ≪
}

void main(){
get_cc();
printf("%d",*poi);
}
```

我的意思是这样的,我错误理解 poi 指针会一直保存,但应当是那块地址会保存,poi 随函数销毁
adadada
2018-05-27 12:58:28 +08:00
@whoami9894 #6 #6 如果没有其它的办法在函数之外释放,那就需要在函数体里 free。
whoami9894
2018-05-27 13:04:24 +08:00
@adadada

好的,明白了,感谢
momocraft
2018-05-27 13:16:49 +08:00
分不分配内存都 "存在", 只是 malloc()之后 free()之前这段时间归你用
whoami9894
2018-05-27 13:29:08 +08:00
@verrickt

您好还想请教一下:像您说的那种情况,应当如何避免返回局部变量的地址呢?是直接传变量的副本作返回值吗?
WordTian
2018-05-27 13:34:41 +08:00
@whoami9894 #9 总结一下,大致就是这样了

int* poi = (int*) malloc(sizeof(int*));

左边的变量在栈区,get_cc 函数结束后,该函数的栈弹出,变量销毁

malloc 分配的内存在堆区,然后把分配的内存的地址传给栈区的 poi 指针

main 中调用 get_cc 结束后,poi 变量消失,但堆区中分配的内存还在,这时就造成了内存泄漏
whoami9894
2018-05-27 13:55:25 +08:00
@WordTian
对的,感谢
verrickt
2018-05-27 14:51:39 +08:00
@whoami9894 还是没看懂你的意思。
针对这个情况画了张[简略的图]( https://www.draw.io/?lightbox=1&target=blank&highlight=0000ff&edit=_blank&layers=1&nav=1&title=Untitled%20Diagram.xml#R7Vtdb%2FMmFP41SO2kVrbx52XcpNvFXmlSJ227pAlJ3Doms0mTvr9%2BYOMvIInbOKnbNRctPmA%2BnvMcOBwwgHer3a8pWi9%2FkBmOgWXMdgCOgWWZhm2xf1zyWkgczykEizSaiUK14CH6ics3hXQTzXDWKkgJiWm0bgunJEnwlLZkKE3Jtl1sTuJ2q2u0wIrgYYpiVfpXNKPLQupbXi3%2FDUeLZdmy6QZFziOaPi9SsklEe8CC8%2FxXZK9QWZcYaLZEM7JtiOAEwLuUEFqkVrs7HHNsS9iK9%2B735Fb9TnFCu7wAixdeULwRQ%2F8T76joG30t8chHhPk7BoDhdhlR%2FLBGU567ZQxgsiVdxezJZElRJU4p3u3tllkNlpEIkxWm6SsrUr7gCHwEf2wonre1NsySLMuGJgIhQ4IAi6rqGgSWEDjoMfEUTMaIosFhYnXFxDJ6AMVXQHmgjOuDQ8XpCIrp9wBKoICy890byGRuzNoJH1OWWvAUmPggCEAwAhMHBHcgNPLEGLBuTDwwCkDIslgBD4S2AirlNtlCLqMpecZ3JCYpkyQkYSXDeRTHkgjF0SJhj1MGJ2bykIMdsYluJDJW0WzGm9Gqqq3MHrRlSRR2VW35GmXZzum6KknQUJaCM05mI756cMBilGXRVMdXPFMWj6PjbwzQ0QywlKU4RjR6aVevG7Vo4Q8SsYZrY5DwdSyJ5BnZpFMs3mouC0cqcqV6KEoXmCr15DqoRt1NLaaiFmNniN%2FXNQRbUpSpGoKtncp7MARLg%2Fi9%2BH1dxI%2BvE2cDXHWyvtzMU03bErxvnXjkeion5wwzj6OqZeKCkQF8ny%2FOoQ9GHk%2BMLBBM8sWZCe95mcAFYZAv4AYIzA42k%2BIs%2Boke8wKcz2ve%2F3xETgic8ZusJUaPOA6rfUZpdfVO44C5iP2S6Amo6N3iETxoRzfGLdNS0NKSeRqHyiJkPs%2FwyWp1j1vbpZ1Ux%2FFunWPTT7XmttxUr48JSN3RDBCSiozHMenDHVR999Jrn0UvLWzcfzd8Ox5ys74RdjpiJWI8p3Vu5emLWrI1SkpZQfMVipKr6zKfdbBZpCHO229L%2B%2B8S8MKP6QlacTIlj9k6fzY0ogKuiGeWy%2F8AuvQL%2B%2FdU9KlZpvgbDamXz1Uvr7jkeoXimEyv2BKEyZyLrq8H0V2KM3p1ga5oyD8eiBm%2BEDYhVUgMo08fNjWoPelEI1lUTB%2BbygikGeSTDqg13fCFqxrf5kuMz9iWA5LLHxxZU9ZxtusOkK7kcGYjpfBX3bp7nrS5rFzHhquoDRyysqe7iprYdzRA%2F7nrlkLemr8HEqiJYMwW%2BEE8kpQuyYIkKJ7UUokXDTjwLqJ%2FCzFP%2F8PTbHTsKWEdq7L4Q533hCl9FUeHaEMJE9Xt%2Fk7I%2Bo2hEzaaPEghlC72B0W8oUkEVSWd972dGaduTp4GyDjT6Eg5OXz8Hkycs1DO3E8589KUg%2BVJd4Ny0D2RcvmrozRFr40CIvy0N5rnScFSM5BOkY%2BUd8UOvVZt0YP3xnSgekjw8SePGnvQHGedzR5gh6jyxSGxZUhczcGGFpLqfO4kTNSQbuEN8lit7wPf5WFcn6XH%2FNQ1GAHfzrNcLuSh3jEI%2FM8MYx8npVAXQVXd1nch281zHQDgjuph6gHvg7aqg7n5eEzUAK3pX256s%2BE5lvsheZhQ42HCS3mYUPUwX4ZIOe%2BSlHPPQbkheZjlZNWinHMhypWNNyi3HSDlILwk5dSZX4EkW6I1T85jvBN3BELtdYGMqZUevEXQDPWo58XDvV1gB4qWAsnR6XrBQK2qWtb6v2Jg7z9kbPo%2FPPrf8JqUOwjvjvd9pmsH9p6lT9Rk3FpOeWYrFHcDTyNXr9cOHHV2%2B7bkbubXmyU7Uk39GbKjBiB%2BoCg5aLRfyjodQ8%2Ba%2BlKQCa22Nm7cIZlnh2jJt3kyF7gdyPPl85POxilV5MrhnR5tU3OPT7PIFt9w%2FB9WUufwBT5uqrClHHtIhtrh%2Bt63oVq2dIHclr%2Fx6Wyo8k3089mpegtR%2FwnRJ7e%2BPSc2lfUZwQcujOyx%2FriwKF5%2FwQkn%2FwE%3D)

代码如下:

``` c
int main()
{
int i =0;
int* j = &i;
int* k = (int*)malloc(sizeof(int));
test();
}
void test()
{
int u = 0;
int& v = u;
int w = (int*)malloc(sizeof(int));

}
```

当前执行的是`int w = (int*)malloc(sizeof(int));`。
Data 和 Stack 的内存是完全分开的。函数里定义的变量都是局部变量,分配在当前栈帧上。当函数返回后,栈帧回弹,局部变量的存储空间就被回收。当`test`执行完成,返回到`main`时,u,v,w 的存储空间就被回收了。

但是 Data 区的内存并不会因为函数返回就被回收,而是需要你自己去调用 free。


> 您好还想请教一下:像您说的那种情况,应当如何避免返回局部变量的地址呢?是直接传变量的副本作返回值吗?
直接返回*值*是一种办法,但 C 中传参的拷贝语义,有时为了避免拷贝比较大的结构,也可以让调用者传指针进来,这样可以避免拷贝。还有一种方法,在被调函数中使用 malloc 分配内存。这三种方法孰优孰劣要看具体的情况。我只用过 C 写玩具,也就没法给你建议了。

第二种方法大概是这样的
``` c
typedef struct
{
int[500] a
} very_big_structure;
int main()
{
very_big_structure t;
callee(&t);
}
void callee(very_big_structure* ptr)
{
//使用 ptr
ptr->a[0]=4;
}

```
verrickt
2018-05-27 14:54:18 +08:00
@whoami9894 如果还是不太懂的话,可以看一看 CSAPP 的 Machine-Level Representation of Programs 部分
whoami9894
2018-05-27 16:10:21 +08:00
@verrickt
感谢感谢!您回复的太认真了
zhicheng
2018-05-27 16:23:04 +08:00
@whoami9894 你 9 楼的代码编译都过不去的,不懂的写个代码实验一下,不要读死书。你不理解的地方是你没有区分开变量和值。
rosu
2018-05-27 16:37:40 +08:00
@whoami9894 从你 9 楼的代码中,我觉得你可能还需要去了解什么叫生命周期。

感觉你 C 基础不是很稳固。建议选对教材,然后多实践。强烈推荐 C primer plus。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/458095

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX