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]に location の文字列内の特定の 1 文字のみを検証するため、必要なメモリが少なくなり、より効率的になります。i

詳細については、このリンクを参照してください。

以下のコードでは、ループが実行されるたびにstrlen文字列全体の長さをカウントしますが、これは効率が悪く、より多くの時間とメモリを必要とします。

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

優れたコンパイラーは毎回計算するとは限りませんが、すべてのコンパイラーがそれを行うと確信できるとは思いません。

それに加えて、コンパイラはそれstrlen(ss)が変わらないことを知る必要があります。これは、がループssで変更されていない場合にのみ当てはまります。for

たとえば、読み取り専用関数をssinループで使用するが、 -parameter を としてfor宣言しない場合、コンパイラはそれがループで変更されていないことさえ認識できず、すべての反復で計算する必要があります。ssconstssstrlen(ss)

于 2012-07-06T15:23:26.877 に答える
4

ssが型const char *であり、ループ内でネスをキャストしていない場合、最適化がオンになっている場合const、コンパイラは 1 回だけ呼び出す可能性があります。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

最近では一般的ではありませんが、20 年前の 16 ビット プラットフォームでは、次のことをお勧めします。

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

コンパイラの最適化があまり賢くなくても、上記のコードで適切なアセンブリ コードが得られる可能性があります。

于 2012-07-11T13:24:44.953 に答える
2

はい。strlen は、i が増加するたびに計算されます。

ループ内でssを変更しなかった場合は、ロジックに影響しないことを意味し、そうでない場合は影響します。

次のコードを使用する方が安全です。

int length = strlen(ss);

for ( int i = 0; i < length ; ++ i )
{
 // blabla
}
于 2012-07-06T15:23:13.740 に答える
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

はい、strlen(ss)コードが実行されるたびに計算されます。

于 2012-07-06T22:03:21.153 に答える
1

はい。テストは、ループ内で ss が変更されないことを知りません。変わらないことがわかっている場合は、次のように書きます。

int stringLength = strlen (ss); 
for ( int i = 0; i < stringLength; ++ i ) 
{
  // blabla 
} 
于 2012-07-06T15:23:04.233 に答える
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

はい。

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

はい、簡単に言えば。また、変更がまったく行われていないことがわかった場合の最適化ステップとして、コンパイラが望んでいるまれな状況では小さな no がありssます。しかし、安全な状態では、YES と考えるべきです。イベント駆動型プログラムのような状況がいくつかありmultithreaded、NOと見なすとバグが発生する可能性があります。プログラムの複雑さがあまり改善されないため、安全にプレイしてください。

于 2012-07-10T19:01:17.563 に答える
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;
}

ループ条件は、各繰り返しの後、ループを再開する前に評価されます。

また、string の長さを処理するために使用する型にも注意してください。stdio でsize_t定義されているものでなければなりません。unsigned int比較してキャストすると、int重大な脆弱性の問題が発生する可能性があります。

于 2012-07-12T12:29:48.070 に答える
0

プレトリアンの答えを詳しく説明すると、次のことをお勧めします。

for( auto i = strlen(s)-1; i > 0; --i ) {foo(s[i-1];}
  • autostrlen が返す型を気にしたくないからです。C++11 コンパイラ (たとえばgcc -std=c++0x、完全に C++11 ではないが自動型が機能する) がそれを行います。
  • i = strlen(s)比較したいから0(下記参照)
  • i > 00 との比較は、他の数値との比較より (わずかに) 速いためです。

欠点はi-1、文字列文字にアクセスするために使用する必要があることです。

于 2012-07-18T11:16:23.960 に答える