如果在循环条件下使用 strlen,是否需要多次计算?

我不确定下面的代码是否会导致冗余计算,或者它是特定于编译器的?

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

i增加时,是否每次都要计算 strlen()

8473 次浏览

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

Anyway I do not want to negate the possibility of the existance of some clever compiler optimisation, that will optimise away any successive call to strlen() after the first one.

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

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

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

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

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

是的。每次我增加的时候都会计算。

如果你的 没有改变 SS在圈子里意味着它 不会影响逻辑否则它将影响。

使用以下代码更安全。

int length = strlen(ss);


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

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

除此之外,编译器必须知道,strlen(ss)不会改变。只有在 for循环中没有更改 ss时才会出现这种情况。

例如,如果在 for循环中对 ss使用只读函数,但没有将 ss参数声明为 const,编译器甚至不能知道 ss在循环中没有更改,因此必须在每次迭代中计算 strlen(ss)

是的,strlen()将在每次迭代中进行计算。在理想情况下,优化者可能能够推断出值不会改变,但我个人不会依赖于此。

我会这么做

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

或者有可能

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

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

If ss is of type const char * and you're not casting away the constness within the loop the compiler might only call strlen once, if optimizations are turned on. But this is certainly not behavior that can be counted upon.

您应该将 strlen结果保存在一个变量中,并在循环中使用该变量。如果您不想创建一个额外的变量,这取决于您正在做什么,您可能需要逆转循环来反向迭代。

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

Yes, every time you use the loop. Then it will every time calculate the length of the string. so use it like this:

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;
}

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

是的,strlen(ss)将在每次迭代时计算长度。如果你通过某种方式增加了 ss,同时也增加了 i,那么就会有无限循环。

是的,简而言之。 编译器希望的情况很少,如果发现在 ss中根本没有任何改变,那么作为一个优化步骤就会有很小的改变。但是在安全的情况下,你应该认为它是。有一些情况,如在 multithreaded和事件驱动程序,它可能会得到错误,如果你认为它是一个否。 注意安全,因为它不会过多地提高程序的复杂性。

是的,strlen()函数被称为 每次都是,循环被求值。

如果你想提高效率,那么一定要记住把所有东西都保存在局部变量中... ... 这需要时间,但是非常有用。.

你可以使用如下代码:

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


for ( int i = 0; i < l ; i++ )
{
// blablabla
}

是的。

strlen() calculated everytime when i increases and does not optimized.

Below code shows why the compiler should not optimize strlen().

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

20年前在16位平台上并不常见,我建议这样做:

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

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

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

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

在重新启动循环之前,在每次重复之后计算循环条件。

还要注意用于处理字符串长度的类型。它应该是 size_t,这已经被定义为 unsigned int在演播室。将其与 int进行比较并将其转换为 int可能会导致一些严重的漏洞问题。< br >

我注意到有人说它是由任何“聪明的”现代编译器默认优化的。顺便说一下,不用优化就能看到结果。我试过: < br > 最小 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 = inteltest.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 的回答,我建议如下:

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

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

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

As of today (January 2018), and gcc 7.3 and clang 5.0, if you compile:

#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() 那个循环的每一次迭代

This also means the allusions/wishful thinking of @Praetorian and @JaredPar doesn't pan out.