我有一段代码,我试图返回指向的值的平方*ptr.
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
main()
{
int a=8,t;
t=square(&a);
printf("%d",t);
}
Run Code Online (Sandbox Code Playgroud)
它对我来说很好,但是这段代码的作者说它可能因为以下原因而无法工作:
因为它可能*ptr会意外地改变,a和b有可能不同.因此,此代码可能返回一个不是正方形的数字!正确的方法是
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}
Run Code Online (Sandbox Code Playgroud)
我真的很想知道他为什么这样说?
jsb*_*eno 10
volatile关键字的概念正是向编译器指示标记为这样的变量在程序执行期间可能以意外的方式发生变化.
然而,这并没有使它成为"随机数"的来源 - 它只是建议编译器 - 实际更改变量内容的责任应该是另一个进程,线程,一些硬件中断 - 任何会写入进程内存但是没有在volatile声明发现自己的函数中内联.在"旧时代"(魔术较少的编译器)中,它所做的一切都是阻止编译器在其中一个CPU寄存器中缓存变量值.我不知道现代编译器触发的优化/去优化策略 - 但它至少会这样做.
在没有任何这样的外部因素的情况下,"易变"变量就像任何其他因素一样.实际上 - 它就像任何其他变量一样 - 因为未标记为volatile的变量也可以通过相同的外部原因进行更改(但是在这种情况下编译的C代码不会为此做好准备,这可能会导致使用不正确的值) .
由于这个问题有一个公认的正确答案,我将简要介绍一下:这是一个简短的程序,您可以运行以查看自己发生的错误行为.
#include <pthread.h>
#include <math.h>
#include <stdio.h>
int square(volatile int *p) {
int a = *p;
int b = *p;
return a*b;
}
volatile int done;
void* call_square(void* ptr) {
int *p = (int*)ptr;
int i = 0;
while (++i != 2000000000) {
int res = square(p);
int root = sqrt(res);
if (root*root != res) {
printf("square() returned %d after %d successful calls\n", res, i);
break;
}
}
done = 1;
}
int main() {
pthread_t thread;
int num = 0, i = 0;
done = 0;
int ret = pthread_create(&thread, NULL, call_square, (void*)&num);
while (!done) {
num = i++;
i %= 100;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
该main()函数生成一个线程,并在另一个循环中square使用易失性指针修改与循环中的数据同时进行平方.相对而言,它不会经常失败,但它在不到一秒的时间内非常可靠地完成:
square() returned 1353 after 5705 successful calls <<== 1353 = 33*41
square() returned 340 after 314 successful calls <<== 340 = 17*20
square() returned 1023 after 5566 successful calls <<== 1023 = 31*33
Run Code Online (Sandbox Code Playgroud)
首先要了解什么是不稳定的:为什么C需要挥发性?
然后,尝试自己找到答案.
这是一个动荡和硬件世界的游戏.:-)
阅读Chris Jester-Young给出的答案:
volatile告诉编译器您的变量可能通过其他方式更改,而不是访问它的代码.例如,它可以是I/O映射的存储器位置.如果在这种情况下没有指定,则可以优化一些变量访问,例如,其内容可以保存在寄存器中,并且存储器位置不会再次读回.