23

从语法的角度来看,我已经看到“volatile”关键字在 C++ 函数中有多少用法?关于在函数上使用 volatile 关键字,但没有明确解释该问题中的案例 1 做了什么。只有一位受访者表示这似乎毫无意义/无用。

然而我不能完全接受这种说法,因为 GNUC 的 AES 软件实现已经使用了多年,并且它们具有许多如下功能:

INLINE volatile void functionname( /* ... */ ) {
    /* ... */
    asm( /* ... */ ) // embedded assembly statements
    /* ... */
}

这种用法一定是有原因的。任何人都可以:

一个。告诉我原来的原因是什么;和

。现在如何达到预期的效果?

我正在使用 Ubuntu 和 GCC 4.6.3。


注意: 我最接近的解释是在 GCC 2.5 之前,您可以通过以下方式欺骗在 2.5 中实现的“noreturn”属性:

void fatal( /* ... */ ) { /* ... */ exit(1); }

typedef void voidfn ();

volatile voidfn fatal;

这将允许编译器识别“致命”不会返回。

但这种情况似乎不适用于 AES 代码。我已经很久没有在装配中做任何事情了,但我想我会认出跳跃或类似的东西。

4

3 回答 3

15

根据gcc 文档(直到 2015 年 2 月)volatile void作为 C (但不是 C++)中的函数返回值等同__attribute__((noreturn))于函数,并告诉编译器该函数永远不会返回。

于 2013-01-12T00:56:51.743 回答
9

C99 标准在 §6.7.3/3 中说明了这一点:

与限定类型关联的属性仅对作为左值的表达式有意义。114)

114) 实现可能会放置一个const不在volatile只读存储区域中的对象。此外,如果从未使用过此类对象的地址,则实现不需要为此类对象分配存储空间

§6.2.5/19 说:

void类型包含一组空值;它是一个不完整的类型,无法完成。

§6.3.2.1/1 说:

左值是具有对象类型或不完整类型的表达式,而不是void; 53) [...]

因此,void不是左值,因此类型限定符(const、、volatilerestrict)对于类型的表达式没有意义void。因此,在任何符合 C99 的编译器中,const voidandvolatile void都是没有意义的(尽管指向const voidandconst volatile 的指针是有意义的)。

此外,§6.9.1/3 的约束不允许函数返回限定类型void

函数的返回类型应该是void数组类型以外的对象类型。

由于这是一个约束,因此符合要求的编译器必须发出诊断(§5.1.1.3/1)。volatile void所以在 C99 中不允许函数返回。

至于volatile void可能曾经做过什么,我不知道,也无法真正推测。我猜你正在查看的 AES 代码可能只是有一些从未清理过的旧代码。

于 2013-01-12T00:18:23.967 回答
7

参考

证据

  • volatile void function(...)不严格符合 C99。(感谢@Adam 和@ouah。@Adam 深入研究 C99 规范,@ouah 将我指向上面列出的 DR。)

  • GCC__attribute__((noreturn))在 2.5 版中volatile void作为volatile void. (GCC 文档。)

  • 上面引用的代码确实将控制权返回到调用它的位置,因为这些指令似乎没有操纵地址寄存器,也没有执行跳转命令。相反,它们将各种值加载到 32 位寄存器中。(代码检查。)

  • 第 323 到 333 行中的命令实现了支持加密操作的特殊操作码。(密码检查加上“挂锁”密码。)

  • 使用汇编函数的代码显然希望它们返回。(代码检查。)

  • noreturn属性告诉编译器该函数不会返回,因此编译器可以基于此进行优化。(GCC 文档。)

  • 来自 GCC 文档:不要假设调用函数保存的寄存器在调用 noreturn 函数之前已恢复。

解决方案

这是与一位同事的讨论,最终让我明白了。当一个函数声明它不会返回时,编译器必须做一些不同的事情。对 GCC 文档的检查证实了这一点。

A. 原由

你需要问自己以下问题。

问: AES 代码专门将值加载到 32 位寄存器中,并对它们执行操作。它如何将答案返回到代码的其余部分?

回答: GCC 优化意味着调用函数的寄存器,否则会在返回时覆盖值,不会被保存。汇编语言函数中的计算结果保留在寄存器中以供后续代码使用。

B. 现在达到预期效果:

几乎不管它。您唯一可以做的就是用 simple 替换volatile void返回类型void,并将noreturn属性添加到函数中。从理论上讲,这应该具有完全相同的效果。在实践中,它没有坏,不要修复它。

愿望

绝对不鼓励广泛使用这种技术。首先,它取决于每个编译器的定制。其次,这取决于那些编译器不会改变他们处理“不返回”情况的方式。第三,它可能会让后续的维护者感到困惑。

唯一有意义的情况是,当您利用高度专业化的机器代码来实现原本不可能的速度提升时。即便如此,也应该权衡取舍。

在这个例子中,正好支持两个编译器,并且只有当机器有特定的硬件支持可以利用时。否则,这一切都通过标准 C 代码处理。这是一个很大的努力。在你做之前确保它会得到回报。

于 2013-01-14T19:21:20.573 回答