以下は、シーケンス ポイント ルールが原因で未定義の動作が発生する 2 つの一般的な問題です。
a[i] = i++; //has a read and write between sequence points
i = i++; //2 writes between sequence points
シーケンスポイントに関して他に遭遇したことは何ですか?
コンパイラが警告できない場合、これらの問題を見つけるのは非常に困難です。
以下は、シーケンス ポイント ルールが原因で未定義の動作が発生する 2 つの一般的な問題です。
a[i] = i++; //has a read and write between sequence points
i = i++; //2 writes between sequence points
シーケンスポイントに関して他に遭遇したことは何ですか?
コンパイラが警告できない場合、これらの問題を見つけるのは非常に困難です。
ダリオの例のバリエーションは次のとおりです。
void Foo(shared_ptr<Bar> a, shared_ptr<Bar> b){ ... }
int main() {
Foo(shared_ptr<Bar>(new Bar), shared_ptr<Bar>(new Bar));
}
メモリリークする可能性があります。2 つのパラメータの評価の間にシーケンス ポイントがないため、2 番目の引数が最初の引数の前に評価されるだけでなく、両方の Bar オブジェクトがいずれかの前に作成される場合もありますshared_ptr
。
つまり、次のように評価されるのではなく、
Bar* b0 = new Bar();
arg0 = shared_ptr<Bar>(b0);
Bar* b1 = new Bar();
arg1 = shared_ptr<Bar>(b1);
Foo(arg0, arg1);
b0
(正常に割り当てられた場合、すぐにラップされるため、安全ですshared_ptr
)、次のように評価される場合があります。
Bar* b0 = new Bar();
Bar* b1 = new Bar();
arg0 = shared_ptr<Bar>(b0);
arg1 = shared_ptr<Bar>(b1);
Foo(arg0, arg1);
つまり、b0
正常に割り当てられb1
て例外がスローされた場合、b0
削除されることはありません。
パラメータリストまたは追加などの実行順序に関して、あいまいなケースがいくつかあります。
#include <iostream>
using namespace std;
int a() {
cout << "Eval a" << endl;
return 1;
}
int b() {
cout << "Eval b" << endl;
return 2;
}
int plus(int x, int y) {
return x + y;
}
int main() {
int x = a() + b();
int res = plus(a(), b());
return 0;
}
a() または b() が最初に実行されますか? ;-)
これは、BjarneStroustupによるc++を使用したプログラミングの原則と実践からの簡単なルールです。
「式の変数の値を変更した場合。同じ式で2回読み取りまたは書き込みを行わないでください。」
a[i] = i++; //i's value is changed once but read twice
i = i++; //i's value is changed once but written twice
私が最近見たものは、クラスのフォーマット時間を節約したいというプログラマーの願望によるもので、完全に間違っていました。
class A
{
public:
...
const char* Format( const string& f ) const
{
fmt = Print( f, value );
return fmt.c_str();
}
operator const char* () const { return fmt.c_str(); }
private:
struct timeval value;
mutable string fmt;
};
A a( ... );
printf( "%s %s\n", a.Format( x ), a.Format( y );
最後の行は、両方の形式で常に同じ値を出力します (または、内部文字列が返されたメモリを解放するため、プログラムをクラッシュさせます)。
もう1つは、私がずっと前に行ったインタビューからのものです。
void func( int x, int y, int z )
{
printf( "%d %d %d\n", x, y, z );
}
...
int i = 0;
func( i, ++i, i++ ); /* don't do this in real software :) */
人々が陥るのを見たダリオの例に似た例:
printf("%s %s\n", inet_ntoa(&addr1), inet_ntoa(&addr2));
addr1 addr1
これは " " または " "を出力するだけでなくaddr2 addr2
(inet_ntoa
以降の呼び出しによって上書きされる静的バッファーへのポインターを返すため)、どちらが該当するかは定義されていません (C は引数で評価の順序を指定しないため)。リスト)。
ほとんどの C コンパイラで機能するが、シーケンス ポイントが原因であいまいな 2 つの適切な式を次に示します。
x ^= y ^= x ^= y; // in-place swap of two variables
また、
int i=0;
printf("%d %d %d", ++i, ++i, ++i); // usually prints out 3 2 1... but not for all compilers!