我不确定以下代码是否会导致冗余计算,还是特定于编译器?
for (int i = 0; i < strlen(ss); ++i)
{
// blabla
}
每当strlen()
增加时,i
会计算出来吗?
答案 0 :(得分:135)
是的,每次迭代都会评估strlen()
。在理想的情况下,优化者可能会推断出价值不会发生变化,但我个人不会依赖于此。
我做的事情
for (int i = 0, n = strlen(ss); i < n; ++i)
或可能
for (int i = 0; ss[i]; ++i)
只要字符串在迭代期间不会改变长度。如果可能,那么您每次都需要调用strlen()
,或者通过更复杂的逻辑来处理它。
答案 1 :(得分:14)
是的,每次使用循环时。然后它会每次计算字符串的长度。 所以像这样使用它:
char str[30];
for ( int i = 0; str[i] != '\0'; i++)
{
//Something;
}
在上面的代码str[i]
中,每次循环开始一个循环时,只在位置i
处验证字符串中的一个特定字符,因此它将占用更少的内存并且效率更高。
有关详细信息,请参阅此Link。
在下面的代码中,每次循环运行strlen
都会计算整个字符串的长度,效率较低,需要更多时间并占用更多内存。
char str[];
for ( int i = 0; i < strlen(str); i++)
{
//Something;
}
答案 2 :(得分:9)
一个好的编译器可能不会每次都计算它,但我认为你不能确定,每个编译器都会这样做。
除此之外,编译器必须知道,strlen(ss)
不会改变。仅当ss
循环中未更改for
时才会出现这种情况。
例如,如果在ss
循环中的for
上使用只读函数但未将ss
- 参数声明为const
,则编译器不能甚至知道ss
在循环中没有改变,并且必须在每次迭代中计算strlen(ss)
。
答案 3 :(得分:4)
如果ss
类型为const char *
并且您没有丢弃循环中的const
,则编译器可能只调用strlen
一次,如果优化已转上。但这肯定不是可以指望的行为。
您应该将strlen
结果保存在变量中,并在循环中使用此变量。如果你不想创建一个额外的变量,取决于你正在做什么,你可能会逃避扭转循环以向后迭代。
for( auto i = strlen(s); i > 0; --i ) {
// do whatever
// remember value of s[strlen(s)] is the terminating NULL character
}
答案 4 :(得分:3)
整个谓词代码将在for
循环的每次迭代中执行。为了记住strlen(ss)
调用的结果,编译器需要知道至少
strlen
是无副作用的ss
指向的内存在循环持续时间内不会改变编译器不知道这些事情中的任何一个,因此无法安全地记住第一次调用的结果
答案 5 :(得分:3)
正式是的,预计每次迭代都会调用strlen()
。
无论如何,我不想否定存在一些聪明的编译器优化的可能性,这将优化掉第一个之后对strlen()的任何连续调用。
答案 6 :(得分:2)
是的,strlen(ss)
将计算每次迭代的长度。如果您以某种方式增加ss
并增加i
;会有无限循环。
答案 7 :(得分:2)
现在不常见,但20年前在16位平台上,我建议这样做:
for ( char* p = str; *p; p++ ) { /* ... */ }
即使您的编译器在优化方面不是很聪明,上面的代码也可以产生良好的汇编代码。
答案 8 :(得分:2)
是的,每次评估循环时,strlen()
函数都会被称为。
如果你想提高效率,那么请记住将所有内容保存在局部变量中......这需要时间,但它非常有用..
您可以使用以下代码:
String str="ss";
int l = strlen(str);
for ( int i = 0; i < l ; i++ )
{
// blablabla
}
答案 9 :(得分:2)
是的,每次代码运行时都会计算strlen(ss)
。
答案 10 :(得分:1)
是即可。每次增加时都会计算strlen。
如果你没有用更改ss 意味着不会影响逻辑,否则会影响。
使用以下代码更安全。
int length = strlen(ss);
for ( int i = 0; i < length ; ++ i )
{
// blabla
}
答案 11 :(得分:1)
是。测试不知道ss在循环内没有改变。如果你知道它不会改变那么我会写:
int stringLength = strlen (ss);
for ( int i = 0; i < stringLength; ++ i )
{
// blabla
}
答案 12 :(得分:1)
截至今天(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()
every single iteration of that loop。惊人的。
这也意味着@Praetorian和@JaredPar的典故/一厢情愿并没有成功。
答案 13 :(得分:0)
是的,简单来说就是这样。
并且在编译器希望的极少数条件下,如果它发现在ss
中根本没有进行任何更改,则作为优化步骤。但在安全的条件下,你应该认为是的。在multithreaded
和事件驱动程序中有一些情况,如果你认为它是NO,它可能会出错。
玩得安全,因为它不会太多地提高程序的复杂性。
答案 14 :(得分:0)
是
strlen()
每当i
增加且未优化时计算。
下面的代码说明了为什么编译器不应该优化strlen()
。
for ( int i = 0; i < strlen(ss); ++i )
{
// Change ss string.
ss[i] = 'a'; // Compiler should not optimize strlen().
}
答案 15 :(得分: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;
}
循环条件在每次重复之后进行评估,然后重新开始循环。
还要注意用于处理字符串长度的类型。它应该是size_t
,它在stdio中被定义为unsigned int
。比较并将其转换为int
可能会导致一些严重的漏洞问题。
答案 16 :(得分: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
.....
答案 17 :(得分: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
才能访问字符串字符。