C语言中声明一个"register volatile int i"变量会有什么行为表现?

12

我理解register关键字将为计算值分配一个寄存器,而volatile关键字将在每次对变量进行计算时从内存中读取该值,并基本上不会优化代码。因此,如果变量同时被分配这两个关键字,那么它是否意味着它本身本质上是volatile的?通过编写示例代码我无法理解其行为。有人能解释一下吗?


3
什么平台和编译器?我不认为大多数通用编译器(如默认配置下的GCC)会遵循 register 命令。 - Joe
2
register 声明的变量不能被地址访问。 - Daniel Fischer
@Daniel:我不理解你的观点。无法像什么一样获取变量的地址?你是说可以通过指针访问它吗? - knightofhorizon
2
如果一个变量声明为register int some_var;(或者register volatile int some_var;),在源代码中使用&some_var将会违反限制,并要求进行诊断(编译器是否中止编译取决于编译器,但你的程序不符合规范)。 - Daniel Fischer
2个回答

14

在C语言中,register存储类的行为与auto存储类完全相同,唯一的区别是如果程序尝试获取或使用该对象的地址(6.5.3.2、6.7.1),实现必须(根据5.1.1.3)发出诊断警告。将register用作编译器优化提示通常没有意义,因为足够聪明以利用对象无需存储的编译器肯定能够跟踪哪些对象可以声明为register;相反,它应被理解为代码质量检查,以确保程序员没有通过获取对象地址无意中破坏了优化机会。

换句话说,从一个有效程序中删除所有register关键字对程序的语义没有影响;在这方面,它类似于static_assert

volatile类型限定符表示访问(读取和写入)该对象被视为副作用,不能被优化掉。对于不存在于定义位置内存中的对象(即具有register存储类的对象),这在性能测试中非常有用:

start = time();
for (multiple loops)
    register volatile int result = test_function();
stop = time();
elapsed = stop - start;

1
@dasblinkenlight 没有任何区别,因为程序是有效的。 - ecatmur
volatile 关键字的更易理解的观点是,它告诉编译器,每当指定变量时,编译器应该假设变量可能已经被其他进程、线程或设备更改。编译器在优化时可能通过假设如果变量值被加载到寄存器中,则可以依赖寄存器中的值作为当前值的副本来消除重复访问变量,直到编译器找到修改变量的源代码为止。Volatile 告诉编译器这个假设是无效的。 - Richard Chambers
实际上,寄存器变量具有一个auto变量没有的特性:您无法获取寄存器变量的地址。 - Richard J. Ross III
1
@RichardChambers 没错,但对于“寄存器(register)”对象来说,这种做法是没有意义的,因为它没有在内存中定义的位置。 - ecatmur
@RichardChambers:如果不获取易失性对象的地址,并且它具有自动存储期限,那么其他线程/设备等基本上无法修改该对象。可以争论一下,调试器可能会算在内。否则,根据“as-if rule”,C语言实现可能完全优化掉这些易失性对象,因为没有办法观察到它们的存在。 - R.. GitHub STOP HELPING ICE
显示剩余3条评论

6

volatile表示对象可能以编译器无法预测的方式发生改变,而register则表示其地址无法被获取。

当该对象实际上是平台上的硬件寄存器时,这两者的结合就有了完美的意义。一些编译器(如gcc)甚至有扩展来将这样的变量固定到特定的硬件寄存器上。

其他一些代码可能会更改这样的硬件寄存器,因此编译器可能对当前值不做任何假设。在这种情况下,添加一个const限定符甚至也是有意义的。例如,在gcc的扩展中使用。

register uint32_t volatile const eax __asm__("eax");

你可以随时使用工具检查eax寄存器,但不会意外更改它。


1
“volatile” 还意味着变量的所有写操作必须实际发生。 - Tor Klingberg
@TorKlingberg,你所说的“写入”register变量是什么意思?这种变量没有“存储”模型,因此也没有“读取”或“写入”的模型。 - Jens Gustedt
@JensGustedt:在使用setjmp的函数中,我认为需要一个具有volatileregister限定符的对象,以反映在两次从setjmp返回之间所做的任何更改,而仅仅带有register限定符的对象则不能做到这点。 - supercat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接