变量和函数的属性包括数据类型和数据的存储类别,存储类别指数据在内存中存储方式(静态和动态),包含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;
}
  1. <pre><span style="font-family:Arial,Helvetica,sans-serif; white-space:normal; background-color:rgb(255,255,255)">
  2. </span></pre><pre><span style="font-family:Arial,Helvetica,sans-serif; white-space:normal; background-color:rgb(255,255,255)">用静态变量记录函数被调用的次数:</span></pre><pre><span style="font-family:Arial,Helvetica,sans-serif; white-space:normal; background-color:rgb(255,255,255)">
  3. </span></pre>
#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;
}
  1. <pre></pre>

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 并不支持它, 暂时无法测试. 
 

存储类型auto,static,extern,register的区别 <转>的更多相关文章

  1. 变量存储类型(auto static extern)

    auto 动态存储类型变量(函数内部变量存储默认为 auto型) auto只用于函数内部定义,单片机在执行这个函数时为它分配内存地址,当函数执行完毕返回后,auto变量会被销毁,再次进入这个函数时,它 ...

  2. C语言的存储类型和关键字extern、static

    1.C语言中每个变量都有3个性质:存储期限.作用域.链接 1)存储期限:变量的存储期限决定了为变量预留的内存被释放的时间.共2种,自动存储期限(auto),静态存储期限(static),自动存储(au ...

  3. C和C指针小记(六)-基本声明、指针声明、typedef 、常量、作用域、链接属性、存储类型、static

    1.变量的声明 声明变量的基本形式: 说明符号(一个或者多个) 声明表达式列表 说明符 (specifier) 包含一些关键字,用于描述被声明的标识符的基本类型,它也可用户改变标识符的缺省存储类型和作 ...

  4. c 变量的存储类型auto等(基础知识)和c函数变量

    总结 1).在c语言中每一个变量和函数有两个属性:数据类型和数据的存储类别. 2). 对数据型(如整型.字符型等).存储类别指的是数据在内存中存储的方式. 存储方式分为两大类: 静态存储类和动态存储类 ...

  5. C06 变量和存储类型

    目录 全局变量 局部变量 存储类型 全局变量和局部变量 变量的作用域 作用域:某些事物起作用或有效的区域. 变量的使用范围称为变量的作用域. 变量的作用域决定了变量的可操作性和有效性. C语言变量的作 ...

  6. C语言中【变量】的存储类型共有4种类型

    在C语言中,对变量的存储类型说明有以下四种:   auto          自动变量 (动态存储) register     寄存器变量(动态存储) extern       外部变量(静态存储) ...

  7. C语言存储类型及各存储类型作用域和生存域比较

    c语言中的存储类型有`auto`, `extern`, `register`,`static` 这四种,存储类型说明了该变量要在进程的哪一个段中分配内存空间,可以为变量分配内存存储空间的有数据区.BB ...

  8. C语言存储类型

    看c专家编程,有说存储类型一直不太清楚.看到一篇文章讲解c的存储类型,讲解了c语言中的各种变量的存储类型,而且是从进程.内存的角度讲解的,以前从没有这样理解过,觉得挺有用的,在这里转载过来. 首先要来 ...

  9. C、C++变量auto,static,register,extern类型

    auto: 推导类型变量:编译器选项指示编译器如何使用 auto 关键字来声明变量. 如果指定默认选项 /Zc:auto,编译器从其初始化表达式中推导声明的变量的类型. 如果指定 /Zc:auto-, ...

随机推荐

  1. Protocol Buffer技术详解(语言规范)

    Protocol Buffer技术详解(语言规范) 该系列Blog的内容主体主要源自于Protocol Buffer的官方文档,而代码示例则抽取于当前正在开发的一个公司内部项目的Demo.这样做的目的 ...

  2. SQL关于apply的两种形式cross apply和outer apply(转载)

    SQL 关于apply的两种形式cross apply 和 outer apply   apply有两种形式: cross apply 和 outer apply   先看看语法:   <lef ...

  3. 磕磕碰碰的Chrome之plugin开发

    前言 在Firefox下可用的npapi插件,在chrome下调用时遇到问题,于是尝试研究chrome下的ppapi插件,一路上真是磕磕碰碰,波折不断啊. 阶段一.复用npapi 尝试将npapi直接 ...

  4. 手把手教你Windows下Go语言的环境搭建

    1.想写GO语言首先得下载go语言的开发包 官方下载地址:https://code.google.com/p/go/downloads/list 我用的是Win7 64位的操作系统,截图如下: 2.把 ...

  5. 批量删除Kindle library 中的不想要的书籍

    这是一个书签形式的脚本  有全选  批量删除什么的   亚马逊本身的删除太麻烦了 网上转的  原帖在这里http://tieba.baidu.com/p/2249582757 改进版本http://t ...

  6. Paragraph 对象&#39;代表所选内容、范围或文档中的一个段落。Paragraph 对象是 Paragraphs 集合的一个成员。Paragraphs 集合包含所选内容、范围或文档中的所有段落。

    Paragraph 对象'代表所选内容.范围或文档中的一个段落.Paragraph 对象是 Paragraphs 集合的一个成员.Paragraphs 集合包含所选内容.范围或文档中的所有段落. 方法 ...

  7. Nestjs 上传文件

    Docs: https://docs.nestjs.com/techniques/file-upload 上传单文件 @Post('upload') @UseInterceptors(FileInte ...

  8. php缓存机制

    1.全页面静态化缓存 <?php ob_start();//如果php.ini已经开启,那么这里会开启一个新的输出缓冲区; echo "<!DOCTYPE html>< ...

  9. Golang 操作mysql使用举例---连接本地数据库

    连接数据库的方式有两种:TCP和Unix域socket. 本文使用Unix domain sockets连接数据库.关于TCP连接数据库可以参考Go 操作mysql使用举例 下面例子中,演示了使用sh ...

  10. 项目管理---git----遇到问题------.gitignore不起作用

    情况 在管理一个版本库时,有时候不想要管理某些文件,这个时候我就把这个问价写到.gitignore文件中,这样应该就可以将这个文件忽略,不再进行·版本管理了,但是经常出现的情况是:将这些文件名写到其中 ...