今天在看S3C2440开发板的初始化代码时,对#define A (* (volatile unsigned long *) 0x48000000这种形式的定义方式有困惑,于是求助GOOGLE大神,在网上搜到了一些文章,觉得以下三篇文章对理解这个有些作用:

文章一:
    对于不同的计算机体系结构,设备可能是端口映射,也可能是内存映射的。如果系统结构支持独立的IO地址空间,并且是端口映射,就必须使用汇编语言完成实际对设备的控制,因为C语言并没有提供真正的“端口”的概念。如果是内存映射,那就方便的多了。
    以 #define IOPIN   (*((volatile unsigned long *) 0xE0028000))为例:作为一个宏定义语句,define是定义一个变量或常量的伪指令。首先( volatile unsigned long * )的意思是将后面的那个地址强制转换成 volatile unsigned long * ,unsigned long * 是无符号长整形,volatile 是一个类型限定符,如const一样,当使用volatile限定时,表示这个变量是依赖系统实现的,以为着这个变量会被其他程序或者计算机硬件修改,由于地址依赖于硬件,volatile就表示他的值会依赖于硬件。
    volatile 类型是这样的,其数据确实可能在未知的情况下发生变化。比如,硬件设备的终端更改了它,现在硬件设备往往也有自己的私有内存地址,比如显存,他们一般是通过映象的方式,反映到一段特定的内存地址当中,这样,在某些条件下,程序就可以直接访问这些私有内存了。另外,比如共享的内存地址,多个程序都对它操作的时候。你的程序并不知道,这个内存何时被改变了。如果不加这个voliatile修饰,程序是利用catch当中的数据,那个可能是过时的了,加了 voliatile,就在需要用的时候,程序重新去那个地址去提取,保证是最新的。归纳起来如下:
1. volatile变量可变 允许除了程序之外的比如硬件来修改他的内容;
2. 访问该数据任何时候都会直接访问该地址处内容,即通过cache提高访问速度的优化被取消;
     对于((volatile unsigned long *) 0xE0028000)为随硬件需要定义的一种地址,前面加上“*”指针,为直接指向该地址,整个定义约定符号IOPIN代替,调用的时候直接对指向的地址寄存器写内容既可。这实际上就是内存映射机制的方便性了。其中volatile关键字是嵌入式系统开发的一个重要特点。上述表达式拆开来分析,首先(volatile unsigned long *) 0xE0028000的意思是把0xE0028000强制转换成volatile unsigned long类型的指针,暂记为p,那么就是#define A *p,即A为P指针指向位置的内容了。这里就是通过内存寻址访问到寄存器A,可以读/写操作。
    对于(volatile unsigned char *)0x20我们再分析一下,它是由两部分组成: 
1)(unsigned char *)0x20,0x20只是个值,前面加(unsigned char *)表示0x20是个地址,而且这个地址类型是unsigned char ,意思是说读写这个地址时,要写进unsigned char 的值,读出也是unsigned char 。 
2)volatile,关键字volatile 确保本条指令不会因C 编译器的优化而被省略,且要求每次直接读值。例如用 while((unsigned char *)0x20)时,有时系统可能不真正去读0x20的值,而是用第一次读出的值,如果这样,那这个循环可能是个死循环。用了volatile 则要求每次都去读0x20的实际值。 那么(volatile unsigned char *)0x20是一个固定的指针,是不可变的,不是变量。而char  *u则是个指针变量。再在前面加"*":*(volatile unsigned char *)0x20则变成了变量(普通的unsigned char变量,不是指针变量),如果#define i (*(volatile unsigned char *)0x20),那么与unsigned char i是一样了,只不过前面的i的地址是固定的。
那么你的问题就可解答了,(*(volatile unsigned char *)0x20)可看作是一个普通变量,这个变量有固定的地址,指向0x20。而0x20只是个常量,不是指针更不是变量。

文章二:
对于不同的计算机体系结构,设备可能是端口映射,也可能是内存映射的。如果系统结构支持独立的IO地址空间,并且是端口映射,就必须使用汇编语言完成实际对设备的控制,因为C语言并没有提供真正的“端口”的概念。如果是内存映射,那就方便的多了。
    举个例子,比如像寄存器A(地址假定为0x48000000)写入数据0x01,那么就可以这样设置了。
#define A (*(volatile unsigned long *) 0x48000000 )
...
     A = 0x01;
...
    这实际上就是内存映射机制的方便性了。其中volatile关键字是嵌入式系统开发的一个重要特点。volatile(可变的)这个关键字说明这变量可能会被意想不到地改变,这样编译器就不会去假设这个变量的值了。这种“意想不到地改变”,不是由程序去改变,而是由硬件去改变。
    volatile 限定编译器不对这个指针的指向的存储单元进行优化, 即不用通用寄存器暂时代替这个指针的指向的存储单元,而是每次取值都直接到指针的指向的存储单元取值.volatile 主要用于变量会异步改变的情况下,主要有三个方面:1.cpu外设寄存器 2.中断和主循环都会用到的全局变量   3.操作系统中的线程间都会用到的公共变量.上述表达式拆开来分析,首先(volatile unsigned long *) 0x48000000的意思是把0x48000000强制转换成volatile unsigned long类型的指针,即对指针的操作的范围是从0x48000000开始的4个字节(long型).暂记为p,那么就是#define A   *p,即A为P指针指向位置的内容了。这里就是通过内存寻址访问到寄存器A,可以读/写操作!
文章三:
理解嵌入式中#define rRTCCON (*(volatile unsigned char *))0x57000043
            #define rRTCCON    (*(volatile unsigned char *)0x57000043) //RTC control
    理解#define rRTCCON (*(volatile unsigned char *)0x57000043) //RTC control 这样的定义,总是感觉很奇怪,今天终于有了一点点心得, 嵌入式系统编程,要求程序员能够利用C语言访问固定的内存地址。既然是个地址,那么按照C语言的语法规则,这个表示地址的量应该是指针类型。所以,知道要访问的内存地址后,比如0x57000043:
    第一步是要把它强制转换为指针类型(unsigned char *)0x57000043,s3c2410的rRTCCON是单字节访问的,所以0x57000043强制转换为指向unsigned char类型。volatile(可变的)这个关键字说明这变量可能会被意想不到地改变,这样编译器就不会去假设这个变量的值了。这种“意想不到地改变”,不是由程序去改变,而是由硬件去改变——意想不到。
     第二步,对指针变量解引用,就能操作指针所指向的地址的内容了;
            *(volatile unsigned char *)0x57000043
     第三步,小心地把#define宏中的参数用括号括起来,这是一个很好的习惯。
    在嵌入式系统中经常使用到Volatile,对于volatile的用法,我根据自己的理解做如下阐述,希望大家可以发表评论:
    在c语言中,volatile关键字是一种类型修饰符, 用它声明的类型变量表示该变量可以被某些编译器未知的外部因素(比如:操作系统、硬件或者其它线程)更改. 遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址(定义的变量在内存中的地址)的稳定访问。
    编译器对代码的优化是指:CPU在执行的过程中,因为访问内存的速度远没有cpu的执行速度快,为了提高效率,引入了高速缓存cache. C编译器在编译时如果不知道变量会被其它外部因素(操作系统、硬件或者其它线程)修改,那么就会对该变量进行标识,即优化.那么这个变量在CPU的执行过程中,就会被放到高速缓存cache去,进而达到对变量的快速访问. 在了解了优化的概念后,试想如果我们事先就知道该变量会被外部因素改变,那么我们就在这个变量定义前加上Volatile,这样编译器就不会对该变量进行优化.这样该变量在cpu处理的过程当中,就不会被放到高速缓存cache中。
     为什么要让变量在执行的过程中不被放到cache中去呢?如果变量是被外部因素改变,那么cpu就无法判断出这个变量已经被改变,那么程序在执行的过程中如果使用到该变量,还会继续使用cache中的变量,但是这个变量其实已经被改变了.需要到内存地址中更新其内容了.还有一个原因,在一些寄存器变量或数据端口的使用中,因为寄存器变量本身也是靠cache来处理,为了避免引起错误,也可以使用volatile修饰符.(简单的说使用volatile的目的就是:让对volatile 变量的存取不能缓存到寄存器,每次使用时需要重新存取。

嵌入式volatile关键字相关推荐

  1. 【嵌入式】C语言中volatile关键字

    00. 目录 文章目录 00. 目录 01. volatile概述 02. volatile应用场景 03. volatile应用示例 04. 嵌入式系统中应用 05. volatile官方说明 vo ...

  2. ARM嵌入式编译器-volatile关键字对编译器优化的影响

    volatile限定符告知计算机,其他agent(而不是变量所在的程序)可以改变该变量的值.通常它被用于硬件地址以及在其他程序或同时运行的线程中共享数据.要求编译器不要对其描述的对象作优化处理,对它的 ...

  3. C++中mutable、volatile关键字

    C++中mutable.volatile关键字 mutable和volatile 很少遇到这两个关键字,学嵌入式估计知道后者,深入研究C++的估计知道前者. (1)mutable 在C++中,muta ...

  4. volatile关键字及编译器指令乱序总结

    本文简单介绍volatile关键字的使用,进而引出编译期间内存乱序的问题,并介绍了有效防止编译器内存乱序所带来的问题的解决方法,文中简单提了下CPU指令乱序的现象,但并没有深入讨论. 以下是我搭建的博 ...

  5. volatile关键字

    [转自:http://hi.baidu.com/limp_t/blog/item/176bf10e0d65e0c97bcbe187.html] volatile关键字是一种类型修饰符,用它声明的类型变 ...

  6. amp quot memory c语言,c volatile 关键字

    C++ volatile 关键字- 简书 2017年1月1日 - 注意:本文中代码均使用Qt 开发编译环境volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改. ...

  7. volatile关键字的作用:防止变量被编译器优化

    本文大部分来自于:http://witmax.cn/volatile.html 我怕链接会失效,故转载此篇文章... volatile关键字是一种类型修饰符,用它声明的类型变量,编译器对访问该变量的代 ...

  8. C语言volatile 关键字

    什么是volatile关键字 volatile用于声明一个变量,告诉编译器该变量值容易发生改变,在编译.读取.存储该变量的时候都不要做任何优化,因此编译后的程序每次需要存储或读取这个变量的时候,都会直 ...

  9. java中实现具有传递性吗_Java中volatile关键字详解,jvm内存模型,原子性、可见性、有序性...

    一.Java内存模型 想要理解volatile为什么能确保可见性,就要先理解Java中的内存模型是什么样的. Java内存模型规定了所有的变量都存储在主内存中.每条线程中还有自己的工作内存,线程的工作 ...

最新文章

  1. Git与SVN区别 \git学习
  2. java map可以直接用增强for吗
  3. 倒计时几秒_和平精英:倒计时0秒时进圈会不会被淘汰?主播展示极限卡圈
  4. 【数据结构与算法】之深入解析运用链表结构计算“两数相加”的算法实现
  5. json动画_three.js动画(四)
  6. .Net Core3.0使用gRPC
  7. 协程-greenlet版(python 版)
  8. 2021辽宁高考成绩查询公布,2021辽宁高考成绩什么时候出
  9. idea中实体类右击没有ptg_这些6到飞起的idea插件,你还没用过?
  10. word vba 点击任意域代码,刷新整个文档的域代码值
  11. 2019年2月10日训练日记
  12. 大数据Top K问题
  13. 使用Keepalived实现负载均衡
  14. 前端JavaScript学习网站(重磅推荐)
  15. Telemetry系统架构
  16. 【迅为iMX6Q】开发板 烧写工具 MfgTool2.exe 打不开问题的解决
  17. ubuntu 18.04.2 server基本安装配置
  18. VS下报LNK1104的一种解决方法
  19. 查尔姆斯理工计算机教授,瑞典查尔姆斯理工大学王二刚教授系统评述:供体-受体型三元共轭聚合物实现高效太阳能电池器件构筑...
  20. TreeView相关

热门文章

  1. boost::hash_range相关的测试程序
  2. boost::ptr_set相关的测试程序
  3. boost::hana::hash用法的测试程序
  4. boost::lexical_cast模块将创建一个to_long_double方法,将 Boost.Variant 的值转换为long double
  5. boost::geometry模块使用变换自定义坐标系示例
  6. boost::fusion::as_list用法的测试程序
  7. ITK:可视化静态稀疏Shi 2D水平设置图层
  8. ITK:侵蚀灰度图像
  9. VTK:几何对象之OpenVRTessellatedBoxSource
  10. OpenCV‘s Kalman filter卡尔曼滤波器的实例(附完整代码)