反复取消引用多个指针,效率较低?

而不是写:

string name = first->next->next->next->name; int age = first->next->next->next->age; 

写作:

 node* billy_block = first->next->next->next; string name = billy_block->name; int age = billy_block->age; 

效率更高? 变量billy_block是否有可能被编译器“优化掉”?

我不懂编译器,所以请使用简单的术语。

任何自尊的现代编译器都会优化原始代码,以便在安全的情况下消除同一指针链的重复解除引用。

但是,首先,代码的原始版本比使用中间变量的代码的可读性低得多。 当指针解引用链很长时,对于人眼而言,链是相同的并不是显而易见的。 具有中间变量的变体明确表明我们想要从同一对象中读取nameage 。 在第一个变体中,它甚至不是很清楚。

其次,在混淆图像对于编译器而言不如您的情况那么明显的情况下,编译器可能不得不放弃优化。 例如,一般情况下你做的事情

 some_ptr->next = first->next->next->next->next; some_ptr->prev = first->next->next->next->prev; 

编译器无法确定第一个赋值是否不影响first->next->next->next 。 (考虑一下如果some_ptr等于first会发生什么。)这会强制编译器安全地播放它,并且每次从一开始就重新评估first->next->next->next 。 在这种情况下,引入中间变量确实会优化代码。 当然,为此,您必须确保这是正确的事情,即在您的程序中使用您自己的可能别名的知识。

编译器的优化取决于您使用的编译器以及传递给它的优化标志。 您始终可以检查编译器输出的汇编代码,以查看它所执行的操作。

例如,随Xcode一起安装的编译器(Apple LLVM版本6.1.0(clang-602.0.53)(基于LLVM 3.6.0svn)),以及node的适当定义,即使使用,也不会删除重复的访问链最高优化设置:

  movq %rdi, %rbx movq (%rbx), %rax movq (%rax), %rax movq (%rax), %rsi addq $8, %rsi leaq -40(%rbp), %r14 movq %r14, %rdi callq __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_ movq (%rbx), %rax movq (%rax), %rax movq (%rax), %rax movl 32(%rax), %ebx 

如果您使用临时变量虽然编译器只是重用了寄存器%rbx ,那么您可以免费获得临时变量:

  movq (%rdi), %rax movq (%rax), %rax movq (%rax), %rbx leaq 8(%rbx), %rsi leaq -40(%rbp), %r14 movq %r14, %rdi callq __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_ movl 32(%rbx), %ebx 

这是测试程序:

 #include  using namespace std; string myname; struct node { node *next; string name; int age; }; int foo1(node *first) { string name = first->next->next->next->name; int age = first->next->next->next->age; myname=name; return age; } int foo2(node *first) { node* billy_block = first->next->next->next; string name = billy_block->name; int age = billy_block->age; myname=name; return age; } 

编译器选项g++ -Wall -S -O3 test.cpp

编译器只能合并两个加载:

 int x = *p; ...; int y = *p; 

如果全部:

  • p不是指向volatile的指针。
  • ...期间没有可能别名的写入。

别名分析的问题非常困难 ,所以这主要意味着任何写入。 另一方面,创建仅分配一次然后读取零次或多次的其他变量的问题基本上是编译器的工作方式,因此从不犹豫地添加临时变量。


但是,如果有人遇到这个答案并使用C ++,请注意析构函数的排序。