如果在循环条件下使用,将多次计算 strlen
Will strlen be calculated multiple times if used in a loop condition?
我不确定以下代码是否会导致冗余计算,还是特定于编译器?
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)
只要字符串在迭代期间不会改变长度。如果可能,那么您需要每次都调用strlen()
,或者通过更复杂的逻辑来处理它。
是的,每次使用循环时。然后它每次都会计算字符串的长度。所以像这样使用它:
char str[30];
for ( int i = 0; str[i] != ' '; i++)
{
//Something;
}
在上面的代码中,str[i]
每次循环开始循环时,仅在位置i
验证字符串中的一个特定字符,因此它将占用更少的内存并且效率更高。
有关详细信息,请参阅此链接。
在下面的代码中,每次循环运行时,strlen
都会计算整个字符串的长度,效率较低,占用更多时间并占用更多内存。
char str[];
for ( int i = 0; i < strlen(str); i++)
{
//Something;
}
一个好的编译器可能不会每次都计算它,但我认为你不能确定,每个编译器都会这样做。
除此之外,编译器还必须知道strlen(ss)
不会更改。仅当ss
在循环中未更改for
时才如此。
例如,如果您在循环中使用ss
for
但未将ss
-参数声明为 const
,则编译器甚至无法知道循环中ss
没有更改,并且必须在每次迭代中计算strlen(ss)
。
如果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
}
形式上是的,每次迭代都会调用strlen()
。
无论如何,我不想否定存在一些聪明的编译器优化的可能性,这将优化第一个之后对 strlen() 的任何连续调用。
整个谓词代码将在 for
循环的每次迭代中执行。为了记住strlen(ss)
调用的结果,编译器至少需要知道
- 该功能
strlen
没有副作用 ss
指向的内存在循环期间不会改变
编译器不知道这两件事,因此无法安全地记住第一次调用的结果
是的。 每次 i 增加时都会计算 strlen
。如果你没有在循环中更改 ss,则意味着它不会影响逻辑,否则会影响。
使用以下代码更安全。
int length = strlen(ss);
for ( int i = 0; i < length ; ++ i )
{
// blabla
}
是的,strlen(ss)
将计算每次迭代的长度。如果您以某种方式增加ss
并增加i
;会有无限循环。
是的,每次计算循环时都会调用 strlen()
函数。
如果你想提高效率,那么永远记住将所有内容保存在局部变量中......这需要时间,但它非常有用..
您可以使用如下代码:
String str="ss";
int l = strlen(str);
for ( int i = 0; i < l ; i++ )
{
// blablabla
}
是的,每次代码运行时都会计算strlen(ss)
。
现在不常见,但 20 年前在 16 位平台上,我推荐这个:
for ( char* p = str; *p; p++ ) { /* ... */ }
即使您的编译器在优化方面不是很聪明,上面的代码也可以产生良好的汇编代码。
是的。测试不知道 ss 不会在循环内更改。如果你知道它不会改变,那么我会写:
int stringLength = strlen (ss);
for ( int i = 0; i < stringLength; ++ i )
{
// blabla
}
啊,即使在理想情况下,它也会该死!
截至今天(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的典故/一厢情愿的想法不会成功。
是的,简单来说。在极少数情况下,编译器希望这样做,如果它发现ss
根本没有进行任何更改,则将其作为优化步骤。但在安全的情况下,您应该将其视为是的。在某些情况下,例如在multithreaded
和事件驱动的程序中,如果您将其视为否,它可能会出错。安全运行,因为它不会过多地提高程序复杂性。
是的。
strlen()
每次i
增加且未优化时计算。
下面的代码显示了为什么编译器不应该优化strlen()
。
for ( int i = 0; i < strlen(ss); ++i )
{
// Change ss string.
ss[i] = 'a'; // Compiler should not optimize strlen().
}
我们可以很容易地测试它:
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
可能会导致一些严重的漏洞问题。
好吧,我注意到有人说它默认由任何"聪明"的现代编译器进行优化。顺便看看没有优化的结果。我试过:最小的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的答案,我推荐以下内容:
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
才能访问字符串字符。
- 为什么"do while"循环不断退出,即使条件计算结果为 false?
- 递归函数计算序列中的平方和(并输出过程)
- (C++)分析树以计算返回错误值的简单算术表达式
- 我的字符计数代码计算错误.为什么
- 在计算中使用二的幂有多有利可图
- 如何计算文件中的"columns"数?
- 计算排序向量的向量中唯一值的计数
- 如何使用 std::累积在 C++ 中计算总和立方体
- 使用Qt C++计算类似Git的SHA1哈希
- OpenCV C++.快速计算混淆矩阵
- cpp二进制搜索问题,计算给定数组中输入元素的出现次数
- C++如何计算用户输入的数字中的偶数位数
- 如何计算数据类型的范围,例如int
- 类似枚举的计算常量
- 计算每个节点的树高,帮助我解释这个代码解决方案
- 当 NUL 字符被定义为字符串的一部分时,为什么 strlen() 不计算终止 NUL 字符的字节?
- 如何使用 strlen 计算空间并在 sprintf 中加载
- 在不使用 strlen() 的情况下计算字符串中的字母
- 如果在循环条件下使用,将多次计算 strlen
- 将文件读取到char数组中,然后计算strlen会在C++中陷入困境