アサートします。 これは何ですか

アサートは、プログラムの任意の場所にある任意のデータの値に関する仮定を確認できる特別な構造です。 この設計は、不正なデータの検出時に自動的に信号を送ることができ、通常、不正なデータの検出場所を示すプログラムの異常終了につながります。 奇妙な、一見したところ、デザイン-最も不適切な瞬間にプログラムを圧倒する可能性があります。 それのポイントは何ですか? ある時点でプログラムの実行中にプログラムのデータの一部が不正確になり、プログラムをすぐに「いっぱい」にせずに、何も起こらなかったように動作し続けた場合にどうなるかを考えてみましょう。 プログラムはその後、目に見えるエラーなしで長時間動作します。 そして、おそらく彼女の知っている理由だけで、将来のいつでも、それ自体が「転倒」するでしょう。 または、同性愛者のポルノサイトから完全なハードドライブコンテンツを送り出します。 これは未定義の動作と呼ばれ、一般的な考えに反して、メモリへのランダムアクセスを持つプログラミング言語(別名C、C ++)に固有のものではありません。 なぜなら assertは、不正なデータを検出した直後にプログラムを終了します。不正なデータにつながったプログラムのバグを迅速に特定して修正することができます。 これが彼の主な目的です。 アサートは、java、c#、c、およびpythonを含む多くのプログラミング言語で利用できます。

どんな種類のアサートがありますか?

アサートを使用すると、コンパイル段階または実行時にプログラムのエラーをキャッチできます。 コンパイル段階でのチェックはそれほど重要ではありません-ほとんどの場合、プログラム実行中に同様のチェックに置き換えることができます。 言い換えれば、コンパイル段階でのアサーションは、構文上の砂糖にすぎません。 したがって、将来的には、アサートとは、プログラム実行中のチェックのみを意味します。



アサートは、次のクラスに分類できます。



引数に無効な値が見つかった場合、この関数が呼び出された場所の近くにバグがある可能性があります。 例:

//    n. //  n      0  10 . int factorial(int n) { //      assert(n >= 0); //  n  10,        //  ,    . assert(n <= 10); if (n < 2) { return 1; } return factorial(n - 1) * n; } //  ''    factorial()    //    0  99. // //   factorial()      , //          . // //     ,       // ,         //   . for (int i = 0; i < 100; ++i) { a[i] = factorial(i); }
      
      





整数オーバーフローとは何ですか。

スタックオーバーフローとは



着信関数の引数は暗黙的である可能性があることを理解することが重要です。 たとえば、クラスメソッドが呼び出されると、このクラスのオブジェクト(thisおよびself)へのポインターが暗黙的に関数に渡されます。 この関数は、グローバルスコープで宣言されたデータ、または字句クロージャのスコープからのデータにもアクセスできます。 関数に入るときにアサートを使用してこれらの引数をチェックすることもお勧めします。

この段階で誤ったデータが検出された場合、この関数のコードにはバグが含まれている可能性があります。 例:

 int factorial(int n) { int result = 1; for (int i = 2; i <= n; ++i) { result *= i; } //         -   //    .    n   // ,   .    // a[i]      . // //          , //      n,    //    . assert(result > 0); return result; }
      
      







任意精度の算術とは何ですか。

関数の結果は暗黙的である場合があります。 たとえば、関数は、関数の引数によって(直接または間接的に)参照されるデータを変更できます。 この関数は、グローバルスコープまたはレキシカルクロージャのスコープからデータを変更することもできます。 関数を終了する前に、このデータの正確性を確認することをお勧めします。

関数の途中で誤ったデータが検出された場合、このチェックの領域のどこかにバグがある可能性があります。 例:

 int factorial(int n) { int result = 1; while (n > 1) { //      . // //      ,      //     n,   . // //   ,      (  //  ), ..      result, //         ,  //    (  )   // result  . assert(result <= INT_MAX / n); result *= n; --n; } return result; }
      
      







assertを使用するタイミングと場所

答えは簡単です-少なくとも少し役に立つと思われるときはいつでもどこでもアサートを使用してください。 結局のところ、コード内のバグのローカライズを大幅に簡素化します。 明らかなコードの実行結果を確認することさえ、その後のリファクタリングに役立ちます。その後、コードはそれほど明白ではないかもしれず、バグが簡単に忍び込む可能性があります。 多数のアサーターがコードの明瞭さを低下させ、プログラムの実行を遅くすることを恐れないでください。 アサートは、一般的なコードから視覚的に際立っており、このコードが機能する前提に関する重要な情報を伝えます。 正しく配置されたアサートは、コード内のほとんどのコメントを置き換えることができます。 ほとんどのプログラミング言語は、コンパイル時またはプログラム実行中のアサートの無効化をサポートしているため、プログラムのパフォーマンスへの影響は最小限です。 アサートは通常、プログラムの開発およびテスト中は有効のままですが、プログラムのリリースバージョンでは無効になります。 プログラムがOOPの最高の伝統で書かれている場合、またはエンタープライズ手法を使用している場合、アサートをまったく無効にすることはできません。パフォーマンスが変わることはほとんどありません。



断言せずにできるのはいつですか?

コードの各行を介してアサーションを複製しても、バグをキャッチする効率が大幅に向上しないことは明らかです。 アサーションの最適な数、およびプログラム内のコメントの最適な数についてのコンセンサスはありません。 アサートの存在を初めて知ったとき、私のプログラムには100500のアサートが含まれるようになり、その多くは何度も互いに重複していました。 時間が経つにつれて、コード内のアサートの数が減少し始めました。 次の規則により、バグをキャッチする効率を大幅に低下させることなく、プログラムのアサート回数を何度も減らすことができました。

入力引数の重複チェックは、この引数と直接機能する関数にのみ配置することで回避できます。 つまり 関数foo()が引数で機能せず、関数bar()に渡すだけの場合、関数foo()でこの引数のチェックを省略することができます。 bar()関数の引数をチェックすることで複製されます。

無効な値へのアサートは省略できます。これにより、これらのアサートのすぐ近くでプログラムがクラッシュすることが保証されます。 プログラムのクラッシュにより、バグをすばやく特定できる場合。 このようなアサートには、ポインターを参照解除する前にNULLをチェックし、除算する前に分周器のゼロ値をチェックすることが含まれます。 繰り返しますが、このようなチェックは、ランタイムがプログラムのクラッシュをこれらの場合に保証する場合にのみ省略できます。

バグをキャッチする効率を損なうことなくassert'ovの数を減らす他の方法がある可能性があります。 これらの方法を知っている場合は、この投稿のコメントでそれらを共有してください。



assertを使用できないのはいつですか?

なぜなら アサートは、コンパイル時またはプログラムの実行中に削除できますが、プログラムの動作を変更しないでください。 アサートを削除した結果としてプログラムの動作が変わる場合、これはアサートの誤用の明確な兆候です。 したがって、アサート内では、プログラムの状態またはプログラムの外部環境を変更する関数を呼び出すことはできません。 たとえば、次のコードはアサーションを誤って使用しています。

 //   . // //  0,      -  : // -    . // - mtx     . //  1,    . int acquire_mutex(mutex *mtx); //   . // //  0,      -  // : // -    . // - mtx     . //  1,    . int release_mutes(mutex *mtx); // ,   . assert(acquire_mutex(mtx)); //   , "" . process_data(data_protected_by_mtx); // ,   . assert(release_mutes(mtx));
      
      







明らかに、アサートが無効になっている場合、データは安全ではない可能性があります。

このエラーを修正するには、関数の結果を一時変数に保存し、この変数をアサート内で使用する必要があります。



 int is_success; is_success = acquire_mutex(mtx); assert(is_success); //        assert'. process_data(data_protected_by_mtx); is_success = release_mutex(mtx); assert(is_success);
      
      







なぜなら assertの主な目的は、バグ(別名プログラミングエラー)をキャッチすることで、プログラミングエラーではない予想されるエラー処理を置き換えることはできません。 例:

 //   buf_size  ,    buf, //     connection. // //  0    ,     . , //       . //  1     . int write(connection *connection, const void *buf, size_t buf_size); int is_success = write(connection, buf, buf_size); // "",    . assert(is_success);
      
      







write()が0を返す場合、これはプログラムにバグがあることを意味しません。 プログラムのアサートが無効になっていると、書き込みエラーが気付かれない場合があり、その後悲しい結果につながる可能性があります。 したがって、assert()はここには適合しません。 ここでは、通常のエラー処理の方が適しています。 例:

 while (!write(connection, buf, buf_size)) { //          . close_connection(connection); connection = create_connection(); }
      
      







私はjavascriptでプログラミングしています。 アサートはありません。 どうすればいいですか?

一部のプログラミング言語には、アサートの明示的なサポートがありません。 必要に応じて、次の「設計パターン」に従って簡単に実装できます。



 function assert(condition) { if (!condition) { throw "Assertion failed! See stack trace for details"; } } assert(2 + 2 === 4); assert(2 + 2 === 5);
      
      







一般に、アサートは通常、自動テスト用に設計されたさまざまなフレームワークとライブラリに実装されます。 時々そこに期待されると呼ばれます。 自動テストとアサートの使用には多くの類似点があります。両方の手法は、プログラムのバグを迅速に特定して修正するように設計されています。 ただし、共通の機能にもかかわらず、自動化されたテストとアサートは相互に排他的ではありませんが、おそらく相互に補強しています。 適切に配置されたアサートは、自動コードテストを簡素化します。 テストプログラムは、プログラムコード内でアサートを重複させるチェックを省略できます。 通常、このようなチェックは、テストプログラムのすべてのチェックのかなりの部分を占めています。



All Articles