关键字static、extern、volatile 、const

一、extern
1)当它与"C"一起连用时,如: extern "C" void fun(int a, int b); 则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的,因为C++的多态。
2)当extern不与"C"在一起修饰变量或函数时,如在头文件中: extern int g_Int; 它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数和变量可以在本模块或者其他模块中使用,记住它是一个声明不是定义!也就是说B模块(编译 单元)要是引用模块(编译单元)A中定义的全局变量或函数时,它只要包含A模块的头文件即可, 在编译阶段,模块B虽然找不到该函数或变量,但它不会报错,它会在连接时从模块A生成的目标代码中找到此函数。只在A中定义一次,在B中就可以使用,在内存中只有一份拷贝,在B中没有实际的空间。

如果在A的头文件中extern char g_str[7] = "123456"; // 这个时候相当于没有extern,也就是写与不写一个效果。
如果B在包含A的头文件,那么会编译报错,因为如果B在包含A的头文件的时候相当于又重新定义了一个g_str的全局变量,相当于两个模块的全局变量重名了。最好的办法就是在头文件中,只声明,不定义extern char g_str[7];然后在赋值。所以只在头文件中做声明,真理总是这么简单。

二、static
1)如果static变量声明在A头文件中,如果第二个模块B包含A的头文件,那么其实在两个模块中各自有static的一个拷贝,是不同的内存,(只有在B模块中修改这个静态值的时候才分配空间,这就是copy-on-write,与fork时候很像。)值是可以不同的。所以一般用static一般不写在头文件中(声明定义都不在),这样就不会给其他模块造成不必要的信息污染。因为一般编译器会有优化,如果B里面的static值不改变,那么是不必要分配空间的,而是在改变B中static的时候会分配空间。
2)还有两点要特别注意的,static函数必须访问static变量,不管是不是在类中,static变量默认初始为0。

三、const
1)一个题目
        const int a = 1;
        int *p = (int *)&a;
        *p = 2;
        cout<<a<<endl;
        cout<<*p<<endl;
输出为1和2.其实如果不访问a的地址,那么a实际是不会分配空间的,如果访问并更改a地址空间的值,那么这个时候就会分配一个空间给a。
2)const如果不和extern一起使用,那么const实际是和static一样的,在每个模块中都有一个const的拷贝。
3)还有就是在类中,每个对象都有一个不同的const值,这个值在对象的生命周期是不变的。
4)const char* g_str = "123456" 与 const char g_str[] = "123465"是不同的, 前面那个const 修饰的是char * 而不是g_str,它的g_str并不是常量,它被看做是一个定义了的全局变量(可以被其他编译单元使用), 所以如果你像让char *g_str遵守const的全局常量的规则,最好这么定义const char* const g_str="123456".
例如       
char str[] = "hello";
const char *p = str;//OK,p的意思是,不可以通过p来修改所指向的内容,但是str[0] = 'a';是可以的,所以这个字符串不是常字符串
char str2[] = "world";
p = str2;//OK,这个时候可以改变p的值

char str[] = "hello";
char const *p = str;//OK,但是同样不是个const字符串,而且可以通过p[0] = 'a'来直接修改。
char str2[] = "world";
p = str2;//编译错误,不可以改变p的指向

所以一个指针,的const有两层含义,一个是可不可以改变指针的值,一个是可不可以通过指针改变所指向的值。只有下面的声明才是真正的const字符串:const char str[] = "hello";

四、volatile

这部分参考:http://www.cnblogs.com/chio/archive/2007/11/24/970632.html
这个关键字告诉编译器,不要优化~
volatile int i=10;
int j = i;
...
int k = i;

volatile 告诉编译器i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的可执行码会重新从i的地址读取数据放在k中。 而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在k中。而不是重新从i里面读。这样以来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问,不会出错。

下面是volatile变量的几个例子:
1) 并行设备的硬件寄存器(如:状态寄存器)
2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3) 多线程应用中被几个任务共享的变量
还有几个问题:
1)一个参数既可以是const还可以是volatile吗?解释为什么。
2) 一个指针可以是volatile 吗?解释为什么。
3) 下面的函数有什么错误:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}

下面是答案:
1)是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变(不大懂,这里貌似不是程序修改,是操作系统更改的)。它是const因为程序不应该试图去修改它。
2)是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
3) 这段代码有点变态。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}

“告诉compiler不能做任何优化”其实有常见的两点:
一、赋值优化的时候
   比如要往某一地址送两指令:
   int *ip =...; //设备地址
   *ip = 1; //第一个指令
   *ip = 2; //第二个指令
   以上程序compiler可能做优化而成:
   int *ip = ...;
   *ip = 2;
   结果第一个指令丢失。如果用volatile, compiler就不允许做任何的优化,从而保证程序的原意:
   volatile int *ip = ...;
   *ip = 1;
   *ip = 2;
   即使你要compiler做优化,它也不会把两次付值语句间化为一。它只能做其它的优化。这对device driver程序员很有用。

二、表示用volatile定义的变量会在程序外被改变,每次都必须从内存中读取,而不能把他放在cache或寄存器中重复使用。

   如   volatile char a;  
        a=0;
       while(!a){
  //do some things;  
       }  
       doother();
   如果没有 volatile doother()不会被执行

参考:
http://blog.csdn.net/youmu543/article/details/6204151

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值