这是一道面试题。我说它们是一样的,但是这被裁定为不正确的回答。从汇编语言的角度来看,有什么想象得到的区别吗?我使用默认的gcc优化和-S编译了两个简短的C程序,以查看汇编输出,它们是相同的。
这是一道面试题。我说它们是一样的,但是这被裁定为不正确的回答。从汇编语言的角度来看,有什么想象得到的区别吗?我使用默认的gcc优化和-S编译了两个简短的C程序,以查看汇编输出,它们是相同的。
i=i+1
需要加载i
的值,加上 1 ,再将结果存回i
。相比之下,++i
只需使用一条汇编指令递增该值,因此理论上它可能更高效。不过,大多数编译器会优化掉这种差异,生成的代码完全相同。
顺便说一下,你懂得如何查看汇编代码,让你比我在过去面试的 90% 的人都优秀。安慰自己的是,你不必和那些毫无头绪的失败者共事。
int i
,如果您的编译器在这些语句中产生了次优汇编代码(当然是启用了优化的情况下),那么就是一个错误,应该进行修复。我们关注的主流ISA编译器(如gcc、clang、MSVC、ICC)都没有问题。不能指望这个C语句编译成任何特定的汇编语言:取决于周围的代码,循环可能会展开,或者增量可以折叠到某些其他操作中。 - Peter Cordes_Atomic int i
,它们并不相同,但这基本上就像C++中的重载运算符。 - Peter Cordes看起来你是对的,而他们是错的。我在一次面试中也遇到了类似的问题,我回答了正确的答案,然而却被认为是错误的。
我自信地与我的面试官争论这个问题,但显然他非常反感我的鲁莽。虽然我没有得到那份工作,但是想想在一个“什么都知道”的人下工作也并不是很理想。
您可能是正确的。一个天真的编译器可能会这样做:
++i to inc [ax]
并且
i = i + 1 to add [ax], 1
但任何半明智的编译器都会优化将1添加到第一个版本。
这一切都假设相关架构具有inc和add指令(像x86一样)。
[ax]
不是有效的 16 位寻址模式。[bx]
是,[eax]
是一个有效的 32 位寻址模式。无论如何,有趣的事实是,在现代英特尔 CPU 上,add [mem], 1
是 add
比 inc
更有效率的一个例子。 INC指令 vs ADD 1: 有关吗? 但如果您的优化编译器生成的汇编代码依赖于这种源码差异,那么您的编译器就存在错误(错过了优化的漏洞)。像 inc
这样的 peephole 应该由编译器后端自行处理,根据情况使用或不使用(如果它们实际上更慢)。 - Peter Cordes++i;
i = i + 1;
还有更多的背景信息吗?
如果我被问到这个问题,我的第一反应会是“它是否是易失性的?” 如果答案是肯定的,那么差异就很大。 如果不是,差别很小,仅仅是语义上的区别,实际上并没有什么影响。 这一点可以从解析树的差异和生成的子树的最终含义得出证明。
所以听起来你在实用方面做得很好,但在语义/批判性思考方面可能存在问题。
在没有上下文的情况下攻击面试官,我会想知道问题的目的是什么。 如果我问这个问题,我想用它来了解候选人是否了解微妙的语义差异,如何生成解析树,如何进行批判性思考等等。 我通常会问我的受访者一个C语言问题,几乎每个候选人都会回答错误-这是有意而为之的。 实际上,我并不在意问题的答案,我在意的是我将与候选人一起走过的旅程,这告诉我比一道琐碎问题的对错更多。
volatile int i
和普通的 int i
有什么区别?大多数编译器都会将它们编译成相同的代码。C标准/ as-if规则并没有指定使用单个指令进行内存目标增量(原子 wrt.中断但不是其他核心)。GCC 错过了 使用单个内存目标指令执行 ++i
或 i=i+1
的优化,但clang对于volatile和非volatile都可以实现:https://godbolt.org/z/j46qGF。因此,在两个编译器中,`++i` 和 i=i+1
之间仍然没有区别,只有在volatile和非volatile之间才有区别。 - Peter Cordesvolatile
使得++i
有很大的不同? - Peter Cordes在C++中,这取决于i
是一个int还是一个对象。如果它是一个对象,它可能会生成一个临时实例。
这里的上下文很重要,因为在优化的发布版本中,如果 i++ 可用于简单的 [inc eax],编译器将对其进行优化。而像 int some_int = i++ 这样的语句需要先将 i 的值存储在 some_int 中,然后才能递增 i。