变量和函数的属性包括数据类型和数据的存储类别,存储类别指数据在内存中存储方式(静态和动态),包含auto,static,register,extern四种

内存中。具体点来说内存分为三块:静态区,堆区,栈区。外部变量和全局变量存放在静态区,局部变量存放在栈区,动态开辟的内存存在堆区。 

一、简要性比较

extern   外部变量声明,是指这是一个已在别的地方定义过的对象这里只是对变量的一次重复引用,不会产生新的变量。 
static   静态数据,数据存放在全局数据区,但作用域只是本 文件/函数 中,所以你可以在两个不同的文件/函数内部申明同名的static变量,但是           它们是两个不同的全局变量。 如果是定义在函数内,那么该对象具有无链接,函数外不能对其访问。如果是定义在函数外,那么该对象具          有内部链接,其它程序文件不能对其访问
auto     普通局部栈变量,是自动存储,这种对象会自动创建和销毁 ,建议这个变量要放在堆栈上面,调用函数时分配内存,函数结束时释放内            存。一般隐藏auto默认为自动存储类别。我们程序都变量大多是自动变量。
register 寄存器变量,
请求编译器将这个变量保存在CPU的寄存器中,从而加快程序的运行.

          系统的寄存器是有限制的,声明变量时如:register int i.这种存储类型可以用于频繁使用的变量

二、补充注意点

1、auto存储类型的变量只能在某个程序范围内使用,他采用堆栈的方式分配空间。

   在定义变量时,auto是可以省略的,如:auto   int   i;等效于int   i.

2、全局变量不允许声明为auto变量,register不适用于全局变量。

3、register是不能取址的。比如 int i;(自动为auto)int *p=&i;是对的, 但register int j; int *p = &j;是错的,因为无法对寄存器的定址。

4.声明变量为register,编译器并不一定会将它处理为寄存器变量,毕竟,资源是有限的。

5、实际上现在一般的编译器都忽略auto和register申明,现在的编译器自己能够区分最好将那些变量放置在寄存器中,那些放置在堆栈中;甚至于将一些变量有时存放在堆栈,有时存放在寄存器中。

6.auto register 是用来修饰变量的,static extern 变量函数都可以

三、详细说明

从变量值存在时间(生存期)角度分:静态存储方式和动态存储方式。

从变量的作用域(空间)角度分:全局变量和局部变量。

在用户区的存储空间:

|程序区    |

|静态存储区|

|动态存储器|

静态存储方式程序运行期间系统分配固定存储空间,动态存储方式根据需求动态分配存储空间。全局变量都存放于静态存储区,动态存储区主要包括:函数的形参,在函数调用时分配存储空间;自动变量(未加static声明的局部变量);函数调用时的现场保护和返回地址等。

Auto自动变量:一般隐藏auto默认为自动存储类别。我们程序都变量大多是自动变量。

Static变量:static声明的局部变量在函数调用结束后不释放存储空间,再次调用函数时该变量已经有值。其他函数时不能引用它的。Static局部变量时在编译赋初值,自动变量的初值是函数调用时赋的。所以局部变量不赋初始值的话static变量自动赋值0或在空字符。而自动变量的初值则是不确定的。最好不要直接使用。函数中需要保留上次调用时的值或者初始化之后变量只被引用而不改变的可以考虑用static局部变量,这样比较耗内存。

Register变量:动态和静态变量都是存放在内存中,程序中遇到该值时用控制器发指令将变量的值送到运算器中,需要存数再保存到内存中。如果频繁使用一个变量,比如一个函数体内的多次循环每次都引用该局部变量,我们则可以把局部变量的值放到CPU的寄存器中,叫寄存器变量。不需要多次到内存中存取提高效率。但是只能局部自动变量和形参可以做寄存器变量。在函数调用时占用一些寄存器,函数结束时释放。不同系统对register要求也不一样,比如对定义register变量个数,数据类型等限制,有的默认为自动变量处理。所以在程序一般也不用。

Extern外部变量:如果我们希望该外部变量只能在本文件内使用,而不能被其他文件引用可以在外部变量定义时加static声明。防止别人写的模块误用。在函数外部定义的全局变量,作用域开始于变量定义,结束于程序文件的结束。我们可以extern来声明外部变量来扩展它的作用域。同一个文件内,extern声明之后就可以作用域扩大到声明处到文件结束。比如在一个函数之后定义外部变量a,之后的函数可以使用该变量,但是之前的函数不能使用,加extern可以解决。多个文件时,可以在未定义该外部变量的文件内做extern声明即可以使用。但是需要注意可能执行一个文件时改变了该全局变量的值,影响其他文件的调用。编译时遇到extern,会先在文件内找是否定义了该外部变量。如果未找到则在链接时在其他文件中找。

四、示例

1、extern:

 extern的对象都具有静态生命周期。 使用extern时,注意不能重复定义,否则编译报错,如:
程序文件一:
extern int a = 10; //编译警告,extern的变量最好不要初始化
程序文件二:
extern int a = 20; //重复定义,应改为extern int a;

一般最好这样,如果需要初始化,可把extern修饰符去掉(但也不要重复定义),另外如果其它程序文件也需要用到该变量,可用extern来声明该变量。
2.
1. 局部变量: 也叫自动变量, 它声明在函数开始, 生存于栈, 它的生命随着函数返回而结束.
#include <stdio.h>

int main(void)
{
    auto int i = 9; /* 声明局部变量的关键字是 auto; 因可以省略, 几乎没人使用 */   
    printf("%d\n", i);  
    getchar();
    return 0;
}



2. 全局变量: 声明在函数体外, 一般应在函数前; 每个函数都可以使用它, 不过全局变量应尽量少用.
#include <stdio.h>

void add(void);
void mul(void);
int gi = 3; /* 全局变量 */
int main(void)
{    
    printf("%d\n", gi);  /* 3  */
    add();
    printf("%d\n", gi);  /* 5  */
    mul();
    printf("%d\n", gi);  /* 10 */
      
    getchar();
    return 0;
}
void add(void) {
    gi += 2;
}
void mul(void) {
    gi *= 2;
}
全局变量会被初始化为空, 而局部变量在没有赋值前是一个垃圾值:
#include <stdio.h>

int gi; /* 全局变量 */
int main(void)
{
    int i; /* 句柄变量 */
    
    printf("%d, %d\n", gi, i);
      
    getchar();
    return 0;
}

当全局变量与局部变量重名时, 使用的是局部变量:
#include <stdio.h>

int a = 111, b = 222;
int main(void)
{
    int a = 123;
    printf("%d,%d\n", a, b); /* 123,222*/
    getchar();    
    return 0;
}

3. static 关键字: 修饰的局部变量是静态局部变量; 静态局部变量存值如同全局变量, 区别在于它只属于拥有它的函数; 它也会被初始化为空.
#include <stdio.h>

void fun1(void);
void fun2(void);
int main(void)
{    
    int i;
    
    for (i = 0; i < 10; i++) fun1();
    printf("---\n");
    for (i = 0; i < 10; i++) fun2();
    
    getchar();
    return 0;
}
void fun1(void) {
    int n = 0;        /* 一般的局部变量 */
    printf("%d\n", n++);
}
void fun2(void) {
    static int n;     /* 静态局部变量; 会被初始化为空 */
    printf("%d\n", n++);
}

用 static 关键字修饰的全局变量是静态全局变量; 静态全局变量只能用于定义它的单元.
//譬如在 File1.c 里面定义了:
static int num = 99;  /* 去掉前面的 static 其他单元才可以使用 */
//在 File2.c 里使用:
#include <stdio.h>

extern int num;
int main(void)
{    
    printf("%d\n", num);
 
    getchar();
    return 0;
}
用静态变量记录函数被调用的次数:
#include <stdio.h>
int fun(void);int main(void){ int i; for (i = 0; i < 10; i++) { printf("函数被调用了 %2d 次;\n", fun()); } getchar(); return 0;}int fun(void) { static int n; return ++n;}

4. 寄存器变量(register):寄存器变量会尽量把变量放到寄存器(而不是栈或堆), 从而加快存取速度
#include <stdio.h>
#include <time.h>

#define TIME 1000000000

int m, n = TIME; /* 全局变量 */
int main(void)
{   
    time_t start, stop;
    register int a, b = TIME; /* 寄存器变量 */
    int x, y = TIME;          /* 一般变量   */
    
    time(&start);
    for (a = 0; a < b; a++);
    time(&stop);
    printf("寄存器变量用时: %d 秒\n", stop - start);
    time(&start);
    for (x = 0; x < y; x++);
    time(&stop);
    printf("一般变量用时: %d 秒\n", stop - start);
    time(&start);
    for (m = 0; m < n; m++);
    time(&stop);
    printf("全局变量用时: %d 秒\n", stop - start);
 
    getchar();
    return 0;
}


4. extern 关键字:
使用外部全局变量应该用 extern 重新声明一下;
如果不使用 extern, 将是重新定义;
在链接阶段, 同一程序中不同单元的全局变量是共用的所以不能重新定义;
本例为了省事并没有涉及另一个文件, 只是把变量放在文件尾模拟了一下.

另外, extern 主要还是用于函数.
#include <stdio.h>

extern int g1;
int main(void)
{    
    extern int g2;
    printf("%d,%d\n", g1,g2);  
    getchar();
    return 0;
}
int g1 = 77;
int g2 = 88;

5. volatile 关键字:

程序在使用变量时, 特别是连续多次使用变量时, 一般是载入寄存器, 直接从寄存器存取, 之后再还回内存;
但如果此变量在返回内存时, 假如内存中的值已经改变了(从外部修改了)怎么办?
为了避免这种情况的发生, 可以用 volatile 说明此变量, 以保证变量的每次使用都是直接从内存存取.
但这样肯定会影响效率, 幸好它并不常用.

另外: 如果 const volatile 同时使用, 这表示此变量只接受外部的修改.
#include <stdio.h>

volatile int num = 123;
int main(void)
{    
    printf("%d\n", num);
    getchar();
    return 0;
}

6. restrict 关键字:

restrict 有点和 volatile 相反; 不过 restrict 只是对指针.
好像 C++Builder 2009 并不支持它, 暂时无法测试.