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

时间:2022-10-24 23:43:51

I'm not sure if the following code can cause redundant calculations, or is it compiler-specific?

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

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

Will strlen() be calculated every time when i increases?

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

18 个解决方案

#1


134  

Yes, strlen() will be evaluated on each iteration. It's possible that, under ideal circumstances, the optimiser might be able to deduce that the value won't change, but I personally wouldn't rely on that.

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

I'd do something like

我喜欢做点什么

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

or possibly

或可能

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

as long as the string isn't going to change length during the iteration. If it might, then you'll need to either call strlen() each time, or handle it through more complicated logic.

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

#2


14  

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

In the above code str[i] only verifies one particular character in the string at location i each time the loop starts a cycle, thus it will take less memory and is more efficient.

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

See this Link for more information.

更多信息请参见此链接。

In the code below every time the loop runs strlen will count the length of the whole string which is less efficient, takes more time and takes more memory.

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

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

#3


9  

A good compiler may not calculate it every time, but I don't think you can be sure, that every compiler does it.

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

In addition to that, the compiler has to know, that strlen(ss) does not change. This is only true if ss is not changed in for loop.

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

For example, if you use a read-only function on ss in for loop but don't declare the ss-parameter as const, the compiler cannot even know that ss is not changed in the loop and has to calculate strlen(ss) in every iteration.

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

#4


4  

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.

如果ss是const char *类型,并且您并没有在循环中抛弃一致性,那么如果打开优化,编译器可能只调用一次strlen。但这肯定不是可以指望的行为。

You should save the strlen result in a variable and use this variable in the loop. If you don't want to create an additional variable, depending on what you're doing, you may be ale to get away with reversing the loop to iterate backwards.

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

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

#5


3  

Formally yes, strlen() is expected to be called for every iteration.

形式上是的,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.

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

#6


3  

The predicate code in it's entirety will be executed on every iteration of the for loop. In order to memoize the result of the strlen(ss) call the compiler would need to know that at least

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

  1. The function strlen was side effect free
  2. 函数strlen是免费的副作用。
  3. The memory pointed to by ss doesn't change for the duration of the loop
  4. ss指向的内存在循环期间不会改变

The compiler doesn't know either of these things and hence can't safely memoize the result of the first call

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

#7


2  

Yes, the strlen(ss) will calculate the length at each iteration. If you are increasing the ss by some way and also increasing the i; there would be infinite loop.

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

#8


2  

Yes, the strlen() function is called every time the loop is evaluated.

是的,每次计算循环时都会调用strlen()函数。

If you want to improve the efficiency then always remember to save everything in local variables... It will take time but it's very useful ..

如果你想提高效率,那就记住在局部变量中保存所有东西……这需要时间,但很有用。

You can use code like below:

您可以使用如下代码:

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

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

#9


2  

Yes, strlen(ss) will be calculated every time the code runs.

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

#10


2  

Not common nowadays but 20 years ago on 16 bit platforms, I'd recommend this:

现在不常见,但20年前,在16位平台上,我建议:

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

Even if your compiler isn't very smart in optimization, the above code can result in good assembly code yet.

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

#11


1  

Yes. The test doesn't know that ss doesn't get changed inside the loop. If you know that it won't change then I would write:

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

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

#12


1  

Yes. strlen will be calculated everytime when i increases.

是的。每次增加时,strlen都会被计算。

If you didn't change ss with in the loop means it won't affect logic otherwise it will affect.

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

It is safer to use following code.

使用以下代码更安全。

int length = strlen(ss);

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

#13


1  

Arrgh, it will, even under ideal circumstances, dammit!

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

从今天(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);
    }
}    

So, we have:

所以,我们有:

  • ss is a constant pointer.
  • ss是一个常数指针。
  • ss is marked __restrict__
  • 党*__restrict__标记
  • The loop body cannot in any way touch the memory pointed to by ss (well, unless it violates the __restrict__).
  • 循环体不能以任何方式接触到ss所指向的内存(嗯,除非它违反__limitt__)。

and still, both compilers execute strlen() every single iteration of that loop. Amazing.

而且,两个编译器都执行strlen()循环的每一个迭代。很神奇的。

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

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

#14


0  

YES, in simple words. And there is small no in rare condition in which compiler is wishing to, as an optimization step if it finds that there is no changes made in ss at all. But in safe condition you should think it as YES. There are some situation like in multithreaded and event driven program, it may get buggy if you consider it a NO. Play safe as it is not going to improve the program complexity too much.

是的,在简单的单词。而且编译器在罕见的情况下,如果发现在ss中没有任何变化,就会进行优化。但在安全的情况下,你应该认为是这样的。在多线程和事件驱动程序中有一些情况,如果您认为它是NO,那么它可能会有错误。确保安全,因为它不会过多地提高程序的复杂性。

#15


0  

Yes.

是的。

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

strlen()计算每次我增加和不优化的时候。

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

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

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

#16


0  

We can easily test it :

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

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

Loop condition evaluates after each repetition, before restarting the loop .

Also be careful about the type you use to handle length of strings . it should be size_t which has been defined as unsigned int in stdio. comparing and casting it to int might cause some serious vulnerability issue.

循环条件在每次重复之后计算,然后重新启动循环。还要注意处理字符串长度的类型。它应该是size_t,在stdio中定义为无符号int。将其与int进行比较和转换可能会导致一些严重的脆弱性问题。

#17


0  

well, I noticed that someone is saying that it is optimized by default by any "clever" modern compiler. By the way look at results without optimization. I tried:
Minimal C code:

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

#include <stdio.h>
#include <string.h>

int main()
{
 char *s="aaaa";

 for (int i=0; i<strlen(s);i++)
  printf ("a");
 return 0;
}

My compiler: g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
Command for generation of assembly code: g++ -S -masm=intel test.cpp

我的编译器: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
     .....

#18


0  

Elaborating on Prætorian's answer I recommend the following:

阐述在Prætorian回答我建议如下:

for( auto i = strlen(s)-1; i > 0; --i ) {foo(s[i-1];}
  • auto because you don't want to care about which type strlen returns. A C++11 compiler (e.g. gcc -std=c++0x, not completely C++11 but auto types work) will do that for you.
  • 自动,因为你不需要关心strlen返回的类型。一个c++ 11编译器(例如gcc -std=c++0x,不完全是c++ 11,但是自动类型可以)将为您做这些。
  • i = strlen(s) becuase you want to compare to 0 (see below)
  • i = strlen(s)因为要与0进行比较(见下面)
  • i > 0 because comparison to 0 is (slightly) faster that comparison to any other number.
  • 我> 0,因为与其他数字相比,0是(稍微)快一些。

disadvantage is that you have to use i-1 in order to access the string characters.

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

#1


134  

Yes, strlen() will be evaluated on each iteration. It's possible that, under ideal circumstances, the optimiser might be able to deduce that the value won't change, but I personally wouldn't rely on that.

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

I'd do something like

我喜欢做点什么

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

or possibly

或可能

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

as long as the string isn't going to change length during the iteration. If it might, then you'll need to either call strlen() each time, or handle it through more complicated logic.

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

#2


14  

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

In the above code str[i] only verifies one particular character in the string at location i each time the loop starts a cycle, thus it will take less memory and is more efficient.

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

See this Link for more information.

更多信息请参见此链接。

In the code below every time the loop runs strlen will count the length of the whole string which is less efficient, takes more time and takes more memory.

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

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

#3


9  

A good compiler may not calculate it every time, but I don't think you can be sure, that every compiler does it.

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

In addition to that, the compiler has to know, that strlen(ss) does not change. This is only true if ss is not changed in for loop.

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

For example, if you use a read-only function on ss in for loop but don't declare the ss-parameter as const, the compiler cannot even know that ss is not changed in the loop and has to calculate strlen(ss) in every iteration.

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

#4


4  

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.

如果ss是const char *类型,并且您并没有在循环中抛弃一致性,那么如果打开优化,编译器可能只调用一次strlen。但这肯定不是可以指望的行为。

You should save the strlen result in a variable and use this variable in the loop. If you don't want to create an additional variable, depending on what you're doing, you may be ale to get away with reversing the loop to iterate backwards.

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

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

#5


3  

Formally yes, strlen() is expected to be called for every iteration.

形式上是的,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.

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

#6


3  

The predicate code in it's entirety will be executed on every iteration of the for loop. In order to memoize the result of the strlen(ss) call the compiler would need to know that at least

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

  1. The function strlen was side effect free
  2. 函数strlen是免费的副作用。
  3. The memory pointed to by ss doesn't change for the duration of the loop
  4. ss指向的内存在循环期间不会改变

The compiler doesn't know either of these things and hence can't safely memoize the result of the first call

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

#7


2  

Yes, the strlen(ss) will calculate the length at each iteration. If you are increasing the ss by some way and also increasing the i; there would be infinite loop.

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

#8


2  

Yes, the strlen() function is called every time the loop is evaluated.

是的,每次计算循环时都会调用strlen()函数。

If you want to improve the efficiency then always remember to save everything in local variables... It will take time but it's very useful ..

如果你想提高效率,那就记住在局部变量中保存所有东西……这需要时间,但很有用。

You can use code like below:

您可以使用如下代码:

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

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

#9


2  

Yes, strlen(ss) will be calculated every time the code runs.

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

#10


2  

Not common nowadays but 20 years ago on 16 bit platforms, I'd recommend this:

现在不常见,但20年前,在16位平台上,我建议:

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

Even if your compiler isn't very smart in optimization, the above code can result in good assembly code yet.

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

#11


1  

Yes. The test doesn't know that ss doesn't get changed inside the loop. If you know that it won't change then I would write:

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

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

#12


1  

Yes. strlen will be calculated everytime when i increases.

是的。每次增加时,strlen都会被计算。

If you didn't change ss with in the loop means it won't affect logic otherwise it will affect.

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

It is safer to use following code.

使用以下代码更安全。

int length = strlen(ss);

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

#13


1  

Arrgh, it will, even under ideal circumstances, dammit!

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

从今天(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);
    }
}    

So, we have:

所以,我们有:

  • ss is a constant pointer.
  • ss是一个常数指针。
  • ss is marked __restrict__
  • 党*__restrict__标记
  • The loop body cannot in any way touch the memory pointed to by ss (well, unless it violates the __restrict__).
  • 循环体不能以任何方式接触到ss所指向的内存(嗯,除非它违反__limitt__)。

and still, both compilers execute strlen() every single iteration of that loop. Amazing.

而且,两个编译器都执行strlen()循环的每一个迭代。很神奇的。

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

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

#14


0  

YES, in simple words. And there is small no in rare condition in which compiler is wishing to, as an optimization step if it finds that there is no changes made in ss at all. But in safe condition you should think it as YES. There are some situation like in multithreaded and event driven program, it may get buggy if you consider it a NO. Play safe as it is not going to improve the program complexity too much.

是的,在简单的单词。而且编译器在罕见的情况下,如果发现在ss中没有任何变化,就会进行优化。但在安全的情况下,你应该认为是这样的。在多线程和事件驱动程序中有一些情况,如果您认为它是NO,那么它可能会有错误。确保安全,因为它不会过多地提高程序的复杂性。

#15


0  

Yes.

是的。

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

strlen()计算每次我增加和不优化的时候。

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

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

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

#16


0  

We can easily test it :

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

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

Loop condition evaluates after each repetition, before restarting the loop .

Also be careful about the type you use to handle length of strings . it should be size_t which has been defined as unsigned int in stdio. comparing and casting it to int might cause some serious vulnerability issue.

循环条件在每次重复之后计算,然后重新启动循环。还要注意处理字符串长度的类型。它应该是size_t,在stdio中定义为无符号int。将其与int进行比较和转换可能会导致一些严重的脆弱性问题。

#17


0  

well, I noticed that someone is saying that it is optimized by default by any "clever" modern compiler. By the way look at results without optimization. I tried:
Minimal C code:

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

#include <stdio.h>
#include <string.h>

int main()
{
 char *s="aaaa";

 for (int i=0; i<strlen(s);i++)
  printf ("a");
 return 0;
}

My compiler: g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
Command for generation of assembly code: g++ -S -masm=intel test.cpp

我的编译器: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
     .....

#18


0  

Elaborating on Prætorian's answer I recommend the following:

阐述在Prætorian回答我建议如下:

for( auto i = strlen(s)-1; i > 0; --i ) {foo(s[i-1];}
  • auto because you don't want to care about which type strlen returns. A C++11 compiler (e.g. gcc -std=c++0x, not completely C++11 but auto types work) will do that for you.
  • 自动,因为你不需要关心strlen返回的类型。一个c++ 11编译器(例如gcc -std=c++0x,不完全是c++ 11,但是自动类型可以)将为您做这些。
  • i = strlen(s) becuase you want to compare to 0 (see below)
  • i = strlen(s)因为要与0进行比较(见下面)
  • i > 0 because comparison to 0 is (slightly) faster that comparison to any other number.
  • 我> 0,因为与其他数字相比,0是(稍微)快一些。

disadvantage is that you have to use i-1 in order to access the string characters.

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