一、内存区域划分与分配:
1、栈区(stack)——程序运行时由编译器自动分配,存放函数的参数值,局部变量的值等,程序结束时由编译器自动释放,先进后出。(注意:地址空间是由高地址向低地址增长。栈的空间较小,一般Linux程序只有几M的空间,所以局部变量或函数入参要避免超大栈内存的使用,防止出现栈溢出。)
2、堆区(heap) —— 在内存开辟另一块存储区域。一般由程序员分配释放,先进先出。用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。(注意:堆地址由低地址向高地址增长。堆内存牢记不忘释放,避免内存泄漏的情况。)
3、全局区(静态区)(static)——编译器编译时即分配内存。全局变量和静态变量的存储是放在一块的。对于C语言初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。而C++则没有这个区别 - 程序结束后由系统释放
4、文字常量区 ——常量字符串就是放在这里的, 程序结束后由系统释放。
5、程序代码区——存放函数体的二进制代码。
其示意图如下图1:
图1 C程序的内存布局
在将应用程序加载到内存空间执行时,操作系统负责代码段、数据段和BSS段的加载,并在内存中为这些段分配空间。栈也是由操作系统管理,堆需要程序员自己管理(申请和释放)。
动态分配: 在运行时动态分配内存,由程序员显式管理,通过malloc申请并free释放,否则,会造成内存泄漏。
静态分配:在编译时就决定分配多少内存(Text+Data+Bss+Stack)。静态分配的内存在进程结束后由系统释放(Text+Data),栈上的数据在退出函数后立即被销毁。
二、认识普通局部变量、静态局部变量、普通全局变量、静态全局变量
1、普通局部变量
普通的局部变量定义的时候直接定义或者在前面加上auto。
void func1()
{
int i = 1; / /普通局部变量
i++;
printf("i = %d.\n", i); //2
}
int main()
{
fun1();//2
fun1();//2
}
2、静态局部变量
静态局部变量定义时前面加static关键字。
void func1()
{
static int i = 1; //静态局部变量
i++;
printf("i = %d.\n", i);
}
int main()
{
fun1();//2
fun1();//3
}
3、普通全局变量
定义在函数外面的变量,定义前不加任何修饰词。
//a.c
char a = 'A'; // 普通全局变量
//全局函数
void msg()
{
printf("Hello\n");
}
//main.c
int main()
{
extern char a; // extern variable must be declared before use
printf("%c ", a);//'A'
(void)msg();//"Hello"
return 0;
}
4、静态全局变量
定义在函数外面的变量,定义前加任何修饰词static。
//a.c
static char a = 'A'; // 普通全局变量
//全局函数
static void msg()
{
printf("Hello\n");
}
//main.c
int main()
{
extern char a; // 报错,无法在外部应用,static的隐藏性
printf("%c ", a);
(void)msg();// 报错,无法在外部应用,static的隐藏性
return 0;
}
5、跨文件引用全局变量(extern)
就是说,你在一个程序的多个.c源文件中,可以在一个.c文件中定义全局变量g_a,并且可以在别的另一个.c文件中引用该变量g_a(引用前要声明)
函数和全局变量在C语言中可以跨文件引用,也就是说他们的连接范围是全局的,具有文件连接属性,总之意思就是全局变量和函数是可以跨文件看到的(直接影响就是,我在a.c和b.c中各自定义了一个函数func,名字相同但是内容不同,编译报错)。
- 所有未加static前缀的全局变量和全局函数都具有全局可见性,其他源文件也能访问。
- 如果加了static,就会对其他源文件隐藏。C语言利用这一特性可以在不同文件中定义同名函数和同名变量,而不必担心命名冲突。
三、变量及函数的总结
生命周期 | 作用域 | 默认值 | 内存区域 | 链接属性 | |
普通全局变量 | 进程(程序)运行开始创建 进程(程序)结束才销毁 | 从定义开始直到文件末尾 | 0 | 全局(静态)变量区 | 外部的 |
静态全局变量 | 进程(程序)运行开始创建 进程(程序)结束才销毁 | 从定义开始直到文件末尾 | 0 | 全局(静态)变量区 | 内部的 |
普通局部变量 | 进入本函数时创建, 函数退出时销毁 | 函数内部有效 | 随机值 | 栈 |
|
静态局部变量 | 进入本函数时创建, 进程结束才销毁 | 函数内部有效 | 0 | 全局(静态)变量区 |
|
动态内存 | 调用动态内存创建函数时 创建,调用释放函数时销毁 |
| 随机值 | 堆 |
|
普通函数 |
|
|
| 代码区 | 外部的 |
静态函数 |
|
|
| 代码区 | 内部的 |