PVS-Studioは、他の静的コードアナライザーと同様に、多くの場合誤検知を生成します。 しかし、不思議な陽性を偽と考えて急いではいけません。 これは、PVS-Studioが再び複数の人々により注意を向けるようになった経緯についての短い話です。
ユーザーは、アナライザーがコードの行ごとに4つの誤検出を即座に生成すると主張して、私たちを支持して書きました。 最初にサポートで書かれた手紙がYevgeny Ryzhkovに届きました。YevgenyRyzhkovは流readに読み、異常なフィードバックに気づかなかったため、すぐに開発者のSvyatoslav Razmyslovに送りました。 Eugeneはコードをじっと見なかったので、プログラマーの半分だけを数えるのは公平でしょう。
Svyatoslavはその手紙を読み、アナライザーがひどく間違っている可能性があることを疑った。 したがって、彼は相談のために私のところに来ました。 Svyatoslavは私の目が固まっていることを望んでおり、アナライザーがこれらすべての奇妙なメッセージを出した理由を教えてくれることに気付くでしょう。 残念ながら、メッセージは本当に非常に奇妙であり、そうすべきではないことを確認しただけです。 しかし、それらが発生した理由は何だったのか、私は気づきませんでした。 バグトラッカーでタスクを開き、何が間違っていたかを理解し始めることが決定されました。
そして、Svyatoslavがバグトラッカーの問題を詳細に説明するために合成例を作成し始めたときだけ、洞察が彼に現れました。 アナライザーに4つのメッセージが表示される理由をすぐに見つけられるかどうかを見てみましょう。
ここに著者の許可を得て発行された手紙のテキストがあります。 そして、手紙に添付された説明図。
ここでのV560警告はすべて間違っています。 個人用の最新バージョンのPVS-Studioで実行します。 基本的に、「IF」ステートメントは正しいです。 外側のものは速度のために行われます-内側のものはまだ必要であり、nonは常にtrueまたはfalseです。
さあ、親愛なる読者、あなた自身をテストする時間です! 間違いありませんか?
気を配る時間。 そして、ユニコーンは少し待ちます。
記事の紹介部分の後、多くの人が間違いを見つけた可能性が高いです。 エラーを見つけるように構成すると、その場所が特定されます。 「誤検知」と呼ばれる手紙を読んだ後に間違いに気付くのははるかに困難です:)。
バグを探すのが面倒な人のための説明。 条件をもう一度検討してください。
if (!((ch >= 0x0FF10) && (ch <= 0x0FF19)) || ((ch >= 0x0FF21) && (ch <= 0x0FF3A)) || ((ch >= 0x0FF41) && (ch <= 0x0FF5A)))
コードの作成者は、文字が3つの範囲のいずれにも該当しないことを検証する予定でした。
エラーは、論理否定(!)演算子が最初の部分式にのみ適用されることです。
条件が満たされている場合:
!((ch >= 0x0FF10) && (ch <= 0x0FF19))
次に、 短絡評価に従って式評価が中断されます。 条件が満たされない場合、変数chの値は範囲[0xFF10..0xFF19]にあります。 したがって、さらに4つの比較は意味がありません。 それらはすべて偽または真になります。
もう一度。 chが [0xFF10..0xFF19]の範囲にあり、計算が続行する場合、以下を参照してください。
- ch> = 0x0FF21-常にfalse
- ch <= 0x0FF3A-常に真
- ch> = 0x0FF41-常にfalse
- ch <= 0x0FF5A-常に真
これは、PVS-Studioアナライザーが警告するものです。
そのため、静的アナライザーは、ユーザーやチームの2人半のプログラマーよりも注意深いものであることがわかりました。
状況を修正するには、追加のブラケットを追加する必要があります。
if (!(((ch >= 0x0FF10) && (ch <= 0x0FF19)) || ((ch >= 0x0FF21) && (ch <= 0x0FF3A)) || ((ch >= 0x0FF41) && (ch <= 0x0FF5A))))
または、条件を書き換えます:
if (((ch < 0x0FF10) || (ch > 0x0FF19)) && ((ch < 0x0FF21) || (ch > 0x0FF3A)) && ((ch < 0x0FF41) || (ch > 0x0FF5A)))
ただし、これらのオプションを使用することはお勧めできません。 コードを読みやすくするために、次のように書きます。
const bool isLetterOrDigit = (ch >= 0x0FF10 && ch <= 0x0FF19) // 0..9 || (ch >= 0x0FF21 && ch <= 0x0FF3A) // A..Z || (ch >= 0x0FF41 && ch <= 0x0FF5A); // a..z if (!isLetterOrDigit)
ブラケットのいくつかを削除したことに注意してください。 今見たように、多数の括弧はエラーを回避するのに役立ちませんでした。 ブラケットは、コードを読みやすくする必要がありますが、複雑ではありません。 プログラマは、比較の優先順位= <、=>が&&演算子の優先順位よりも高いことをよく覚えています。 したがって、ここではブラケットは必要ありません。 しかし、優先順位を尋ねると-&&または||、多くは混乱しています。 したがって、一連の計算&&を指定するには、|| カッコは置く方が良いです。
||と書くほうが良いのはなぜですか。 冒頭で、「 プログラミング、リファクタリング、その他すべての主な問題 」という記事で説明しました(「同じ種類のコードを「テーブル」に揃える」を参照)。
ご清聴ありがとうございました。 PVS-Studioをダウンロードして使用を開始します。 初期段階で多くのエラーと潜在的な脆弱性を特定するのに役立ちます。
この記事を英語圏の聴衆と共有したい場合は、翻訳へのリンクを使用してください:Andrey Karpov。 PVS-Studioが3人半のプログラマーよりも注意深いことを証明した方法