C语言编程技巧
volatile的用法
volatile的本意是“易变的” EETOP专业博客---电子工程师自己的家园!~XP0Z,|Rz[#p?~
M7p e6W{5d#k'^0由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如:EETOP专业博客---电子工程师自己的家园9x+Q%ww_
static int i=0;jt[1]Y/W
V5y1t0EETOP专业博客---电子工程师自己的家园$`7@(jC/w7G0hf
int main(void)EETOP专业博客---电子工程师自己的家园'C,yk
@4x U?M+]
{ ,Q,](b4J!? z p@0...
!xN"N~h[1]d;[0while
(1)
?W?MG3S&^P-U7w&C0{EETOP专业博客---电子工程师自己的家园f/\h Zd*E
if (i) dosomething();
6mCWh I
~0}
1f&]I)t5Z0x&q^[1]c)T0}EETOP专业博客---电子工程师自己的家园0N-O$P'r2r8Pq/O8t
iu^ `i f [0/* Interrupt
service routine. */EETOP专业博客---电子工程师自己的家园T}+i6MY
void ISR_2(void)
$v%^#{*IM4k?u i0{EETOP专业博客---电子工程师自己的家园C:j(u\ b:`
i=1;EETOP专业博客---电子工程师自己的家园6A!G#w??C-? }&kO
}cQM(ED"A0
#x3ix"?@)J?Tk E I0程序的本意是希望ISR_2中断产生时,在main当中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远也不会被调用。如果将将变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。+Z{?e[,{/IzB0EETOP专业博客---电子工程师自己的家园
ADw
VWf;[w&s8R
一般说来,volatile用在如下的几个地方:y?\&V.atr,?-\*V0EETOP专业博客---电子工程师自己的家园(A-RcO&uY
1、中断服务程序中修改的供其它程序检测的变量需要加volatile;EETOP专业博客---电子工程师自己的家园n?b'w'Aw?A"u
b3HT:lj,v(K02、多任务环境下各任务间共享的标志应该加volatile;*c S.M'Y(r0EETOP专业博客---电子工程师自己的家园;t5g)AW'Ko?}
?9m1Ha
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;EETOP专业博客---电子工程师自己的家园E*v
|8j2v$b
7_6\#Y;~%p%j?V[1]]:Upf0另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。
关于C++中的内联函数(inline)
在c++中,为了解决一些频繁调用的小函数大量消耗栈空间或者是叫栈内存的问题,特别的引入了inline修饰符,表示为内联函数。 ]&CMg[1]R6s%MU0
8O
I'D+eC&N3{?X6c*k0 可能说到这里,很多人还不明白什么是栈空间,其实栈空间就是指放置程序的局部数据也就是函数内数据的内存空间,在系统下,栈空间是有限的,如果频繁大量的使用就会造成因栈空间不足所造成的程序出错的问题,函数的死循环递归调用的最终结果就是导致栈内存空间枯竭。
下面我们来看一个例子:
#include
cR Y+Y']1ba&h+Hh3L0#include
VHN&Z+J*u5e0using namespace std;
OS7a2RD{(~ J5}'M*{
6A \(Z$B6B0inline string dbtest(int a); //函数原形声明为inline即:内联函数 %E[1]_'S{#Bq0K4c6lx k3N0
EETOP专业博客---电子工程师自己的家园8U
A@
dg/M9U3J8p eM7Z
EETOP专业博客---电子工程师自己的家园+o%H)E?-I+V1a*]x
void main()
:r Y Q&Oz*^Fs|'HN0{
5M [|UXx[1]j|o0 for (int i=1;i<=10;i++)
,p y3sc?pHK)`&C0 {
EETOP专业博客---电子工程师自己的家园-I6J
I:S5E2_ G.C
K8^
l
cout << i << ":" << dbtest(i) << endl;
Bk"a?Z/cr9Q0 } EETOP专业博客---电子工程师自己的家园C6~ SN6?$]l
cin.get(); EETOP专业博客---电子工程师自己的家园[1]O}h E2v7p"g
m`X!k,o$PlI0} EETOP专业博客---电子工程师自己的家园
t!s.o-if#X.|EETOP专业博客---电子工程师自己的家园?p;X;yHu
string dbtest(int a)//这里不用再次inline,当然加上inline也是不会出错的 EETOP专业博客---电子工程师自己的家园5D(TGk(u:Bw)F8w&i
{
7L2b-w?sXZ!yJ
[,P j0 return (a%2>0)?"奇":"偶"; EETOP专业博客---电子工程师自己的家园~,i"V:k-EQ EETOP专业博客---电子工程师自己的家园Motp#vO"q)F
}
上面的例子就是标准的内联函数的用法,使用inline修饰带来的好处我们表面看不出来,其实在内部的工作就是在每个for循环的内部所有调用dbtest(i)的地方都换成了(i%2>0)?"奇":"偶"这样就避免了频繁调用函数对栈内存重复开辟所带来的消耗。
说到这里很多人可能会问,既然inline这么好,还不如把所谓的函数都声明成inline,嗯,这个问题是要注意的,inline的使用是有所限制的,inline只适合函数体内代码简单的函数使用,不能包含复杂的结构控制语句例如while switch,并且不能内联函数本身不能是直接递归函数(自己内部还调用自己的函数)。
说到这里我们不得不说一下在c语言中广泛被使用的#define语句,是的define的确也可以做到inline的这些工作,但是define是会产生副作用的,尤其是不同类型参数所导致的错误,由此可见inline有更强的约束性和能够让编译器检查出更多错误的特性,在c++中是不推荐使用define的。参考
阅读(1482) | 评论(0) | 转发(0) |