主要探讨关于volatile在定义设备寄存器时应该放到什么位置最合适的问题。另外,在文章中也提到下面两个观点:
*对任意数据类型T,C提供一种标准内置的转换。这个转化可以完成从T指针到volatile T指针的转换,并规定其逆过程即volatile T指针向T指针转换为非法。
*const指针和volatile指针在转换规则方面具有相似性。
本篇文章就后一个观点继续深入探讨。
本人认为const指针的转换规则与const指针的基本一致,因此只要我们懂得其中的一种规则,那么另外的一种就可以不攻自破。关键就是要懂得其中的共同规律,而不是去死记硬背一些具体应用。

1.自相矛盾
T *p;
...
void f(T const *qc);

如果调用f(p)将p传入函数,T指针将会转换成const T指针。整个转换过程是自动完成的,不必人为地强制转换T指针。这是一条潜规则。相反,在下面情况下,如果调用g(pc),就会产生编译错误:
T const *pc;
...
void g(T *q);

因为编译器拒绝将const T指针转换成T指针。这也是一条潜规则
让记住下面的推断:如果你许诺你使用const是不让其它程序改变const对象的内容,那么你自己在后面编写const相关代码时必须要遵守这个许诺。就象一些做官的,表面是一套,背后又是另一套,最后对自己的所做所为不能自圆其说!
下面举个简单的例子来说明诺言是怎么许下,又是怎么被打破的。
假设有人写了下面的代码:
int const *p;
显然,他希望通过const阻止任何有意图去修改const对象的内容的行为,可他又继续写下了"挨扁"的代码:
*p += 3; /*改变p指向的内容*/
++(*p);

因为,他自己又去修改p指针指向的内容,自相矛盾啊!!!
那让我们回头看原先的代码:
T const *pc;
...
void g(T *q);

当你定义const类型的指针pc,等价于你对编译器许诺说我决不允许有代码直接地或间接地甚至潜在地去修改pc指向的内容。当然,我们的编译器是“大好人”,肯定会爽快地答应。接着,你又对编译器许诺说g函数可以修改通过q传入的任何指针的内容。最后你试着调用g(pc)将p通过pc传入g。这时编译器肯定看不过去了,一定会这样地质问你:
你为何将const指针pc传入可能会改变pc指向内容的g函数呢,你不是决不允许其它代码直接地或间接地甚至潜在地去修改pc指向的内容吗,你现在将pc传入g函数不是自己打自己嘴巴吗?嘿嘿,哑口无言了吧!所以,既然做出了许诺,就要坚持到底
继续下面的代码:
T *p;
...
void f(T const *qc);

显然,你许诺编译器说任何代码都可以改变p指向的内容并且你编写的f函数不会改变通过qc传入的其它指针指向的内容。编译器又一次爽快地答应了你。最后你调用了f(p)。这次,编译器只是对你笑笑,心理暗自道:小样你可别让我逮到在f函数中调用诸如g之类可能会改变p指向的代码哦!

2.Const vs Volatile
前面提过,const指针的转换规则与const指针的基本一致。不同的是const是你答应编译器不会编写可能改变const对象指向的内容的代码,而volatile则是编译器答应你不会对相关代码进行优化。
看下面的代码:
T volatile *pv;
...
void g(T *q);

对比const可以知道,调用g(pv)肯定会出现编译错误。因为你跟编译器说不要间接或直接地甚至潜在地优化pv相关的代码,同时你又有跟编译器说它可以优化通过q传入的指针的相关代码。如果你调用g(pv),将不能优化的pv传入可能会优化pv的g函数,显然也是危险并且自相矛盾的做法。
再看:
T *p;
...
void h(T volatile *qv);

对比const可以知道,调用h(p)不会有事,因为编译履行了它的诺言,不在h函数中优化通过qv传入的任何指针相关的代码。

结论:const指针的转换规则与const指针的基本一致,主要的不同在于谁许下了诺言。对于const,诺言的主体是我们自己,而对于volatile则是编译器。不论谁许了诺,都必须遵守并兑现它。