extern

extern的两个作用:

  • 修饰变量或函数,提示编译器此变量或函数是在其它文件中定义的,但要在此处引用;
  • 进行链接指定,如: extern "C" void fun(int a, int b);  告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的,;

static

static的主要作用:

  • static局部变量,静态变量,函数返回时不会销毁;
  • static全局变量,仅在当前文件可见;
  • static函数,仅在当前文件可见;
  • 修饰类成员,静态成员为所有对象所有,不属于某个特定的对象;static成员函数没有this指针,只能访问类的static成员变量;

另外,static变量如果没有初始化,会自动用0填充;

例子: 用static成员实现singleton模式,

class Singleton{
public:
static Singleton* getInstance() {
if(!sg) {
sg = new Singleton(); //创建实例,但在哪里释放呢?
}
return sg;
} private:
Singleton() { } //构造函数声明为私有函数
static Singleton *sg; //私有静态变量
}; //初始化静态变量sg为0,不指向任何对象
Singleton* Singleton::sg = NULL; int main(){
Singleton *sg = Singleton::getInstance(); return ;
}

restrict

restrict 仅能修饰指针,用于告诉编译器只能通过该指针修改其指向的对象(内存区域),也就是说restrict指针将独占所指的内存,所有修改都得通过这个指针来,编译器可以放心大胆地把这片内存中前若干字节用寄存器cache起来。

例如下面两个C库函数,都是从s2指向的位置复制n字节数据到s1指向的位置,且均返回s1的值。两者之间的差别由关键字restrict造成,即memcpy假定两个内存区域没有重叠;memmove函数则不做这个假定。

void * memcpy(void * restrict s1, const void * restrict s2, size_t n);
void * memove(void * s1, const void * s2, size_t n);

volatile

volatile 变量是随时可能发生变化的,编译器不要对其进行优化,以免出错。

例如:

int i=;
int j = i;
...
int k = i;

由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在k中,而不是重新从i里面读。

又例如在嵌入式编程中,需要往某地址发送两条指令:

int *ip =...;     //设备地址
*ip = ; //第一个指令
*ip = ; //第二个指令

以上代码可能会被编译器优化成:

int *ip = ...;
*ip = ;

导致第一条指令丢失。

如果用volatile关键字声明变量,就不允许编译器做优化:

volatile int *ip =...;     //设备地址
*ip = ; //第一个指令
*ip = ; //第二个指令

定义为 volatile 的变量是说该变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。准确地说就是,编译器在用到volatile变量时必须从内存读取,而不能使用保存在寄存器里的值。

下面是volatile变量的几个例子:

  1. 并行设备的硬件寄存器(如:状态寄存器);
  2. 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables);
  3. 多线程应用中的共享变量;

volatile在多线程环境中的应用,可以先看下面的例子:

volatile long int n = ;

void foo()
{
int i;
for(i=; i<; i++) {
n++;
}
} #define N 100 int main()
{
int k;
pthread_t th[N]; for (k=; k<N; k++) {
pthread_create(&th[k], NULL, (void*)foo, NULL);
} for (k=; k<N; k++) {
pthread_join(th[k], NULL);
} printf("n=%ld\n", n);
return ;
}

并发100个线程,每个线程对共享变量n累加1万次,理论上最后的结果应该是100w,但代码实际结果却是比这个更小的一个随机值。

我们知道volatile修饰的变量在每次被线程访问时,都强迫从内存中重读该变量的值。而且,当变量值发生变化时,强迫线程将变化值回写到内存。这样在任何时刻,两个不同的线程总是看到某个变量的同一个值。

既然如此,为何每次结果还是不同呢,这是因为n++操作并非原子性,也就是说volatile不能保证所修饰的变量进行原子操作。

以汇编过程来看自增操作:

mov        eax,dword ptr [ebp-]     ;把i的值mov到eax寄存器
add eax, ;自加
mov dword ptr [ebp-],eax ;再存放至i变量中

分为3个步骤:

1)读取volatile变量值到寄存器;

2)增加寄存器的值;

3)把寄存器的值回写内存;

可见,如果线程A在步骤2、3之间被另一个线程B抢占,线程B对共享变量完成一次自增,等到线程A继续完成步骤3时,就丢失了线程B的这次操作。

要解决上面的多线程如何正确使用共享变量的问题,通常需要加锁,或者使用原子变量。

先来看X86架构下面原子变量的定义:

typedef struct {
volatile int counter;
} atomic_t;

原子类型其实是 int 类型,只是使用volatile禁止寄存器对其暂存。

原子操作的API:

atomic_read(atomic_t * v);                   // v
atomic_set(atomic_t * v, int i); // v = i
void atomic_add(int i, atomic_t *v); // v += i
void atomic_sub(int i, atomic_t *v); // v -= i
void atomic_inc(atomic_t *v); // v++
void atomic_dec(atomic_t *v); // v--
int atomic_dec_and_test(atomic_t *v); // (--v)==0?true:false
int atomic_inc_and_test(atomic_t *v); // (++v)==0?true:false
int atomic_sub_and_test(int i, atomic_t *v); // (v-i)==0?true:false
int atomic_add_negative(int i, atomic_t *v); // (v+i)<0?true:false
int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t * v);
int atomic_dec_return(atomic_t * v);

extern、static、restrict、volatile 关键字的更多相关文章

  1. const,static,volatile关键字的作用

    const关键字: 1.欲阻止一个变量被改变,可使用const,在定义该const变量时,需先初始化,以后就没有机会改变他了: 2.对指针而言,可以指定指针本身为const,也可以指定指针所指的数据为 ...

  2. 深入理解static、volatile关键字

    static 意思是静态的,全局的.被修饰的东西在一定范围内是共享的,被类的所有实例共享,这时候需要注意并发读写的问题. 只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内找到他们. ...

  3. Java 中 static 和 volatile 关键字的区别?

    static指的是类的静态成员,实例间共享 volatile跟Java的内存模型有关,线程执行时会将变量从主内存加载到线程工作内存,建立一个副本,在某个时刻写回.valatile指的每次都读取主内存的 ...

  4. const extern static 终极指南

    const extern static 终极指南 不管是从事哪种语言的开发工作,const extern static 这三个关键字的用法和原理都是我们必须明白的.本文将对此做出非常详细的讲解. co ...

  5. Java Volatile关键字 以及long,double在多线程中的应用

    概念: volatile关键字,官方解释:volatile可以保证可见性.顺序性.一致性. 可见性:volatile修饰的对象在加载时会告知JVM,对象在CPU的缓存上对多个线程是同时可见的. 顺序性 ...

  6. java中 static,final,transient,volatile关键字的作用

    static 和final static  静态修饰关键字,可以修饰 变量,程序块,类的方法: 当你定义一个static的变量的时候jvm会将将其分配在内存堆上,所有程序对它的引用都会指向这一个地址而 ...

  7. 也来说说C/C++里的volatile关键字

    去年年底的样子,何登成写了一篇关于C/C++ volatile关键字的深度剖析blog(C/C++ Volatile关键词深度剖析).全文深入分析了volatile关键字的三个特性.这里不想就已有内容 ...

  8. Java并发编程:volatile关键字解析

    Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在 ...

  9. volatile关键字 学习记录2

    public class VolatileTest2 implements Runnable{ volatile int resource = 0; public static void main(S ...

随机推荐

  1. python图片和分形树

    链接: 这10个Python项目很有趣! Python 绘制分形图(曼德勃罗集.分形树叶.科赫曲线.分形龙.谢尔宾斯基三角等)附代码 使用Python生成树形图案 神奇的代码:用 Python 生成分 ...

  2. 版本适配 sdk version MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  3. Redis进阶之redis的生命周期

    D:\Redis-x64-3.2.100>redis-cli.exe -h 127.0.0.1 -p 6379127.0.0.1:6379> set aa "123"( ...

  4. Nginx软件优化

    1.1 Nginx优化分类 安全优化(提升网站安全性配置) 性能优化(提升用户访问网站效率) 1.2 Nginx安全优化 1.2.1 隐藏nginx版本信息优化 官方配置参数说明:http://ngi ...

  5. virt-manager中为centos 7.2 扩容根分区

    1. 打开virt-manager,添加一块磁盘. Add Hardware --> 选中Storage --> Manager (操作参考下图) 点击Manager之后,弹出Choose ...

  6. iCloud无法导入vCard问题。fix the error when import vcard/vcf to icloud.

    问题描述:当登录icloud.com,进入通讯录的时候,导入VCF格式的联系人的时候会报错.如图: 1.从outlook的联系人中选一个联系人,导出联系人卡片-vCard文件 (如果是塞班手机,可以用 ...

  7. MySQL 各级别事务的实现机制

    MySQL 各级别事务的实现机制在处理cnctp项目已合包裹状态同步的问题时,发现读包裹状态和对包裹状态的更新不在一个事务内,我提出是否会因为消息并发导致状态一致性问题.在和同事讨论的过程中,我们开始 ...

  8. 高性能Javascript(1)

    第一部分 加载与运行 <html> <head> <title>Script Example</title> </head> <bod ...

  9. C# 使用NLog记录日志入门操作

    环境:win7 64位, VS2010 1.首先用VS2010创建命令行工程NLogDemo 2.在程序包管理器控制台中输入:Install-Package NLog -Version 4.4.12 ...

  10. jQuery的deferred对象详解(转)

    jQuery的开发速度很快,几乎每半年一个大版本,每两个月一个小版本. 每个版本都会引入一些新功能.今天我想介绍的,就是从jQuery 1.5.0版本开始引入的一个新功能----deferred对象. ...