C / C ++は未定義の動作でいっぱいになる可能性があります。一方の手は、予期しない動作につながる可能性があるため、悪いです。一方、積極的な最適化が可能になり、通常、C / C ++を使用している場合は速度に関心があります。
壊れるテストケースを書くのは難しいかもしれません-それはもはや存在しない奇妙なアーキテクチャやコンパイラを伴うかもしれません。また、賢明なアーキテクチャでは「問題を引き起こすことはないはず」と思われるかもしれません。
ただし、ある時点でプラットフォームを変更します(たとえば、モバイルに移行してARMに移植するか、速度を上げてGPUを使用する)。この時点で状況が崩れ始める可能性があり、デバッグする必要があります。コンパイラを新しいバージョンに更新するのと同じくらい簡単な場合があります(必要な場合もあります)。
問題のあるコードは次のとおりです。
int d[16];
int SATD (void)
{
int satd = 0, dd, k;
for (dd=d[k=0]; k<16; dd=d[++k]) {
satd += (dd < 0 ? -dd : dd);
}
return satd;
}
最後の反復中にd[++k] => d[++15] => d[16]
アクセスされます。通常、次の要素は(Cメモリモデルではなくページングに関して)正当なメモリであるため、些細なコンパイラでさえ、奇妙な動作をする実行可能ファイルを生成しませんでした。テストケースを記述する唯一の方法は、C(おそらくVM)とまったく同じメモリモデルを持つプラットフォームを見つけることでした。
しかしd[++k]
、すべてのループで実行されるgcc 4.8 prerelaseが見られます。そうでk < 16
なければ、アクセスは違法であり、コンパイラに供給されるプログラムの合法性は契約の一部です。したがって、仮定が与えられた場合、ループ条件は常に真であるため、無限ループになります。これは奇妙に聞こえるかもしれませんが、完全に合法的な最適化でした-放出system("dd if=/dev/zero of=/dev/sda"); system("format c:")
はループの正しい置き換えにもなります。動作を表示するために選択できる微妙な方法があります。たとえば、トランザクショナルメモリに関する講義中に、2つのスレッドが同じ値をインクリメントしたときに、プレゼンターが間違った値を取得しようとしたことが何度かあったことを覚えています。
ただし、一部の言語とは反対に、C / C ++は標準化されているため、このような論争は客観的なソースを参照して行うことができます。
- これが問題ではないと思う場合は、 C / C ++標準を使用してそれを証明してみてください(つまり、定義された値と動作につながる)
- これが問題だと思う場合は、 C / C ++標準を使用してそれを証明するように依頼してください(つまり、未定義の値または動作につながる)
一般的に、チームがC / C ++で記述している場合、標準を手元に置いておくと便利です。チームの専門家でさえ、時々奇妙な何かを見つけることができます。