一 变量的类型:
1. 全局变量:代码块之外的变量,全局变量一般是在.c中定义的,在.h用extern关键字声明的(定义只能是一次,声明是可以多次),如果其他.c文件想用就可以调用这个.h就行。还有另外几种方法来实现全局变量在多个.c文件共享。
2. 静态变量:static关键字修饰的变量,静态变量有2种,一种是代码块之外的静态变量,这个是变量的只是本文件可用,代码块之内的静态变量,只能被代码块内所使用
3.局部变量:代码块内部的变量,没有任何的修饰符,实际这些变量的隐藏的修饰符是auto
变量的一个很重要的性质作用域与内存的管理有很大的关系,作用域就是这个变量的生命周期、存在时间。
二 函数:函数默认都是全局的,当然也可以用static变量修饰,那么这个函数成了仅本文件有效。
三 内存管理:
每一个应用程序都把所占用的内存分为了几个区域:下面就是分配示意图
#include <stdio.h> #include <string.h> #include <stdlib.h> int a = 10;//已经初始化的全局变量,数据段 int h;//未初始化的全局变量,bss段 static int b = 15;//全局static变量,数据段 int test(int x, int y); int main(void) { int i = 0;//局部变量,栈 printf("&a = %p, a = %d, &b = %p, b = %d, &i = %p, i = %d, &h = %p, h = %d\n", &a, a, &b, b, &i, i, &h, h); int c = 1; printf("&c = %p, c = %d\n", &c, c); char *s ="hello";//s 存放栈,hello常量,数据段,所以s指向的内存在数据段 char *p = (char*)malloc(10);//堆 printf("&p = %p, p = %d, p = %s\n", &p, p, p); if (p != NULL) { strcpy(p,s); } printf("&p = %p, p = %d, %s\n", &p, p, p); printf("c = %p, %s\n", s, s); i = test(a,c); printf("&i = %p, i = %d\n", &i, i); i = test(a,c); printf("&i = %p, i = %d\n", &i, i); if (p != NULL)//下面这段比较容易被忽视,但 { free(p); printf("&p = %p, p = %d, %s\n", &p, p, p); p = NULL; printf("&p = %p, p = %d, %s\n", &p, p, p); } return 0; } int test(int x, int y) { static int d = 0;//数据段 printf("&d = %p, d = %d\n", &d, d); int e = 2; d += (x -y -e); return d; }
这些内存区分别都存了那些数据呢
1.代码段:存放cpu要执行的指令,通常都是只读,防止被修改。
2.数据段:已经初始化的全局变量,常量,静态变量。
3.bss段:未初始化的全局变量和未初始化的静态变量。
4.栈:是由编译器自动分配和释放的,存放的是一些局部变量和函数的参数。地址是向下生长的。每当函数被调用的时候,会把函数的返回地址和一些调用信息存放在栈区,再为函数的临时变量和参数在栈区申请内存。函数返回时,这些临时变量和参数的内存就会别销毁。
5堆:程序员自行分配和释放,不自行释放就会造成一些意想不到的bug,地址是向上生长的。
分了这么多区,编译器是基于什么呢,代码都是顺序执行,通过递归和跳转会重复执行一些代码,因为一些代码块会被重复使用,所以代码块里面申请的变量都放在栈里面,全局变量和静态变量一般在整个程序运行时期都要被访问。这样就可以节约内存。