为什么'volatile'变量的值不改变?

时间:2020-09-26 11:25:16

标签: c

我试图准确地理解C的基本知识,volatile关键字。

它似乎不像我想的那样工作。 我在某处阅读:

volatile指定可以通过操作修改与var相关的值 除了附近代码中的代码。 当声明var volatile时,编译器从
重新加载值。 程序每次访问它时都会存储。 这会阻止通过优化编译器进行优化。 主要用于确保可预测的程序行为。

我有两个程序,是从两个不同的.c文件编译而成的。

start_loop具有名为int的全局done变量,其中0为初始值和无限循环,该变量应测试done的值是否已更改为{{ 1}}。还将1变量的地址输出到done

stdout程序:

start_loop

然后是#include <stdio.h> #include <time.h> volatile int done = 0; void delay(int seconds){ // some logic for time delay here } void main() { while (done !=1){ delay(4); printf("adress of `done`: %p\nvalue: %d\n", &done, done); } } 程序,该程序应将change_done_var_value变量的地址作为done参数并将其从main更改为0
因此,1程序中的无限循环应检查start_loop的值并退出程序。

done

change_done_var_value

在第一个终端标签中启动#include <stdio.h> #include <stdlib.h> void main(int argc, char *argv[]) { char *str_arg_done = argv[1]; long int done_addr = (int)strtol(str_arg_done, NULL, 16); long int *done = (long int *)done_addr; // converting str to pointer printf("got the address as first arg(str): %s -> address/value %p/%li\n", argv[1], done, *done); printf("change `done` to 1?\n"); char answer = fgetc(stdin); if (answer == 'y') { *done = 1; printf("changed\nnew value: %li", *done); } else { printf("canceled\n"); } 程序后,我得到了:

start_loop

在第二个终端标签中,我启动了4 seconds delay passed #1 adress of `done`: 0x601048 value: 0 4 seconds delay passed #2 adress of `done`: 0x601048 value: 0 ... 并将change_done_var_value传递给了它:

0x601048

但是got the address as first arg(str): 0x601048 -> adress/value 0x601048/0 change `done` to 1? y changed new value: 1 程序仍在运行:

start_loop

似乎4 seconds delay passed #46 adress of `done`: 0x601048 value: 0 程序获得了正确的change_done_var_value值,这是因为它在取消指向address变量的指针的同时显示了正确的0值。

我需要做什么才能使工作正常(从done更改done的值并停止执行change_done_var_value程序)? 否则它无法正常工作。

2 个答案:

答案 0 :(得分:4)

附近代码中未执行的操作

是;这意味着能够修改相应内存块的事物。通常,在同一程序中这将是不同的 thread 。在某些非常特定于平台的情况下,您可以将变量安排在对OS(或硬件)具有特殊意义的特定位置。例如,在旧的掌上电脑或控制台上,您可以从代表I / O寄存器的虚拟内存位置读取信息,以了解按下键盘上的哪个按钮。通过使用volatile,编译器将理解必须检查该值,因为硬件可能已写入该内存,并且您的程序依赖于了解更改。

在现代台式计算机上,程序具有内存保护和内存空间虚拟化。他们无法轻松查看彼此的数据,因此通常需要进行一些特殊的安排/权限才能这样做。但是即使有可能,您也不能仅通过使用其名称来简单地检查另一个程序的变量-该名称仅对编译器有意义。它不是已编译程序的实际部分(尽管调试模式下的编译可能会尝试使其表现得有点类似)。

答案 1 :(得分:3)

如果我理解正确,您尝试在另一个程序中使用一个程序的内存吗?在任何现代操作系统上,“它都不会这样工作”,因为程序(或更好的是:“进程”)被有目的地封装,这样,如果没有特定于操作系统的 interprocess,它们就无法访问其他进程的内存通讯库功能。您可以在以下网站上了解有关它们的更多信息以及如何在基于Linux的操作系统中的C语言中使用它们:

Linux Interprocess Communications

很抱歉,由于我还没有在通用的桌面操作系统上使用过内部进程间通信,因此无法进行详细说明。

还有一个更重量级的选项可以使用套接字API(通常甚至可以在遍布全球的不同PC网络中使用)。使用套接字API并不困难,并且在大学的网络讲座课程中有讲授。然后,您将获得“服务器”和“客户端”程序,并且必须注意一些必须按特定顺序调用的“原语”。 教程的常见来源似乎是Beej Guide

此保护机制称为内存虚拟化。它使每个进程都使用自己的虚拟地址空间,该空间不等于其他进程之一。或简短:不同程序中的相同地址将导致实际内存中的物理地址分开。

我认为这里的设计错误是编译两个不同的程序来完成任务。通常的做法是仅编译一个使用多线程(即执行多个线程)的程序,因为多线程比进程间通信更轻巧,并且可能更易于使用(至少我知道怎么做) )。对于多线程,可以在基于Linux的系统中使用pthread库,如果我没有记错的话,可以使用-pthread命令行选项将其包含在程序中。

祝您编程愉快!