我经常注意到以下模式:
for (int i = 0; i < strlen(str); ++i) {
// do some operations on string
}
上述循环的复杂性为O(N²)
,因为strlen
的复杂性为N
,并且每次迭代都会进行比较。
但是,如果我们在循环之前计算strlen
并使用该常量,则循环的复杂性将减少到O(N)
。
我相信还有很多其他优化措施。
编译器是否进行了这样的优化,或者程序员是否必须采取预防措施来防止它?
答案 0 :(得分:1)
虽然我没有任何确凿的证据,但我的猜测是:
编译器对变量str
进行数据流分析。如果它可能在循环内被修改或标记为volatile
,则无法保证strlen(str)
在迭代之间保持不变,因此无法缓存。否则,缓存应该是安全的,并且会进行优化。
答案 1 :(得分:1)
是的,好的优化器能够做到这种转换,如果他们可以确定字符串在循环体中保持不变。它们可以拉出保持不变的循环表达式。
无论如何,如果您将所有字符都转换为大写字母,编译器很难推断字符串长度不会发生变化。
我个人赞成&#34;防守&#34;方法,不依赖于高级编译技能,并自己做明显的优化。如果代码将被移植到不同的环境,使用较差的编译器,或者只是怀疑。
还要考虑优化已关闭的情况。
答案 2 :(得分:0)
了解编译器做什么,可以做什么,不能做什么的第一步是将您的意图写入代码并看看会发生什么:
const int len = strlen(str);
for (int i=0; i<len; ++I)
{
// do some operations which DO NOT CHANGE the length of str
}
当然,你有责任不改变循环中str的长度......你可以小写,大写,交换或替换字符......你可能会断言()&#39;如果你真的关心(在调试版本中)。在这种情况下,您将您的意图传达给编译器,如果您很幸运并且使用了一个好的编译器,那么您可能会得到您想要的东西。
我真的怀疑这种优化会对你的代码产生任何影响:如果你正在进行繁重的字符串操作,你会(1)知道你是使用长字符串还是短字符串,(2)使用一个库( a)明确跟踪字符串的长度,(b)使(特别是重复的)字符串连接更便宜,因为它在C中。
答案 3 :(得分:0)
尝试
for (int i = 0; str[i]; ++i) {
// do some operations on string
}
由于strlen
基本上是这样做的