如果在循环条件下使用,strlen会被多次计算吗?

我不确定以下代码是否会导致冗余计算,还是编译器特定的?

for (int i = 0; i < strlen(ss); ++i) { // blabla } 

每次i增加时都会计算strlen()吗?

是的, strlen()将在每次迭代中被评估。 有可能在理想情况下,优化器可能会推断出价值不会改变,但我个人不会依赖这个。

我会做一些像

 for (int i = 0, n = strlen(ss); i < n; ++i) 

或者可能

 for (int i = 0; ss[i]; ++i) 

只要string在迭代期间不会改变长度。 如果可能,那么你需要每次调用strlen() ,或者通过更复杂的逻辑来处理它。

是的,每次使用循环。 然后每次计算string的长度。 所以像这样使用它:

 char str[30]; for ( int i = 0; str[i] != '\0'; i++) { //Something; } 

在上面的代码中, str[i]只在每次循环开始一个循环时validation位置i中的string中的一个特定字符,因此它将占用较less的内存并且更高效。

看到这个链接了解更多信息。

在下面的代码中,每次循环运行时, strlen都会计算整个string的长度,效率更低,花费更多的时间和更多的内存。

 char str[]; for ( int i = 0; i < strlen(str); i++) { //Something; } 

一个好的编译器可能不会每次计算它,但是我不认为你可以确定每个编译器都这么做。

除此之外,编译器必须知道,strlen(ss)不会改变。 只有在for循环中没有改变ss时,这才是真实的。

例如,如果在for循环的ss中使用只读函数,但不要将ss参数声明为const,那么编译器甚至不能知道该循环中ss是否被更改,并且必须计算strlen( ss)在每个迭代中

如果ss的types是const char *并且不是在循环中强制转换,那么编译器可能只会调用strlen一次,如果优化打开的话。 但这当然不是可以指望的行为。

您应该将strlen结果保存在一个variables中,并在循环中使用该variables。 如果你不想创build一个额外的variables,取决于你在做什么,你可能会脱胎换骨,以反向迭代。

 for( auto i = strlen(s); i > 0; --i ) { // do whatever // remember value of s[strlen(s)] is the terminating NULL character } 

正式是, strlen()预计将被调用每个迭代。

无论如何,我不想否定一些聪明的编译器优化的存在的可能性,这将优化掉第一个之后的任何连续调用strlen()。

整个谓词代码将在for循环的每次迭代中执行。 为了记忆strlen(ss)调用的结果,编译器至less需要知道

  1. 函数strlen是无副作用的
  2. ss指向的内存在循环的持续时间内不会改变

编译器不知道这些事情,因此不能安全地记忆第一次调用的结果

是的,每当代码运行时,strlen(ss)都会被计算出来。

是的, strlen(ss)会计算每次迭代的长度。 如果你通过某种方式增加ss ,并增加i ; 会有无限的循环。

是的, 每次评估循环都会调用strlen()函数。

如果你想提高效率,那么一定要记住将所有内容保存在局部variables中……这将需要时间,但是非常有用。

你可以使用下面的代码:

 String str="ss"; int l = strlen(str); for ( int i = 0; i < l ; i++ ) { // blablabla } 

现在不常见,但20年前在16位平台上,我build议这样做:

for(char * p = str; * p; p ++){}

如果你的编译器在优化方面不是很聪明的话,上面的代码可以产生很好的汇编代码。

是。 testing不知道ss在循环内部没有改变。 如果你知道它不会改变,那么我会写:

 int stringLength = strlen (ss); for ( int i = 0; i < stringLength; ++ i ) { // blabla } 

是的 。 每增加一次,strlen就会被计算出来。

如果你没有改变 循环意味着它不会影响逻辑,否则会影响。

使用下面的代码更安全。

 int length = strlen(ss); for ( int i = 0; i < length ; ++ i ) { // blabla } 

是的,用简单的话来说。 在编译器希望的极less情况下,如果发现ss完全没有改变,就作为优化步骤。 但在安全的情况下,你应该认为是的。 在multithreaded和事件驱动的程序中有一些情况,如果你认为它是NO,它可能会出错。 因为它不会太多地提高程序的复杂性,所以玩起来很安全。

是。

strlen()计算每当i增加,并没有优化。

下面的代码显示了为什么编译器不应该优化strlen()

 for ( int i = 0; i < strlen(ss); ++i ) { // Change ss string. ss[i] = 'a'; // Compiler should not optimize strlen(). } 

我们可以很容易地testing它:

 char nums[] = "0123456789"; size_t end; int i; for( i=0, end=strlen(nums); i<strlen(nums); i++ ) { putchar( nums[i] ); num[--end] = 0; } 

循环条件在每次重复之后,在重新启动循环之前进行评估。

另外要小心你用来处理string长度的types。 它应该是在stdio中被定义为unsigned int size_t 。 比较并将其转换为int可能会导致一些严重的漏洞问题。

好吧,我注意到有人说这是由任何“聪明”的现代编译器默认优化。 顺便看看没有优化的结果。 我试过了:
最小的C代码:

 #include <stdio.h> #include <string.h> int main() { char *s="aaaa"; for (int i=0; i<strlen(s);i++) printf ("a"); return 0; } 

我的编译器:g ++(Ubuntu / Linaro 4.6.3-1ubuntu5)4.6.3
生成汇编代码的命令:g ++ -S -masm = intel test.cpp

 Gotten assembly code at the output: ... L3: mov DWORD PTR [esp], 97 call putchar add DWORD PTR [esp+40], 1 .L2: THIS LOOP IS HERE **<b>mov ebx, DWORD PTR [esp+40] mov eax, DWORD PTR [esp+44] mov DWORD PTR [esp+28], -1 mov edx, eax mov eax, 0 mov ecx, DWORD PTR [esp+28] mov edi, edx repnz scasb</b>** AS YOU CAN SEE it's done every time mov eax, ecx not eax sub eax, 1 cmp ebx, eax setb al test al, al jne .L3 mov eax, 0 ..... 

在Prætorian的答案详细阐述我build议如下:

 for( auto i = strlen(s)-1; i > 0; --i ) {foo(s[i-1];} 
  • auto因为你不想关心strlen返回的types。 一个C ++ 11编译器(例如, gcc -std=c++0x ,不完全是C ++ 11,但是自动types工作)将为你做。
  • i = strlen(s)因为你想比较0 (见下文)
  • i > 0因为与0比较(稍微)比任何其他数字都要快。

缺点是您必须使用i-1才能访问string字符。