113

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

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

strlen()每次加的时候会计算i吗?

4

18 回答 18

144

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

我会做类似的事情

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

或者可能

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

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

于 2012-07-06T15:24:31.567 回答
14

是的,每次你使用循环。然后它每次都会计算字符串的长度。所以像这样使用它:

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

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

有关更多信息,请参阅此链接

在下面的代码中,每次循环运行strlen时都会计算整个字符串的长度,效率较低,需要更多时间并占用更多内存。

char str[];
for ( int i = 0; i < strlen(str); i++)
{
//Something;
}
于 2012-07-06T15:31:10.243 回答
9

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

除此之外,编译器必须知道,这strlen(ss)不会改变。ss仅当未在for循环中更改时才如此。

ss例如,如果您在in循环中使用只读函数for但未将ss-parameter 声明为const,则编译器甚至无法知道ss循环中没有更改,并且必须strlen(ss)在每次迭代中进行计算。

于 2012-07-06T15:23:26.877 回答
4

如果ss是类型const char *并且你没有抛弃const循环中的ness strlen,如果打开了优化,编译器可能只会调用一次。但这当然不是可以指望的行为。

您应该将strlen结果保存在一个变量中并在循环中使用该变量。如果您不想创建一个额外的变量,这取决于您在做什么,您可能会通过反转循环来向后迭代而侥幸逃脱。

for( auto i = strlen(s); i > 0; --i ) {
  // do whatever
  // remember value of s[strlen(s)] is the terminating NULL character
}
于 2012-07-06T15:29:01.073 回答
3

形式上是的,strlen()预计每次迭代都会被调用。

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

于 2012-07-06T15:21:24.013 回答
3

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

  1. 该功能strlen无副作用
  2. 指向的内存ss在循环期间不会改变

编译器不知道这些事情中的任何一个,因此无法安全地记住第一次调用的结果

于 2012-07-06T15:22:55.917 回答
2

的。每次 i 增加时都会计算 strlen。

如果您没有在循环中更改 ss with ,则意味着它不会影响逻辑,否则会影响。

使用以下代码更安全。

int length = strlen(ss);

for ( int i = 0; i < length ; ++ i )
{
 // blabla
}
于 2012-07-06T15:23:13.740 回答
2

是的,strlen(ss)每次代码运行时都会计算。

于 2012-07-06T22:03:21.153 回答
2

是的,strlen(ss)它将在每次迭代时计算长度。如果您ss以某种方式增加i; 会有无限循环。

于 2012-07-07T11:04:20.783 回答
2

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

如果您想提高效率,请始终记住将所有内容保存在局部变量中...这需要时间,但它非常有用..

您可以使用如下代码:

String str="ss";
int l = strlen(str);

for ( int i = 0; i < l ; i++ )
{
    // blablabla
}
于 2012-07-11T09:06:39.563 回答
2

现在不常见,但 20 年前在 16 位平台上,我推荐这个:

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

即使您的编译器在优化方面不是很聪明,上面的代码也可以生成好的汇编代码。

于 2012-07-11T13:24:44.953 回答
1

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

int stringLength = strlen (ss); 
for ( int i = 0; i < stringLength; ++ i ) 
{
  // blabla 
} 
于 2012-07-06T15:23:04.233 回答
1

Arrgh,即使在理想的情况下,它也会,该死的!

截至今天(2018 年 1 月),gcc 7.3 和 clang 5.0,如果你编译:

#include <string.h>

void bar(char c);

void foo(const char* __restrict__ ss) 
{
    for (int i = 0; i < strlen(ss); ++i) 
    {
        bar(*ss);
    }
}    

所以,我们有:

  • ss是一个常量指针。
  • ss被标记__restrict__
  • 循环体不能以任何方式触及ss(好吧,除非它违反__restrict__)指向的内存。

而且,两个编译器都会执行strlen() 循环的每一次迭代。惊人的。

这也意味着@Praetorian 和@JaredPar 的典故/一厢情愿的想法不会成功。

于 2018-01-28T00:27:59.747 回答
0

是的,简单来说。并且编译器希望在极少数情况下作为优化步骤,如果它发现根本没有进行任何更改ss。但在安全条件下,您应该认为它是 YES。在某些情况下,例如multithreaded事件驱动程序,如果您认为它是“否”,它可能会出现错误。谨慎行事,因为它不会过多地提高程序的复杂性。

于 2012-07-10T19:01:17.563 回答
0

是的。

strlen()每次i增加且未优化时计算。

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

for ( int i = 0; i < strlen(ss); ++i )
{
   // Change ss string.
   ss[i] = 'a'; // Compiler should not optimize strlen().
}
于 2012-07-11T11:12:08.310 回答
0

我们可以很容易地测试它:

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

循环条件在每次重复后评估,然后重新开始循环。

还要注意用于处理字符串长度的类型。它应该是在 stdio 中size_t定义的。unsigned int比较和转换它int可能会导致一些严重的漏洞问题。

于 2012-07-12T12:29:48.070 回答
0

好吧,我注意到有人说它默认由任何“聪明”的现代编译器优化。顺便看看没有优化的结果。我试过:
最小的 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
     .....
于 2012-07-18T10:07:04.090 回答
0

详细说明 Prætorian 的回答,我建议以下内容:

for( auto i = strlen(s)-1; i > 0; --i ) {foo(s[i-1];}
  • auto因为您不想关心 strlen 返回的类型。C++11 编译器(例如gcc -std=c++0x,不完全是 C++11,但自动类型可以工作)会为你做这件事。
  • i = strlen(s)因为你想比较0(见下文)
  • i > 0因为与 0 的比较比与任何其他数字的比较(略)快。

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

于 2012-07-18T11:16:23.960 回答