admin管理员组

文章数量:815885

C语言 栈和堆详解

关于C语言malloc的一个问题,自动和手动分配内存的区别是什么?
//定义一个结构

typedef struct linkedlist* nextnode; 
typedef struct linkedlist{char c;nextnode next;
}link;//第一种 
link* newLink(){link* l;l=(link*)malloc(sizeof(link));l->next=NULL;return l;
}//第二种,和上面的效果一样! 
link* newLink(){link a={};link* b=&a;return b;
}

C语言新手,想问一下,第一种和第二种的区别是什么?我感觉两个效果是一样的,都能返回我所需要的结构指针。

— 以上是问题 —

— 以下是回答 —

作者:Tabjy
链接:
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

先上结论:Local variables (局部变量),也就是你的第二种写法,会在离开 scope (作用域),也就是函数结束时被销毁掉。(严格上来说还在内存那个位置,并没有被销毁,下面会有解释)而用 malloc()申请的内存只有在整个程序退出之后,或者你手动调用 free() 的时候被释放掉。
以下是解释
要理解这个问题,首先要知道内存的结构。一般来说内存分为了一下四个部分:

  1. Stack (栈内存):主要是用来存储 function calls (函数调用)local variables 的空间,其本质就是一个 Stack(栈)。最底层的便是 main() 函数,每调用一个函数时就会执行 push 操作,每当函数 return 时便执行 pop 操作。什么时候 main() 也被 pop 了,整个程序也就结束了。(如果这个 stack 变得太高以至于超出了最大内存地址,就会出现所谓的 stackoverflow
  2. HEAP(堆内存):主要是用来存储由 malloc() 等申请的内存位置。如果 malloc() 返回 null 的话就往往表示这一块空间已经用完了。
  3. Static (静态内存):不太确定这个翻译。这里的变量的生命周期与整个程序相同,即在进程创建是被申明,在程序退出时被销毁。 global variables(全局作用域变量), file scope variables(文件作用域变量)被 static 关键字修饰的变量会存在这里。
  4. Code:关于这方面的划分,有不同的理解。可以简单的理解为程序的二进制指令和一些其他必要的东西。

这个问题只涉及到 stackHEAP 内存。刚刚说了,所有的 local variable 按照 stack 的形式被存储在 stack memory 中。这个 stack 由一个 stack pointer 来管理,它所指向的内存地址按照 function call 的运行情况来增加或减少。比如 函数A 调用了 函数B,函数B 就会被 push 到 函数A 之上,原本的 stack pointer 也会从 函数A 移动到 函数B。pop 函数B 时 stack pointer 的操作也是同理,那么理论上包含在 函数B 中的变量已经被释放,为什么大多数情况还是能在 函数A 中得到这个值呢?因为C语言在设计之初就是以性能为优先,所以 pop 时只会移动 stack pointer 而不会花费额外的资源去覆盖掉之前的内存。如果在使用那个 pointer 之前并没有再调用其他的函数,那么这么 pointer 所对应的地址就不会被覆盖掉。

请考虑以下情况:

int* getAnswer() {int a = 42;return &a;
}void overwriteMemoryWithGarbageData() {int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
}int main() {int *answerToLife = getAnswer();overwriteMemoryWithGarbageData();printf("%d\n", *answerToLife);return 0;
}

这段代码便不会得到 生命、宇宙以及任何事情的终极答案,而是像 9 这样的垃圾数据。此时,正确的处理方法便是:

int* getAnswer() {int *ret = malloc(sizeof(int));*ret = 42;return ret;
}

记住,系统永远不会主动释放 HEAP 中的内存,因为C语言中并没有垃圾回收之类的机制,滥用 malloc() 会导致 HEAP 很快被填满造成内存泄露。所以在用完之后尽量记得 free(void *__ptr):

int main() {int *meaningOfLife = getAnswer();overwriteMemoryWithGarbageData();printf("%d\n", *meaningOfLife);free(meaningOfLife);return 0;
}

**在实际使用中,只要是需要被自身以外的函数调用的变量,请全部从 HEAP 中申请。**而申请者也需要记住在使用完毕后及时 free() 。
解释完毕
这里只是 CS 大一学生,并不保证以上内容的正确性或准确性,总之考试是这么答的就是了(当然没有写这么多)。因为学习环境是英文,而且无法保证翻译的准确定,以上术语均使用英文(某些在第一次出现时给出了我所认为正确的翻译)。如于事实有所出入,还请多多包涵。

作者:Tabjy
链接:
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本文标签: C语言 栈和堆详解