assert关键字与 oolatile关键字的理解

 C语言中assert断言的用法

       一般来说,在程序必须符合一定条件的情况下,才能继续运行,否则就会产生不可预期的错误。比如除0操作,就可以对被除数(暂命名为iDividend)进行断言:
   assert( iDividend != 0 );   一旦iDividend==0,程序就会报错,并自动退出。

        assert 是一个宏,而不是函数,包含在assert.h 头文件中。如果其后面括号里的值为假,则程序终止运行,并提示出错;如果后面括号里的值为真,则继续运行后面的代码。

如何使用assert_param

    在STM32的固件库和提供的例程中,到处都可以见到assert_param()的使用。如果打开任何一个例程中的stm32f10x_conf.h文件,就可以看到实际上assert_param是一个宏定义;
在固件库中,它的作用就是检测传递给函数的参数是否是有效的参数。
所谓有效的参数是指满足规定范围的参数,比如某个参数的取值范围只能是小于3的正整数,如果给出的参数大于3,
则这个assert_param()可以在运行的程序调用到这个函数时报告错误,使程序员可以及时发现错误,而不必等到程序运行结果的错误而大费周折。

这是一种常见的软件技术,可以在调试阶段帮助程序员快速地排除那些明显的错误。

它确实在程序的运行上牺牲了效率(但只是在调试阶段),但在项目的开发上却帮助你提高了效率。

 

 Volatile深入理解

     就象大家更熟悉的const,auto,register等关键字一样,volatile是一个类型修饰符。它是被设计用来修饰被不同线程访问和修改的变量。如果没有volatile,基本上会导致这样的结果:要么无法编写多线程程序,要么编译器失去大量优化的机会。Volatile 是易变的、不稳定的意思。用它修饰的变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。

下面举个例子说明一下问题

int m=10;

int n=m;

当编译完之后,它不会改变,所以不会每次都从内存中读取m的值。

volatile int m=10;

int n=m;

    因为volatile是不稳定的,所以它的值可能发生变化。每次使用它的时候必须从内存中取出i的值,因而编译器生成的汇编代码会重新从m 的地址处读取数据放在n中。所以 必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。

在嵌入式编程中,这种情况经常出现,例如volatile int flag=1;//前面加了volatile则flag的值就不会一直是“1”了。

这样看来,如果m是一个寄存器变量或者表示一个端口数据或者是多个线程的共享数据,就容易出错,所以说volatile可以保证对特殊地址的稳定访问。但是注意:在VC++6.0 中,一般Debug 模式没有进行代码优化,所以这个关键字的作用有可能看不出来。你可以同时生成Debug 版和Release 版的程序做个测试。

     下面三种是典型的volatile关键字的运用场合:

(1)并行设备的硬件寄存器(如:状态寄存器)

(2)一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)  

(3) 多线程应用中被几个任务共享的变量  

(4)下面是来此百度百科的一些解释:本人经过小小的修改,方便理解,在此也贴出来:

从编译器的优化方面理解: 

     在本次线程内, 当读取一个变量时,为提高存取速度,编译器优化时有时会先把变量读取到一个寄存器中;以后,再取变量值时,就直接从寄存器中取值;当变量值在本线程里改变时,会同时把变量的新值copy到该寄存器中,以便保持一致。这就是所谓的“不稳定”,其实还不如说是“变相的一致性”更好!这也是使用volatile关键字的优势。

    当变量在因别的线程等而改变了值,该寄存器的值不会相应改变,从而造成应用程序读取的值和实际的变量值不一致,当该寄存器在因别的线程等而改变了值,原变量的值不会改变,从而造成应用程序读取的值和实际的变量值不一致,从而会出现错误。这就是没有使用volatile关键字的缺点。   

举一个不太准确的例子:发薪资时,会计每次都把员工叫来登记他们的银行卡号;一次会计为了省事,没有即时登记,用了以前登记的银行卡号;刚好一个员工的银行卡丢了,已挂失该银行卡号;从而造成该员工领不到工资   

员工 -- 原始变量地址 

银行卡号 -- 原始变量在寄存器的备份     

补充: volatile应该解释为“直接存取原始内存地址”比较合适,“易变的”这种解释简直有点误导人; “易变”是因为外在因素引起的,象多线程,中断等,并不是因为用volatile修饰了的变量就是“易变”了,假如没有外因,即使用volatile定义,它也不会变化;  

而用volatile定义之后,其实这个变量就不会因外因而变化了,可以放心使用了;大家看看前面那种解释(易变的)是不是在误导人 。

有volatie修饰的变量,每次操作时遵循下面动作:

从内存取值 ---> 放入寄存器 ---> 操作 --->写回内存

没有volatie修饰的变量,操作可能遵循(可能不是所有情况都如此):

从内存取值 ---> 放入寄存器 ---> 第一次操作 ---> 第二次操作(此时仍操作寄存器中的值) …… --->第N次操作 --->写回内存

volatile 跟以前的 register 相反. register 告诉编译器尽量将变量放到寄存器中使用, 而volatile 强制将更改后的值写回内存(无论是cache还是内存). 如果不写回内存, 对于一些全局共享的变量, 可能导致不一致问题.

volatile在优化中的微妙作用。
1.阻止编译器优化
volatile隐含地告诉编译器特殊寄存器可能会改变内容,即使没有任何显式地代码去改变它的内容。

2.无意中降低了效率

     总结:使用volatile关键字就是为了是编程方便,让编译器少出现bug,尤其是在硬件和底层编程的时候,经常会用到这个关键字,如果处理的不好,会出现意想不到的结果,从而也会导致程序出错。所以只有真正理解了volatile才会在使用时得心应手。

posted on 2020-04-12 11:08  一郎哥哥  阅读(212)  评论(0编辑  收藏  举报

导航