次のコードが冗長な計算を引き起こす可能性があるかどうか、またはコンパイラ固有ですか?
_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] != '\0'; i++)
{
//Something;
}
上記のコードではstr[i]
は、ループがサイクルを開始するたびに、位置i
の文字列内の特定の1文字のみを検証するため、必要なメモリが少なくなり、より効率的です。
詳細はこちら Link を参照してください。
以下のコードでは、ループが実行されるたびに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
nessをキャストしない場合、最適化がオンになっていると、コンパイラはstrlen
を1回だけ呼び出す可能性があります。 。しかし、これは確かに信頼できる行動ではありません。
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
が指すメモリは、ループの期間中変化しませんコンパイラはこれらのどちらも認識していないため、最初の呼び出しの結果を安全にメモできません。
はい、strlen(ss)
はコードが実行されるたびに計算されます。
はい、strlen()
関数が呼び出されます毎回ループが評価されます。
効率を向上させたい場合は、常にローカル変数にすべてを保存することを忘れないでください...時間がかかりますが、非常に便利です。
以下のようなコードを使用できます。
String str="ss";
int l = strlen(str);
for ( int i = 0; i < l ; i++ )
{
// blablabla
}
はい、strlen(ss)
は各反復で長さを計算します。何らかの方法でss
を増やし、i
も増やす場合。無限ループになるでしょう。
今日では一般的ではありませんが、20年前の16ビットプラットフォームでは、これをお勧めします。
for ( char* p = str; *p; p++ ) { /* ... */ }
コンパイラーの最適化があまり良くない場合でも、上記のコードを使用すると、優れたアセンブリコードが得られます。
はい。 strlenは、iが増加するたびに計算されます。
ssを変更しなかった場合をループ内で指定すると、それが意味しますロジックには影響しませんそれ以外の場合は影響します。
次のコードを使用する方が安全です。
int length = strlen(ss);
for ( int i = 0; i < length ; ++ 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__
_に違反しない限り)。stillの場合、両方のコンパイラーがstrlen()
そのループの1回の繰り返し を実行します。すごい。
これは、@ Praetorianと@JaredParの暗示/希望的思考がうまくいかないことも意味します。
はい。テストは、ssがループ内で変更されないことを認識していません。変更されないことがわかっている場合は、次のように記述します。
int stringLength = strlen (ss);
for ( int i = 0; i < stringLength; ++ i )
{
// blabla
}
まあ、誰かがそれが「賢い」現代のコンパイラによってデフォルトで最適化されていると言っていることに気づきました。ちなみに、最適化せずに結果を見てください。私は試した:
最小限の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
.....
プレトリアの答えについて詳しく述べると、次のことをお勧めします。
_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
_を使用する必要があることです。
はい、簡単な言葉で。そして、コンパイラーが望んでいるまれな状況では、ss
に変更がまったく加えられていないことが判明した場合の最適化ステップとして、小さなノーがあります。しかし、安全な状態では、それをYESと考えるべきです。 multithreaded
やイベント駆動型プログラムのような状況があり、それをNOと見なすとバグが発生する可能性があります。プログラムの複雑さをあまり改善しないので、安全にプレイしてください。
はい。
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
として定義されていますunsigned int
stdioで。比較してint
にキャストすると、深刻な脆弱性の問題が発生する可能性があります。