问题一:c语言中volatile和const可以同时修饰一个对象吗?
很多地方都说,volatile表示“易变的”,const表示"不变的,恒定的"。这两者看起来是矛盾的。
但答案是:可以的。上面的字面上的矛盾,来源于对这两个关键字含义的解释不准确。
准确的说,volatile表明对该对象的读写操作都不能被编译器优化。而const表示"只读的",对const对象的显式写操作会被编译器发现并报告编译错误。
两者同时修饰一个对象的典型情况,是用于驱动中访问外部设备的只读寄存器。
网上很多解释volatile的资料,都说“表明该变量可能会被外部所改变,而这个改变是编译器所不知道的,编译器每次需要读取这个变量的时候,都直接从变量地址中读取数据”。这是对的,但是漏掉来对写操作的效果,容易让人以为volatile对写操作不起作用。
为此我做了一个小测试,明确证明volatile同样防止来编译器对写操作的优化。
int main()
{
int a;
a = 1;
a = 2;
a = 3;
return a;
}
加上-O选项编译后的汇编代码如下:
main:
pushl %ebp
movl %esp, %ebp
movl $3, %eax
popl %ebp
ret
可以看出来,a=1; a=2;两个赋值操作都被编译器优化掉了,甚至连变量a本身都被优化掉了。把a定义为: volatile int a再用同样的参数编译一下,得到的汇编代码如下:
main:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl $1, -4(%ebp)
movl $2, -4(%ebp)
movl $3, -4(%ebp)
movl -4(%ebp), %eax
leave
ret
对a的连续3次赋值操作都被保留了。
问题二:volatile和register能同时修饰一个对象吗?
答案:gcc可以接受你这样做, 但是这个register也就白加了。
问题三:volatile可以修饰结构变量吗? 可以修饰指针吗?
答案: 可以。修饰结构变量时,相当于结构体的每个成员都被volatile修饰。
修饰指针时,有两种写法,它们的效果不同。
1)volatile char * p; 或者 char volatile *p;
表示对*p的赋值,将全部保留不做优化。例如,*p=1; *p=2; *p=3; 编译器不会自做主张优化成*p=3;而是3个赋值操作全部保留。
但对p的赋值操作,还是会被优化。例如,p=&a; p=&b; p=&c;编译器会只留下p=&c;
2) char * volatile p;
这样写的话,不管是对p还是*p的赋值操作,都不会被优化掉。
问题四:如何强行突破编译器对const的保护?例如const int *p; int *m; 如何让 *p=3; 或者 m=p; 这样的操作通过编译?
可以定义一个union:
union {
const int *a;
int *b;
} x;
x.a = p;
m = x.b;
一切OK。