文档章节

C语言 —— const和volatile同时修饰某对象的意义

follitude
 follitude
发布于 2016/05/13 17:08
字数 1777
阅读 85
收藏 3
点赞 2
评论 0

const和volatile同时修饰某对象的意义:

    volatile const int i;

(1)本程序段中不能对 i 做修改,任何修改都是非法的,或者至少是粗心,编译器应该报错,防止这种粗心;
(2)另一个程序段则完全有可能修改,因此编译器最好不要做太激进的优化。

“const”的含义:“请做为常量使用”,而并非“放心吧,那肯定是个常量”。
“volatile”的含义:“请不要做没谱的优化,这个值可能变掉的”,而并非“你可以修改这个值”。
因此,它们本来就不是矛盾的。

下面分别来看下这两个类型修饰符

const:

eg.1:

const意味着“只读”,只读表示编译器不允许代码修改变量,或者说这个变量在此处不能被修改,但并不表示这个变量在其它地方不能够被修改(不能被修改岂不就成了常量?)

#include <stdio.h>
int main(void) {
    int i = 5;
    const int* p = &i;
    *p = 6;    // 不可以
    i = 7;    // 完全可以,而且那个“const”的“*p”也跟着变成了7。
    printf("%d\n", *p);
    return 0;
}

 

eg.2:


void funcA(const char* str) {
    ...
}
在上面的程序中,str所指向的内存区域就是只读的,但这个只读性只在函数funcA内部,
出了funcA,这块内存完全有可能是能够被修改的。
void funcB(void) {
    char name[] = "Jim King";
    funcA(name);
}

以上两例足够说明,用const修饰表达了我们自己的代码不会改变所修饰的对象的值,但是别的代码有可能改变此对象的值。

 

volatile:

volatile表示禁止优化。因为编译器会认为如果代码没有改变变量,那么这个变量就不会改变,因此编译器会用寄存器把该变量缓存起来,每次需要读取变量值的时候,就从缓存中读取。如果使用volatile,就应该直接从初始的内存区去读数据。当然,每一次修改变量的值时,也是将新值写入到内存中,而不是只写到缓存中。这在大多数时候是正确的,但是在多线程或者中断的场合就不正确了。

eg.1:

中断程序。在下面的程序中,我们将intr_func注册为中断函数,某个中断发生时触发这一函数:

unsigned char flag = 1;

int main(int argc, char **argv) {
    reg_intr(XXX, intr_func);
    while(flag) {
        printf("hello\n");
    }
    return 0;
}

void intr_func(void) {
    flag = 0;
}

当不加volatile时,编译器会直接将while条件中的flag换成1(即编译器做了优化),因此即使中断发生也无法结束循环;如果给flag加上volatile标识,编译器就不会做前述的优化,程序就依照设计的意图工作了。

const、volatile综合举例

eg.1:

嵌入式系统中比较常见的例子。很多嵌入式系统允许我们访问外部寄存器,该寄存器的地址假设是0x0018,并假设该寄存器的最低位表示设备状态,1为忙碌,0为空闲。


#define GET_REG_VALUE(reg)    (*reg)       //get register value 
const unsigned char* STATUS_REG   = 0x0018;    // status register
const unsigned char  STATUS_BUSY = 0x01;        //busy bit 
while (GET_REG_VALUE(STATUS_REG) & STATUS_BUSY);     // wait until free 
// do something to operate the device
...


这段代码很可能会死循环。因为编译器会将地址0x0018的对象的值缓存起来,然后每次while的时候都从缓存中读取。虽然STATUS_REG的值是const unsigned char *,但这仅仅表示STATUS_REG寄存器是个只读寄存器,我们不能够在代码中去写这个寄存器,但并不表示这个寄存器是不能够改变的。硬件完成了它的任务之后,就会把状态设置成空闲,因此该寄存器的最后一位在我们循环的时候很可能已经发生了变化。因此在这样的地方,我们要禁止编译器自作聪明的优化,方法如下:


const volatile unsigned char *STATUS_REG   = 0x0018;    /* status register */

总结此例:

从const角度看,说明了用const修饰表达了我们自己的代码不会改变所修饰的对象的值,但是硬件有可能改变此对象的值

从volatile角度看,由于之前变量const修饰了变量,导致了此变量“只读”。然而循环过程中该变量又存在连续不变的情况,编译器就多管闲事的做了优化,导致后来寄存器发生变化也还是死不要脸的进入循环,所以就加入volatile来告诫编译器不要乱优化。

通过以上举例,我们归纳总结以下const和volatile类型修饰符:

1.我们需要明白“volatile”的含义并非是“non-const”,所以他们才可以放在一起。

2.在C/C++语言中,const没有反义词,如果一个变量没有const修饰,那它本身就是const的反义词,而并非加上volatile才是const的反义词。

3.volatile标识一个变量意味着这个变量可能被非本程序的其他过程改变。如果一个变量不会被本程序改变,通常可能给它加上const,但如果该变量可能被其他程序改变而本程序又在检测这个变量的值,就需要给它加上volatile,于是变量就同时有volatile和const了。两者同时修饰一个对象的典型情况,是用于驱动中访问外部设备的只读寄存器。

4.对于非指针非引用的变量,const volatile同时修饰的意义确实不大(个人觉得)。

 

现在就很好解答《C语言深度解剖》中的问题了,题述:

const volatile int i=10;这行代码有没有问题?如果没有,那 i 到底是什么属性?

回答一:没有问题,例如只读的状态寄存器。它是volatile,因为它可能被意想不到地改变;它是const,因为程序不应该试图去修改它。volatile和const并不矛盾,只是控制的范围不一样,一个在程序本身之外,另一个是程序本身。

回答二:没问题,const和volatile这两个类型限定符不矛盾。const表示(运行时)常量语义:被const修饰的对象在所在的作用域无法进行修改操作,编译器对于试图直接修改const对象的表达式会产生编译错误。volatile表示“易变的”,即在运行期对象可能在当前程序上下文的控制流以外被修改(例如多线程中被其它线程修改;对象所在的存储器可能被多个硬件设备随机修改等情况):被volatile修饰的对象,编译器不会对这个对象的操作进行优化。一个对象可以同时被const和volatile修饰,表明这个对象体现常量语义,但同时可能被当前对象所在程序上下文意外的情况修改。

© 著作权归作者所有

共有 人打赏支持
follitude
粉丝 4
博文 118
码字总数 4956
作品 0
浦东
从菜鸟到大神——如何快速掌握C语言

C语言关键字是嵌入式C语言必须掌握的利器,C语言中有很多的关键字,有register,static,const,extern,typedef,很多人可能只知道其表面含义,对其中的用法理解还不到位,希望这篇文章能帮助你:...

从梦流风 ⋅ 05/31 ⋅ 0

小朋友学Python(10):C/C++/Java/Python的关键字

一、C语言关键字 C语言关键字有32个 auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static s......

翡翠森林Z ⋅ 2017/12/28 ⋅ 0

C/C++中volatile关键字详解

1. 为什么用volatile?   C/C++ 中的 volatile 关键字和 const 对应,用来修饰变量,通常用于建立语言级别的 memory barrier。这是 BS 在 “The C++ Programming Language” 对 volatile 修...

fx677588 ⋅ 2017/08/13 ⋅ 0

2675字带你进阶C语言中的关键字.md

C语言进阶之C语言关键字 关键字总结.png 基本数据类型 1.数据类型 数据类型可以理解为固定内存大小的别名(例如,在C语言中int数据类型代表了4个字节的内存) 数据类型是可以创建变量的模板...

PcDack ⋅ 2017/12/03 ⋅ 0

在C语言中以编程的方式获取函数名

仅仅为了获取函数名,就在函数体中嵌入硬编码的字符串,这种方法单调乏味还易导致错误,不如看一下怎样使用新的C99特性,在程序运行时获取函数名吧。 对象反射库、调试工具及代码分析器,经常...

小熊猫大暴走 ⋅ 2011/12/23 ⋅ 0

C语言中auto,register,static,const,volatile的区别

1)auto   这个关键字用于声明变量的生存期为自动,即将不在任何类、结构、枚举、联合和函数中定义的变量视为全局变量,而在函数中定义的变量视为局部变量。这个关键字不怎么多写,因为所有...

BeyondChallenge ⋅ 2015/08/21 ⋅ 0

C语言中auto,register,static,const,volatile的区别

1)auto 这个关键字用于声明变量的生存期为自动,即将不在任何类、结构、枚举、联合和函数中定义的变量视为全局变量,而在函数中定义的变量视为局部变量。这个关键字不怎么多写,因为所有的变...

曾赛 ⋅ 2011/01/04 ⋅ 0

类型限定符const

C语言中一共有两种类型限定符:const和volatile。C99还有第三种类型限定符,即restrict,它只用于指针(受限指针)。volatile只用在底层编程中。 把对象声明为cost有以下几个好处: 1)const是...

彼得雷 ⋅ 2015/11/05 ⋅ 0

C语言再学习--关键字

C语言一共有32个关键字,如下表所示: 关键字 说明 auto 声明自动变量 short 声明短整型变量或函数 int 声明整型变量或函数 long 声明长整型变量或函数 float 声明浮点型变量或函数 double 声...

qq_29350001 ⋅ 2016/11/03 ⋅ 0

C语言笔记(关键字)

gdb调试 gcc 源程序 -g;加gdb调试信息 gdb可执行程序;(gdb调试) l(ist):查看源码,按一下从main开始10行以此往后 l n:查看n处上下10行的源码 run:运行程序 b(reak)行号:加断点 ...

believe_s ⋅ 2017/11/17 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

RabbitMQ学习以及与Spring的集成(三)

本文介绍RabbitMQ与Spring的简单集成以及消息的发送和接收。 在RabbitMQ的Spring配置文件中,首先需要增加命名空间。 xmlns:rabbit="http://www.springframework.org/schema/rabbit" 其次是模...

onedotdot ⋅ 25分钟前 ⋅ 0

JAVA实现仿微信红包分配规则

最近过年发红包拜年成为一种新的潮流,作为程序猿对算法的好奇远远要大于对红包的好奇,这里介绍一种自己想到的一种随机红包分配策略,还请大家多多指教。 算法介绍 一、红包金额限制 对于微...

小致dad ⋅ 36分钟前 ⋅ 0

Python 数电表格格式化 xlutils xlwt xlrd的使用

需要安装 xlutils xlwt xlrd 格式化前 格式化后 代码 先copy读取的表格,然后按照一定的规则修改,将昵称中的学号提取出来替换昵称即可 from xlrd import open_workbookfrom xlutils.copy ...

阿豪boy ⋅ 今天 ⋅ 0

面试题:使用rand5()生成rand7()

前言 读研究生这3 年,思维与本科相比变化挺大的,这几年除了看论文、设计方案,更重要的是学会注重先思考、再实现,感觉更加成熟吧,不再像个小P孩,人年轻时总会心高气傲。有1 道面试题:给...

初雪之音 ⋅ 今天 ⋅ 0

Docker Toolbox Looks like something went wrong

Docker Toolbox 重新安装后提示错误:Looks like something went wrong in step ´Checking if machine default exists´ 控制面板-->程序与应用-->启用或关闭windows功能:找到Hyper-V,如果处......

随你疯 ⋅ 今天 ⋅ 0

Guacamole 远程桌面

本文将Apache的guacamole服务的部署和应用,http://guacamole.apache.org/doc/gug/ 该链接下有全部相关知识的英文文档,如果水平ok,可以去这里仔细查看。 一、简介 Apache Guacamole 是无客...

千里明月 ⋅ 今天 ⋅ 0

nagios 安装

Nagios简介:监控网络并排除网络故障的工具:nagios,Ntop,OpenVAS,OCS,OSSIM等开源监控工具。 可以实现对网络上的服务器进行全面的监控,包括服务(apache、mysql、ntp、ftp、disk、qmail和h...

寰宇01 ⋅ 今天 ⋅ 0

AngularDart注意事项

默认情况下创建Dart项目应出现以下列表: 有时会因为不知明的原因导致列表项缺失: 此时可以通过以下步骤解决: 1.创建项目涉及到的包:stagehand 2.执行pub global activate stagehand或pub...

scooplol ⋅ 今天 ⋅ 0

Java Web如何操作Cookie的添加修改和删除

创建Cookie对象 Cookie cookie = new Cookie("id", "1"); 修改Cookie值 cookie.setValue("2"); 设置Cookie有效期和删除Cookie cookie.setMaxAge(24*60*60); // Cookie有效时间 co......

二营长意大利炮 ⋅ 今天 ⋅ 0

【每天一个JQuery特效】淡入淡出显示或隐藏窗口

我是JQuery新手爱好者,有时间就练练代码,防止手生,争取每天一个JQuery练习,在这个博客记录下学习的笔记。 本特效主要采用fadeIn()和fadeOut()方法显示淡入淡出的显示效果显示或隐藏元...

Rhymo-Wu ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部