为何GCC与Clang未将strlen从该循环中提升出去?
为啥
strlen(ss)没法被优化出循环? 先看这段代码:
#include <string.h> void bar(char c); void foo(const char* restrict ss) { for (int i = 0; i < strlen(ss); ++i) { bar(*ss); } }
乍一看,ss是restrict限定的const char*,按说编译器应该能把strlen(ss)从循环里提出来——毕竟循环里看起来没修改ss指向的内容,对吧?但实际情况是,哪怕开了最高优化级别-O3,Clang 5.0和GCC 7.3都没做这个优化,这背后的原因其实很关键:
- 问题出在
bar(*ss)这个函数调用上。虽然restrict保证了没有其他直接指向ss内存的指针,但编译器对bar的实现一无所知——它没法确定bar会不会通过间接方式修改ss指向的字符串。比如:bar可能引用了一个全局的char*变量,而这个变量刚好指向ss的内存区域;bar可能通过某些指针操作,修改了字符串里的字符,甚至把终止符'\0'给改了;- 只要
bar有修改ss指向内存的可能性,strlen(ss)的结果就可能在每次循环中变化,编译器绝对不敢把它提到循环外面——否则就会生成错误的代码。
那怎么才能让编译器做这个优化呢?有几种办法:
- 把
bar的实现放在同一个编译单元里,并且让编译器看到它确实没有修改ss指向的内存; - 给
bar加上__attribute__((const))(GCC/Clang扩展),告诉编译器这个函数没有副作用,既不修改任何外部状态,也不依赖除参数外的其他变量; - 如果
bar是个空函数或者只做纯计算,编译器就能放心地把strlen提出来了。
内容的提问来源于stack exchange,提问作者einpoklum
相关产品推荐
相关产品推荐

