您的位置:首页 > 其它

几个常见关键字auto,overlay,static,register,const,volatile,extern

2010-10-19 19:55 507 查看
几个常见关键字auto,overlay,static,register,const,volatile,extern
(1) auto

  这个这个关键字用于声明变量的生存期为自动,即将不在任何类、结构、枚举、联合和函数中定义的变量视为全局变量,而在函数中定义的变量视为局部变量。所有的变量默认就是auto的。 auto 型参数以从右到左的顺序压入软件堆栈。对于多字节数据来说,首先将低字节压入软件堆栈。

      下面给出一个 C 函数的原型: char add (auto char x, auto char y); 要用值 x = 0x61 和 y = 0x65调用函数add ,必须先将 y 的值压入软件堆栈,然后将 x 的值压入软件堆栈。由于返回值长度为 8 位,所以返回到 WREG 中。

      下面给出一个 C 函数的原型: int sub (auto int x, auto int y); 要用值 x = 0x7861 和 y = 0x1265调用函数 sub,必须先将 y 的值压入软件堆栈,然后将 x的值压入软件堆栈。由于返回值长度为 16 位,所以返回到 PRODH:PRODL 中。

(2)overlay

      overlay存储类别可用于局部变量 (但不能用于形式参数、函数定义或全局变量)。 overlay 存储类别将相关变量分配到一个特定于函数的静态重叠存储区。这种变量是静态分配存储空间的,但每次进入函数时都要被初始化。例如: void f (void) { overlay int x = 5; x++; } 尽管 x 的存储空间是静态分配的,x 在每次进入函数时都会被初始化为 5。如果没有初始化,那么进入函数时其值是不确定的。 MPLINK 链接器将使不同时运行的函数中定义为 overlay的局部变量 共享存储空间。例如,在下面的函数中: int f (void) { overlay int x = 1; return x; } int g (void) { overlay int y = 2; return y; } 如果 f 和 g 永远不会同时运行,则 x 和 y共享相同的存储空间。但是,在下面的函数中: int f (void) { overlay int x = 1; return x; } int g (void) { overlay int y = 2; y = f ( ); return y; } 由于 f 和 g 可能会同时运行, x和 y 不能共享相同的存储空间。 使用overlay 局部变量的优点是,其存储空间是静态分配的,也就是说,在一般情况下,存取这种变量所需要的指令较少(因此所生成代码占用的程序存储空间也较小)。 同时,由于一些变量可以共享相同的存储空间,这些变量所需分配的总的数据存储空间比定义为static时要小。 auto占用空间小,但速度慢;static速度快,但占用空间大。overlay作为Macrochip的杰作,占用空间较小,速度确不比static慢。

(3)static

  见http://hi.baidu.com/mooncbz/blog/item/99f3c0823d8ccda50df4d26e.html

       常见的两种用途:

    1>统计函数被调用的次数;

    2>减少局部数组建立和赋值的开销。

      变量的建立和赋值是需要一定的处理器开销的,特别是数组等含有较多元素的存储类型。在一些含有较多的变量并且被经常调用的函数中,可以将一些数组声明为static类型,以减少建立或者初始化这些变量的开销。定义:在局部变量前加上static关键字时,就定义了静态局部变量。

      特点:

A、该变量在全局数据区分配内存。

B、初始化:如果不显式初始化,那么将被隐式初始化为0。  

C、它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或 语句块结束时,其作用域随之结束。   

      详细说明:

    1>、变量会被放在程序的全局存储区中,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与堆栈变量和堆变量的区别。

    2>、变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。

    3>、当static用来修饰全局变量时,它就改变了全局变量的作用域,使其不能被别的程序extern,限制在了当前文件里,但是没有改变其存放位置,还是在全局静态储存区。   

      使用注意:

    1>若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度;

    2>若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度;

    3>设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题(只要输入数据相同就应产生相同的输出)。

(4)register

  这个关键字命令编译器尽可能的将变量存在CPU内部寄存器中而不是通过内存寻址访问以提高效率。

(5)const

  被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。它可以修饰函数的参数、返回值,甚至函数的定义体。

 作用:

    1>修饰输入参数

      a.对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const引用传递”,目的是提高效率。例如将void Func(A a) 改为void Func(const A &a)。

      b.对于内部数据类型的输入参数,不要将“值传递”的方式改为“const引用传递”。否则既达不到提高效率的目的,又降低函数的可理解性。如void Func(int x) 不应该改为void Func(const int &x)。

    2>用const修饰函数的返回值

      a.如果给以“指针传递”方式的函数返回值加const修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const修饰的同类型指针。如对于: const char * GetString(void);        如下语句将出现编译错误:char *str = GetString();//cannot convert from 'const char *' to 'char *';        正确的用法是:const char *str = GetString();

      b.如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const修饰没有任何价值。 如不要把函数int GetInt(void) 写成const int GetInt(void)。

    3>const成员函数的声明中,const关键字只能放在函数声明的尾部,表示该类成员不修改对象.

 说明:

   const type m; //修饰m为不可改变

   示例:

typedef char * pStr; //新的类型pStr;

char string[4] = "abc";

const char *p1 = string;     

p1++; //正确,上边修饰的是*p1,p1可变     

const pStr p2 = string;     

p2++; //错误,上边修饰的是p2,p2不可变,*p2可变

   同理,const修饰指针时用此原则判断就不会混淆了。     

const int *value; //*value不可变,value可变     

int* const value; //value不可变,*value可变     

const (int *) value; //(int *)是一种type,value不可变,*value可变,逻辑上这样理解,编译不能通过,需要

tydef int* NewType;     

const int* const value;//*value,value都不可变

(6)volatile

  表明某个变量的值可能在外部被改变,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。它可以适用于基础类型如:int,char,long......也适用于C的结构和C++的类。当对结构或者类对象使用volatile修饰的时候,结构或者类的所有成员都会被视为volatile. 该关键字在多线程环境下经常使用,因为在编写多线程的程序时,同一个变量可能被多个线程修改,而程序通过该变量同步各个线程。

  简单示例:

   DWORD __stdcall threadFunc(LPVOID signal)

   {   int* intSignal=reinterpret_cast(signal);

     *intSignal=2;

     while(*intSignal!=1) sleep(1000);

     return 0;

          }   该线程启动时将intSignal 置为2,然后循环等待直到intSignal 为1 时退出。显然intSignal的值必须在外部被改变,否则该线程不会退出。但是实际运行的时候该线程却不会退出,即使在外部将它的值改为1,看一下对应的伪汇编代码就明白了:mov ax,signal      label:  if(ax!=1) goto label 对于C编译器来说,它并不知道这个值会被其他线程修改。自然就把它cache在寄存器里面。C 编译器是没有线程概念的,这时候就需要用到volatile。volatile 的本意是指:这个值可能会在当前线程外部被改变。也就是说,我们要在threadFunc中的intSignal前面加上volatile关键字,这时候,编译器知道该变量的值会在外部改变,因此每次访问该变量时会重新读取,所作的循环变为如下面伪码所示:label: mov ax,signal   if(ax!=1)      goto label   

注意:一个参数既可以是const同时是volatile,是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

(7)extern

       extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。 extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字.

(1) 对于extern变量来说,仅仅是一个变量的声明,其并不是在定义分配内存空间。如果该变量定义多次,会有连接错误。我的理解是只要变量或者函数是在别的文件中定义的,那么要调用的话,一律要在调用前加上extern的申明。

(2) extern 表示将变量或函数声明为外部连接; 变量默认是内部链接,函数默认是外部链接,因此用来外部连接的函数,声明时有无extern都可以连接通过..而全局变量则不行..如是外部文件中定义的,一定需要声明成extern。与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。 extern 是个提前说明,如果所定义的函数 void test(void);它的定义在调用之前的话那么有无extern 都是一样的,但是如果函数定义在调用只后的话就的加extern来声明

(3) 通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。也就是说c文件里面定义,如果该函数或者变量与开放给外面,则在h文件中用extern加以声明。所以外部文件只用include该h文件就可以了。而且编译阶段,外面是找不到该函数的,但是不报错。link阶段会从定义模块生成的目标代码中找到此函数。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐