宏定义的用法以及再次解释const和volatile
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了宏定义的用法以及再次解释const和volatile相关的知识,希望对你有一定的参考价值。
__I、 __O 、__IO是什么意思?
这是ST库里面的宏定义,定义如下:
#define __I volatile const /*!< defines ‘read only‘ permissions */
#define __O volatile /*!< defines ‘write only‘ permissions */
#define __IO volatile /*!< defines ‘read / write‘ permissions */
volatile和const在嵌入式设备上属于必须要掌握的知识。
const,在C语言中,不是常量,是只读变量,或者说是不能通过程序直接更改的变量,但可以间接修改。const修饰的变量,并不是不能更改的,只是不能通过程序直接修改,即,在嵌入式设备中,const修饰的寄存器,是可以通过硬件更改的。
#define __I volatile const
定义的是输入口,既然是输入,证明stm32需要读取数据,而读取数据我们一般都是限制为只读属性,volatile表示这个__I修饰的寄存器是易变的。所以这里问题就来了,既然是const你还说易变,不是混乱了吗?所以才有之前介绍的,const只是我们程序不能直接修改,但是可以间接修改和被硬件修改。
#define __O volatile
定义输出端口,输出用volatile修饰应该没什么争议。
#define __IO volatile
定义输入输出,既要做输入也要做输出,volatile也没有什么争议。
但争议就在于st官方的注释__O为什么是只写‘write only‘?而同样用volatile修饰的__IO却备注‘read / write‘?
其实,__O,表示的是输出,输出就是写啊,有人问我,为什么不加const,例如#define __O volatile const 更能代表只写啊,这究其原因还是没理解const,const是只读变量,并不是不改变的常量。输出不需要只读属性就已经是只写了,因为你不能在输出的时候使用输入功能,即不能读取。
既然说到volatile,就不得不提一个经典的面试题了: 下面的函数有什么错误:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
这段代码有点变态。这段代码的目的是用来返指针*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;
}
回到宏定义话题:
在阅读hal库函数或者linux内核函数时,总有人在群里问下面这种宏的用法:
1.宏定义防止 使用时错误
用小括号包含。
例如:#define ADD(a,b) ((a)+(b))
用do{}while(0)语句包含多语句防止错误
例如:#difne DO(a,b) a+b;\
a++;
应用时:if(….)
DO(a,b); //产生错误
else
……
解决方法: #difne DO(a,b) do{a+b;\
a++;}while(0)
以上是关于宏定义的用法以及再次解释const和volatile的主要内容,如果未能解决你的问题,请参考以下文章
iOS开发中 const,static,extern用法总结