文章目录
一、函数执行过程
C 语言程序运行时,先从 main 函数出发,在 main 中以出栈入栈的方式调用外部函数。
int main()
{
int a = 1, b = 2;
int add_int(int a, int b);
add_int(a, b);
return 0;
}
int add_int(int a, int b)
{
int sum = 0;
sum = a + b;
return sum;
}
可以看到 main 函数是第一个入栈的,其次是 add_int 函数。严格遵循先入后出,后入先出的原则。
二、 内存区域
1. 内存区域有哪些
代码区:当我们的程序被执行时,它会有一个加载准备的过程。其中函数及内部的流程
结构代码指令会被放到代码区中,等待着被调用执行。
常数区:字符串常量、const 全局变量在程序启动加载时会放置在常数区,被放置在此
区域的数据不可被修改,只能读取。
全局区/静态区:全局变量和代码中的静态变量会在程序启动执行时被加载到此区域。
此区域的变量空间只有程序结束关闭后才会被释放。
堆区:堆区是由程序员自主管理的内存空间,可以通过 malloc 等函数在堆区内存中申
请需要的任意大小的内存空间。但当此内存空间不打算继续使用时,务必使用 free 函
数释放掉这些空间,否则在程序关闭以前,这些内存空间是不会被其它程序所使用的,
会造成很大的空间浪费甚至宕机。直到程序被关闭后没有手动释放的空间才会被操作系
统回收
栈区:当函数被调用时,其内部的形参、函数内的其它局部变量会被创建在栈区。当
函数被调用结束后,配合函数的局部变量就会被操作系统自动释放回收。
2.全局变量与局部变量的区别
全局变量通常指在函数体外部创建的变量。程序被启动执行时,它们会被创建在全
局区。而局部变量指函数体内的定义的变量,包括形式参数在内都是局部变量。它们是
伴随函数调用而被创建于栈区,函数调用结束后从栈区被回收的临时性变量。全局变量
与局部变量的区别在于生命周期与作用域的不同。
三、堆内存使用方法
堆内存是由程序员根据程序的需要而自行申请的内存空间。堆内存给了程序员极
大的自主性使用一片内存空间。但是当程序不需要这个空间时需要及时自行释放
申请的堆内存空间。我们在申请内存空间时有三种方式。
1. malloc 函数
malloc 函数用于向堆内存空间申请一片指定字节数量的内存空间,其参数是
需要的字节总数量。建议用 sizeof 运算符计算需要的字节数大小。malloc 的
返回值是一个 void * 类型的指针。因为 malloc 不确定你申请一片内存空间是
干什么用的,它只能返回给你准备好的内存片空间的首地址。如果是 c++编
译器,其语法检测比较严格,会认为 malloc 返回的类型是 void *,为了不发
报错建议大家用强转类型转换。
#include <stdio.h>
#include <malloc.h>
int main()
{
int* p =(int*) malloc(sizeof(int));
*p = 10;
return 0;
}
2. free 函数
free 函数用于释放掉不使用的堆内存空间。我们只需要把申请的内存片区空间的首
地址交给 free 函数即可。
3. calloc 函数
calloc 函数声明语法为:void * calloc(size_t count,size_t,size);
其中第一个参数代表数据成员总数量,第二个参数代表其中一个数据成员所需的空
间大小。
#include <stdio.h>
#include <malloc.h>
int main()
{
int size = 5;
int* p = calloc(size, sizeof(int));
p[0] = 1;
p[1] = 3;
p[2] = 5;
p[3] = 7;
p[4] = 9;
free(p);
p=NULL;
return 0;
}
那么 malloc 与 calloc 在创建类似数组空间有什么区别呢?表面上是参数不同,
malloc 需要计算总的空间大小,calloc 则分开设置数量与单个的大小。
还有个区别是 calloc 具有空间初始化清零的功能:
malloc:
calloc
4. realloc 函数
realloc 函数 用于当前的内存空间不够用,想要扩展一下大小的情况
int main()
{
int size = 5;
int* p = calloc(size, sizeof(int));
p[0] = 1;
p[1] = 3;
p[2] = 5;
p[3] = 7;
p[4] = 9;
size += 3;
p = realloc(p, size * sizeof(int));
p[5] = 10;
p[6] = 20;
p[7] = 30;
free(p);
p=NULL;
return 0;
}
从p = realloc(p, size * sizeof(int));这句代码可以看到realloc函数的第一个
参数是现有的数据在堆的内存空间地址,第二个参数是扩容后最终空间的
总大小。realloc会根据扩容的情况进行空间的处理。如果现有空间的后续
空间没有被使用且能够容纳所需的扩容,就在这个基础上扩充。如果不能,
就会在堆内存中寻找一段能容纳我们要求大小的新的内存空间,再把现有
的内容复制到新的空间,并返回新空间的地址。
四、栈内存与堆内存的区别
1、分配方式与分配效率不同:
当函数被调用执行时,其形参与函数体内的局部变量会一起在栈区内全部被创建出来,
而堆内存的空间分配是 malloc、calloc 等函数被执行时才会分配空间。
从分配效率上讲栈内存高于堆内存。
2、 存储内容不同:
堆内存更适用于存储数据量较大的情况,而栈内存更适合存储临时少量的数据。如果函数
之间共享一个数据结构(比如链表),那么比较好的处理是把这个数据结构存储在堆内存,
栈内存掌握这个堆空间的指针即可,这样就避免了栈空间频繁的创建与回收带来的开销。
3.管理方式不同:
栈内存空间是由操作系统支持管理的,堆内存需要开发者自行维护——创建与回收。如果
开发者疏忽了回收就会产生垃圾内存。所以堆内存是容易产生内存碎片的,栈内存由于系统
自动维护则不会产生内存碎片