我试图准确地理解C的基本知识,volatile
关键字。
它似乎不像我想的那样工作。 我在某处阅读:
volatile
指定可以通过操作修改与var相关的值 除了附近代码中的代码。 当声明varvolatile
时,编译器从
重新加载值。 程序每次访问它时都会存储。 这会阻止通过优化编译器进行优化。 主要用于确保可预测的程序行为。
我有两个程序,是从两个不同的.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
程序)?
否则它无法正常工作。
答案 0 :(得分:4)
附近代码中未执行的操作
是;这意味着能够修改相应内存块的事物。通常,在同一程序中这将是不同的 thread 。在某些非常特定于平台的情况下,您可以将变量安排在对OS(或硬件)具有特殊意义的特定位置。例如,在旧的掌上电脑或控制台上,您可以从代表I / O寄存器的虚拟内存位置读取信息,以了解按下键盘上的哪个按钮。通过使用volatile
,编译器将理解必须检查该值,因为硬件可能已写入该内存,并且您的程序依赖于了解更改。
在现代台式计算机上,程序具有内存保护和内存空间虚拟化。他们无法轻松查看彼此的数据,因此通常需要进行一些特殊的安排/权限才能这样做。但是即使有可能,您也不能仅通过使用其名称来简单地检查另一个程序的变量-该名称仅对编译器有意义。它不是已编译程序的实际部分(尽管调试模式下的编译可能会尝试使其表现得有点类似)。
答案 1 :(得分:3)
如果我理解正确,您尝试在另一个程序中使用一个程序的内存吗?在任何现代操作系统上,“它都不会这样工作”,因为程序(或更好的是:“进程”)被有目的地封装,这样,如果没有特定于操作系统的 interprocess,它们就无法访问其他进程的内存通讯库功能。您可以在以下网站上了解有关它们的更多信息以及如何在基于Linux的操作系统中的C语言中使用它们:
Linux Interprocess Communications
很抱歉,由于我还没有在通用的桌面操作系统上使用过内部进程间通信,因此无法进行详细说明。
还有一个更重量级的选项可以使用套接字API(通常甚至可以在遍布全球的不同PC网络中使用)。使用套接字API并不困难,并且在大学的网络讲座课程中有讲授。然后,您将获得“服务器”和“客户端”程序,并且必须注意一些必须按特定顺序调用的“原语”。 教程的常见来源似乎是Beej Guide。
此保护机制称为内存虚拟化。它使每个进程都使用自己的虚拟地址空间,该空间不等于其他进程之一。或简短:不同程序中的相同地址将导致实际内存中的物理地址分开。
我认为这里的设计错误是编译两个不同的程序来完成任务。通常的做法是仅编译一个使用多线程(即执行多个线程)的程序,因为多线程比进程间通信更轻巧,并且可能更易于使用(至少我知道怎么做) )。对于多线程,可以在基于Linux的系统中使用pthread库,如果我没有记错的话,可以使用-pthread命令行选项将其包含在程序中。
祝您编程愉快!