我是C语言的初学者,遇到了以下代码:
#include "stdio.h"
unsigned int ReturnSquare(void);
int main(void) {
int k;
int *mPtr;
mPtr = (int*) 0x1234;
*mPtr = 10;
k = (int) ReturnSquare();
printf("%p --> %d\n",mPtr,k);
}
unsigned int ReturnSquare(void)
{
unsigned volatile int a = * (unsigned volatile int *) 0x1234;
unsigned volatile int b = * (unsigned volatile int *) 0x1234;
return a * b;
}
能否请您帮助我理解此代码中volatile的用途?
该程序似乎无法正常运行。任何建议和解释都非常欢迎。预先谢谢你。
答案 0 :(得分:2)
当您两次读取同一寄存器时,编译器可以决定优化性能。
它可以将代码变成这样:
unsigned int a = * (unsigned int *) 0x1234;
unsigned int b = a;
当您添加volatile时,编译器将在第二次读取时不假设其值将相同,并且会生成额外的指令以解引用指针以再次注册。
现在可能对您来说太高级了,但是您可以使用编译器上的汇编输出选项进行检查,易失性版本将提供更多的汇编指令。
答案 1 :(得分:2)
您显示的代码是volatile
的错误示例,而对于C代码而言,通常是错误的示例。
首先,然后代码执行此操作:
mPtr = (int*) 0x1234;
*mPtr = 10;
它使用一个看似任意的地址0x1234
,并在其中放置一个int
值。通常,您不知道您是否可以写入该地址。它可能未映射到您的虚拟地址空间中,如果是,则那里可能有重要的内容,并且对其进行写操作将破坏程序。因此,该程序正在做一些不好的事情且不受支持,我们不应该期望它会起作用。 (在特殊环境中,可能指定了内存地址空间的布局,并且可以按这种方式使用。这种情况应始终清楚地记录在案,并且代码仅限于针对其设计的特定系统;不适合用作通用C代码。)
第二,代码没有做任何特别的事情,以显示带有和不带有volatile
的对象之间的任何区别。除了使用0x1234
写入int
并使用unsigned int
读取错误之外,如果程序没有执行此代码,正常执行该代码将产生令人惊讶的结果100由于使用0x1234
而崩溃。更好的例子是这样的程序:
#include <stdio.h>
int main(void)
{
int a = 1234;
volatile int b = 5678;
printf("Press enter to proceed.\n");
getchar();
printf("a = %d.\n", a);
printf("b = %d.\n", b);
}
然后将指示学生编译启用了优化和调试的程序,在调试器中运行它,在程序等待输入时在调试器中中断它,使用调试器更改{{1 }}和a
,然后继续运行该程序。结果将是程序显示b
及其原始值1234,但显示a
及其更改后的值。 (实际上,由于优化,b
可能不存在调试器可以更改的方式。)
这将表明编译器假定它对非易失性对象(例如a
)具有完全控制权,因此它可以通过优化代码的方式来假定它们不会发生意外更改,但是编译器不会做出此类假设与a
对象。借助volatile
对象,编译器每次在源代码中使用它时都会从内存中重新加载它(并且每次在源代码中对其进行修改时,它都会将它写入内存中。)
volatile
的含义是对象可能会以编译器通常不知道的方式更改。因此,要演示volatile的工作方式,需要从程序外部修改程序。尽管调试器是实现此目的的一种方法,但是volatile
的预期用途是访问地址空间中连接到I / O设备的位置,而不是普通存储器。当某些输入/输出操作发生时,这些位置可能会改变。 volatile
关键字告诉编译器不要将对象视为普通内存,以期望它们可能因外部操作而意外更改。