相关的问题已经被问过很多次了,但它们似乎都模糊地暗示了编译器可能会进行的优化,因此我们必须使用volatile来避免这些优化.因此,我在这里感兴趣的是理解编译器会做什么优化,或者,如果我没有在下面的代码片段中包含volatile,那么会(或可能)出错,该代码片段使用内存映射接口轮询键盘,然后,一旦接收到字符,就将其发送到显示器:

/* Define register addresses. */
#define KBD_DATA (volatile char *) 0x4000
#define KBD_STATUS (volatile char *) 0x4004
#define DISP_DATA (volatile char *) 0x4008
#define DISP_STATUS (volatile char *) 0x4012

void main() {
  char ch;
  /* Transfer the characters. */ 
  while (1) {
    while ((*KBD_STATUS & 0x2) == 0); 
    ch = *KBD_DATA;
    while ((*DISP_STATUS & 0x4) == 0); 
    *DISP_DATA = ch;
  } 
}

以上是Hamacher等人的Computer Organization篇文章.他们写下了以下内容:

请注意,KBD_STATUS和DISP_STATUS指针被声明为易失性指针.这是必要的,因为节目只有reads个相应位置的内容.不会向这些位置写入任何数据.优化编译器可以移除看起来没有影响的程序语句,该程序语句包括引用存储器中被读取但从未写入的位置的语句.由于内存映射的KBD_STATUS和DISP_STATUS寄存器的内容在程序外部的影响下发生变化,因此必须通知编译器这一事实.编译器不会删除涉及被声明为易失性的指针或其他变量的语句.

上面的重点是我的.

换一种说法,我不明白优化编译器如何或为什么可以在不改变程序语义的情况下删除引用KBD_STATUS和DISP_STATUS的程序语句.也就是说,为什么编译器要删除特别涉及这两个内存位置的语句?或者Hamacher等人说编译器可能会 Select read(一种说法,我知道这意味着"翻译为汇编,这是这样的......"),然后假设这个内存位置不会改变,这样就不需要从这些内存位置产生后续的汇编读指令了?

推荐答案

他们正在总结几个案子.

  1. 该变量有一个初始值.如果从未赋值,则它实际上是一个常量,编译器可以用该初始值替换对该变量的任何引用.
  2. 该变量没有初始值.在这种情况下,对变量的读取是不确定的,编译器可以将其视为具有任何值.因此,它可以删除读数并使用一些任意值.
  3. 即使它没有完全删除所有的读取,当有一个像您所显示的那样的循环时,它也可以假设该值在循环期间不会改变.所以像这样的循环
while ((*KBD_STATUS & 0x2) == 0); 

可以被编译,就好像它是

if ((*KBD_STATUS & 0x2) == 0) {
    while (1) ; // infinite loop
}

C++相关问答推荐

如何将匿名VLA分配给指针?

Bison解析器转移/减少冲突

C如何显示字符串数组中的第一个字母

InetPton()函数无效的IP地址

如何使用Python C API实现多线程程序?

自定义变参数函数的C预处置宏和警告 suppress ?

在循环中复制与删除相同条件代码的性能

对于C中给定数组中的每个查询,如何正确编码以输出给定索引范围(1到N)中所有数字的总和?

插座打开MacOS组件

强制转换变量以在 struct 中蚕食

C代码可以在在线编译器上运行,但不能在Leetcode上运行

C语言中奇怪的输出打印数组

如何修复我的qsort()算法?它每次都给出不同的结果

将char*数组深度复制到 struct 中?

I';我试着从.txt文件中读取文本,并用c计算其中的单词数量

如何不断地用C读取文件?

指向返回 struct 成员的指针,安全吗?

#define X Defined(Y) 是有效的 C/C++ 宏定义吗?

如何使用 VLA 语法使用 const 指针声明函数

C 预处理器中的标记分隔符列表