7

アサーションは、条件(precondition、postcondition、invariants)が満たされているかどうかを確認し、プログラマーがデバッグ段階で穴を見つけるのに役立ちます。

例えば、

void f(int *p)
{
  assert(p);
  p->do();
}

私の質問は、リリースモードで条件を満たせなかったと想定し、それに応じてケースを処理する必要があるかどうかです。

void f(int *p)
{
  assert(p);

  if (p)
  {
    p->do();
  }
}

結局のところ、アサーションは、テストする条件が決して偽であってはならないことを意味します。しかし、チェックせずに失敗すると、プログラムがクラッシュします。ジレンマのように聞こえます。どうやって対処しますか?

4

8 に答える 8

24

アサーションが失敗した場合、プログラムはクラッシュするはずです。

アサーションが失敗したということは、プログラマーがプログラムフローをどのように進めることができるかを理解する上で根本的な誤りを犯したことを意味します。これは開発援助であり、生産援助ではありません。本番環境では、例外が「発生する可能性がある」ので処理する場合がありますが、アサーションは「決して」失敗しないはずです。

「ああ、でもアサーションが本番環境で失敗したらどうなる?私はそれらを捕まえる必要がある!」と言うキャンプにいるなら。その後、あなたはポイントを逃しています。そのような場合、なぜ例外をスローしない(またはエラーを処理しない)のか、自問してみてください。

一般的に言えば、assertは「条件が満たされない場合は例外をスローする」の単なる省略形ではありません(まあ、それは操作的意味論である場合もありますが、表示的意味論ではありません)。むしろ、アサーションが失敗するということは、アプリケーションが開発者が可能でさえないと信じている状態にあることを意味します。このような場合でも、コードを実行し続けたいですか?明らかに(私は言うでしょう)、いいえ

于 2010-10-29T05:59:00.557 に答える
2

防御的なプログラミングは常に最良です。すべてのテストにもかかわらず、アプリケーションにはバグが含まれていると常に想定する必要があります。そのため、NULLポインターの違いを回避して先に進むことができる状況では、NULLチェックを追加することが最善の利益になります。

ただし、クラッシュを回避する簡単な方法がない場合もあります。そのような場合、開発サイクル中に問題を検出する唯一の方法はアサーションです。

ただし、重要なポイントの1つは、データの整合性に関する主要な問題を検出するためにアサートもよく使用されます。これらのアサートを超えて続行すると、データが破損するリスクがあります。そのような場合は、データを破棄するよりもクラッシュする方がよい場合があります。(明らかに、少なくともエラーの説明を含む妥当なUIを表示するあらゆる種類のクラッシュハンドラーが望ましいでしょう)。

于 2010-10-29T05:59:58.663 に答える
1

アサーションはデバッグコードであり、操作コードではありません。入力エラーをキャッチするためにそれらを使用しないでください。

于 2010-10-29T05:59:04.807 に答える
1

厳密に言えば、2番目のコードには冗長性があります。

void f(int *p)
{
  assert(p);
  if (p)    // Beats the purpose of assertion
  {
    p->do();
  }
}

アサーションは、エラーが発生したことを意味します。予期しない/処理されないもの。上記のコードでは、どちらか

1)pがnullの場合を適切に処理しています。(p-> do()を呼び出さないことによって)-これはおそらく正しい/期待されることです。ただし、アサーションは誤ったアラームです。

2)一方、p-> do()を呼び出さないことで何かがうまくいかない場合(おそらくコードまたは出力でさらに)、アサーションは正しいですが、とにかく続行しても意味がありません。

上記のコードでは、プログラマーはとにかく誤っているケースを処理するためにさらに一生懸命働いています。

とは言うものの、アサーションを何かがうまくいかなかったものとして扱うことを好む人もいますが、それでも正しい出力が得られるかどうかを確認しましょう。IMO、これは悪い戦略であり、バグ修正中に混乱を引き起こします。

于 2010-10-29T07:47:54.180 に答える
0

アサーションは、テストでバグを検出するために使用されます。理論は、リリースすると機能することを十分にテストしたというものです。

実際の操作でこの状態が発生する可能性がある場合は、アサーションに依存しないでください。例外またはその他のエラーメカニズムを使用してください。

于 2010-10-29T05:59:34.427 に答える
0

前述のように、アサートはデバッグに役立ちます。彼らは決してそれを本番コードにすべきではありません(コンパイルされたように、もちろん#ifdefsでそれらをラップすることは大丈夫です)

修正することが制御できない問題が発生していて、本番コードをチェックする必要がある場合は、次のようにします。

void f(int *p)
{

  if (!p)
  {
    do_error("FATAL, P is null.");
  }

  p->do();
}

ここで、do_errorは、エラーをログに記録して正常に終了する関数です。

于 2010-10-29T05:59:46.270 に答える
0

私はそれらをリリースビルドに残しておくと言います。どのビルドにもバグがあるはずです。製品にアサーションがあるということは、uwerから問題レポートを受け取ったときに問題をより簡単に特定できることを意味します。

例外の処理にはあまり力を入れないでください。スタックトレースを含む完全な例外を取得できることを確認してください。これは特にリリースビルドに当てはまります。

于 2010-10-29T06:01:50.450 に答える
0

多くの人がアサーションをリリースモードにすることについてコメントしているので:

私が取り組んでいることでは、効率が非常に重要です(大規模なデータセットでの実行が完了するまでに数十時間から数日かかる場合があります)。したがって、デバッグコードでのみアサートする特別なマクロがあります(QAなどで実行されます)。例として、forループ内のアサートは間違いなくオーバーヘッドであり、リリースコードでは回避したい場合があります。結局のところ、すべてがうまくいけば、アサートは失敗するはずがありません。

リリースコードが適切であると主張する1つの例は、ロジックがコードの特定のブランチにまったくヒットしない場合です。この場合、assert(0)は問題ありません[したがって、どのような種類のassert(0)も常にリリースコードに残すことができます]。

于 2010-10-29T07:53:56.453 に答える