在 C 语言中正确使用易失性关键字



我是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 --> %dn",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;
}

你能帮我了解这段代码中易失性的用处吗?

该程序似乎无法正常工作。非常欢迎任何建议和解释。提前谢谢你。

当您读取同一寄存器两次时,编译器可以决定优化行为。

它可以将代码转换为如下所示的内容:

unsigned  int a = * (unsigned int *) 0x1234;
unsigned  int b = a;

添加 volatile 时,编译器不会在第二次读取时假设值相同,并且它将生成额外的指令来取消引用指针以再次注册。

现在对您来说可能太高级了,但您可以使用编译器上的程序集输出选项进行检查,易失版本将有更多程序集指令。

它强制编译器在每次使用引用的值时读取引用的值。编译器知道编译器在正常程序执行路径中看不到的内容可以更改此对象。

你展示的代码是volatile做的一个不好的例子,也是一般 C 代码的一个坏例子。

首先,代码执行以下操作:

mPtr = (int*) 0x1234;
*mPtr = 10;

它接受一个看似任意的地址,0x1234,并在那里放置一个int值。一般来说,你不可能知道你可以写信到这个地址。它可能没有映射到您的虚拟地址空间中,如果是,那里可能有一些重要的东西,重写它会破坏程序。所以这个程序正在做一些不好且不受支持的事情,我们不应该期望它会起作用。(在特殊环境中,可以指定内存地址空间的布局,并且可以以这样的方式使用。这种情况应始终明确记录在案,并且代码仅限于其设计的特定系统;它不适合用作通用 C 代码。

其次,代码不执行任何特殊操作来显示有volatile和不带的对象之间的任何区别。除了它使用int写入0x1234并使用unsigned int读取的错误之外,如果程序没有因使用0x1234而崩溃,则正常执行此代码将产生一个不足为奇的结果,即 100。一个更好的例子是这样的程序:

#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);
}

然后,将指示学生在启用优化和调试的情况下编译此程序,在调试器中运行它,在程序等待输入时中断它(在调试器中(,使用调试器更改ab的值,然后继续运行该程序。结果是程序显示a的原始值 1234,但显示b的更改值。(事实上,由于优化,a可能不以调试器可以更改的方式存在。

这将证明编译器假设它对非易失性对象(如a(具有完全控制权,因此它可能会以假设它们不会意外更改的方式优化代码,但编译器不会对volatile对象做出此类假设。对于volatile对象,编译器每次在源代码中使用时都会从内存中重新加载它(并且每次在源代码中修改它时都会将其写入内存(。

volatile的含义是对象可能会以编译器通常不知道的方式更改。因此,演示易失性的工作原理需要从程序外部修改程序。尽管调试器是实现此目的的一种方法,但volatile的预期用途是访问地址空间中连接到 I/O 设备而不是普通内存的位置。发生某些输入/输出操作时,这些位置可能会更改。volatile关键字告诉编译器不要将对象视为普通内存,以免它们因外部操作而意外更改。

最新更新