存储类、链接和内存管理

        对存储类、内存管理这方面的学习,能够让我们更加的理解程序的一个运行过程,理解程序在运行中的具体机制和它的实现方法。虽然对于写代码本身并没有太大的帮助,但是它能够使得我们更好的理解程序,更容易去判断程序是否能按我们的意愿来实现,或者说让我们纠错更容易的知道是哪里出了问题。

 当然,复习的第一步依然是概念:

1.存储类型

C语言中的存储类型有下面几个关键字:auto、static、register、extern。

auto:自动存储类,由系统自动分配栈存储空间,用户不用操心。这种类型的变量是局部于某个程序范围内的,只能在某个程序范围内使用,一般在函数体内,定义变量的缺省方式就是属于auto类变量。

static:静态存储类,既可以在函数体外也可以在函数体内定义,它在内存中是以固定地址存放的,在整个程序的运行过程中它都不会消失,并且static类型的变量只被初始化一次,且变量的值具有继承性。举个例子:


  1. void func(void)  
  2. {  
  3.      int i = 0;  
  4.      i += 1;  
  5.      printf("%d\n",i);   
  6. }  
  7.  
  8. int main(void)  
  9. {  
  10.      func();  
  11.      func();  
  12.      func();  
  13.      return 0;  

运行结果:1,1,1

但是如果定义是这样的:static int i = 0;结果就不一样了,变成了1,2,3 。我们可以看到加了static关键字之后,每次调用函数func不会重复赋值而是延续上次运算的结果。

register:寄存器存储类型,这种类型是比较牛的,因为不是你写了register关键字它就会变成寄存器类型的变量,这是要看“心情”的,若是“心情”不好就会直接降级成为auto型变量。我们知道寄存器内运行速度是很快的,所以资源就比较宝贵,你定义了这样一个变量之后,编译器会自动判断是否需要满足你。因此对于寄存器变量的定义一般是使用频率比较高的变量。

extern:外部存储类型,用于外部变量的声明,它的作用范围是从定义开始到程序运行结束,它对于变量的类型和值是没有改变的权限的,因为extern关键字是用于声明而不是定义。

声明 定义
可以多次声明 只能定义一次
不分配内存,不可初始化 分配内存空间,可以初始化
  定义是特殊的声明

2.作用域

代码块作用域:在代码块中({.....}之内的变量)定义的变量的作用的范围。如:局部变量,static局部变量。

函数原型作用域:函数声明(int fun(int x),小括号内的范围)使用变量作用的范围。

文件作用域:在所有函数之外定义的变量的作用的范围,又分为本文件作用域(全局变量static,函数static)和多文件作用域(全局变量和函数)。

3.链接

链接类型其实是和作用域对应起来的。

空链接:具有代码块作用域和函数作用域的变量是空链接。

内部链接:具有文件作用域的变量,它可以在一个文件的任何地方使用。

外部链接:具有文件作用域的变量,它可以在一个多文件程序的任何地方使用。

4.存储期

静态存储期,从变量定义开始到整个程序运行结束。

自动存储期:栈里定义的变量(由系统分配维护),程序进入这些变量的代码块时,将为这些变量分配内存,退出时释放内存。

动态存储期:堆里定义的变量,用户自己定义,malloc( )开始到free ( )或者退出代码块结束。

下面的总结和一个例子:


  1. int a;  //全局变量,静态存储期,外部链接,多文件作用域  
  2. static int b;  //全局变量,静态存储期,内部链接,本文件作用域  
  3. void fun(void)  
  4. {  
  5.      int x;  //局部变量,自动存储期,空链接,代码块作用域  
  6.      static int y; //局部变量,静态存储期,空链接,代码块作用域  
  7. }  
  8. int main(void)  
  9. {  
  10.      ....  
  11.      ....  
  12.      return 0  

代码分析比较:


  1. A: B:
  2. int main(void) int main(void)
  3. { {
  4. char *p = "abcdefg"; char p[]="abcdefg";
  5. *p = ‘x’; *p = ‘x’;
  6. printf("%s\n",p); printf("%s\n",p);
  7. return 0; return 0;
  8. } }
  9. //A 错误,B正确。

以上代码为什么会有这样的结果呢?这就是一个存储位置的问题了,代码A中字符串“abcdefg”存储于常量区只读数据段,因此是不能改变它的,试图改变它的结果只有一个段错误;而代码B就不同了,数组元素是在栈中分配内存的,可以进行改变等操作。

网上下载的两个图表:

C语言复习篇之存储类、链接和内存管理

C语言复习篇之存储类、链接和内存管理