はじめに
私たちは定期的に開いているプロジェクトをチェックし 、チェックできる他のすべてのものをチェックします。 たとえば、Visual C ++ 2012の一部であるライブラリをチェックしました。結果は、「 Visual C ++ 2012ライブラリでエラーが検出されました 」というメモでした。
Visual C ++ディストリビューションには、ライブラリのソースコードが含まれています。 C ++ Builderの状況はさらに悪い。 ヘッダーファイルのみがあります。 そのため、一部のインライン関数のみをチェックできました。 それにもかかわらず、私は何か面白いものを見つけることができました。 コードの対応するセクションを検討してください。
アラートを操作する
#pragma warning(無効化:4115) #include <objbase.h> #pragma warning(デフォルト:4115)
PVS-Studioが発行する診断メッセージ:
V665おそらく、このコンテキストでは「#pragma warning(default:X)」の使用法が正しくありません。 代わりに「#pragma warning(push / pop)」を使用する必要があります。 チェック行:16、18。iaguid.h 18
警告出力モードをデフォルト状態に設定することは好ましくありません。 正しいアプローチは、以前の状態を保存してから復元することです。 これを行うには、「#pragma warning(push [、n])」および「#pragma warning(pop)」を使用します。
失敗したマクロ
#define SET_VTYPE_AND_VARREF(type、val)\ this-> vt = VT_ ## type | VT_BYREF; \ V_ ## type ## REF(this)= val; TVariantT&operator =(システム::通貨* src) { クリア(); if(src) SET_VTYPE_AND_VARREF(CY、 reinterpret_cast <tagCY *>(&(src-> Val)));; return * this; }
PVS-Studioが発行する診断メッセージ:
V640コードの操作ロジックがそのフォーマットに対応していません。 2番目のステートメントは常に実行されます。 中括弧が欠落している可能性があります。 utilcls.h 1781
マクロSET_VTYPE_AND_VARREFの記述は不十分です。 その内容は中括弧{}で囲まれていません。 その結果、条件「if(src)」はマクロの最初の行にのみ適用されます。
未定義の動作
#define _BITS_BYTE 8 テンプレート<class _Uint、 _Uint _Ax、 _Uint _Cx、 _Uint _Mx> クラスlinear_congruential { static _CONST_DATA int _Nw = (_BITS_BYTE * sizeof(_Uint)+ 31)/ 32; ボイドシード(seed_seq&_Seq) { _Uint _Arr [3 + _Nw]; .... int _Lsh = _BITS_BYTE * sizeof(_Uint); .... for(int _Idx = _Nw; 0 <--_ Idx;) _Arr [3 + _Idx-1] | = _Arr [3 + _Idx] << _Lsh; .... } }
PVS-Studioが発行する診断メッセージ:
V610 linear_congruentialのインスタンス化<unsigned long、40014、0、2147483563>:未定義の動作。 シフト演算子 '<<を確認してください。 右側のオペランド「_Lsh」は、昇格した左側のオペランドのビット単位の長さ以上です。 ランダム738
この関数では、変数 '_Lsh'の値は32になります。32ビット型を31ビット以上シフトすることはできません。 標準からの抜粋:右側のオペランドが負の場合、または昇格した左側のオペランドのビット長以上の場合、動作は未定義です。
また、DXVABitMaskマクロは危険な方法で作成されました。
#define DXVABitMask(__ n)(〜((〜0)<< __n))
標準から対応する行を引用するには:それ以外の場合、E1が符号付き型で負でない値を持ち、E1 * 2 ^ E2が結果型で表現できる場合、それが結果の値です。 それ以外の場合、動作は未定義です。
このマクロのため、PVS-Studioはいくつかの警告を表示します。 例:
V610未定義の動作。 シフト演算子 '<<を確認してください。 左のオペランド '(〜0)'は負です。 dxva.h 1080
シフトと未定義の振る舞いについての詳細は記事で読むことができます: フォードを知らないで、水に入らないでください。 パート3 。
new演算子の動作の記録されていない変更。
「新しい」演算子を呼び出した後、ポインターがNULLでないことが確認される場所はかなりありました。 今ではこれは意味がなく、有害ですらあります。 メモリ割り当てエラーの場合、「new」演算子は例外std :: bad_allocをスローします。
例外をスローしない演算子「new」を呼び出すことができます。 C ++ Builderには、このための特別なマクロさえあります。
#define NEW_NOTHROW(_bytes)new(nothrow)BYTE [_bytes]
ただし、メモリの割り当てによって順序を復元することはどこでも可能ではなかったようです。 いくつか例を挙げます。
inline void _bstr_t :: Assign(BSTR s)throw(_com_error) { if(m_Data!= NULL){ m_Data-> Assign(s); } その他{ m_Data = new Data_t(s、TRUE); if(m_Data == NULL){ _com_issue_error(E_OUTOFMEMORY); } } }
PVS-Studioが発行する診断メッセージ:
V668メモリは「new」演算子を使用して割り当てられたため、「m_Data」ポインターをnullに対してテストする意味はありません。 メモリ割り当てエラーの場合、例外が生成されます。 comutil.h 454
行「_com_issue_error(E_OUTOFMEMORY);」 決して実行されません。 エラーの場合、例外std :: bad_alloc()がスローされます。
静的インラインBYTE * __ CorHlprNewThrows(size_tバイト) { BYTE * pbMemory =新しいBYTE [バイト]; if(pbMemory == NULL) __CorHlprThrowOOM(); return pbMemory; }
PVS-Studioが発行する診断メッセージ:
V668「new」演算子を使用してメモリが割り当てられたため、「pbMemory」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 corhlpr.h 56
テンプレート<クラスTYPE、クラスARG_TYPE> void CDXArray <TYPE、ARG_TYPE> :: SetSize(int nNewSize、int nGrowBy) { .... TYPE * pNewData =(TYPE *)新しいBYTE [nNewMax * sizeof(TYPE)]; //まあ、クラッシュするよりはましだ if(pNewData == NULL) 帰る .... }
PVS-Studioが発行する診断メッセージ:
V668「new」演算子を使用してメモリが割り当てられたため、「pNewData」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 dxtmpl.h 338
残りのコードフラグメントは類似しており、それらを引用しても意味がありません。 診断メッセージのみに制限します。
- V668「new」演算子を使用してメモリが割り当てられたため、「p」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 d3dx10math.inl 1008
- V668「new」演算子を使用してメモリが割り当てられたため、「p」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 dxtmpl.h 123
- V668「new」演算子を使用してメモリが割り当てられたため、「pNewData」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 dxtmpl.h 395
- V668メモリは「new」演算子を使用して割り当てられたため、「m_pHashTable」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 dxtmpl.h 1126
- V668「new」演算子を使用してメモリが割り当てられたため、「newBrush」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 gdiplusbrush.h 44
- V668メモリは「new」演算子を使用して割り当てられたため、「retimage」ポインターをnullに対してテストする意味はありません。 メモリ割り当てエラーの場合、例外が生成されます。 gdiplusbrush.h 374
- V668「new」演算子を使用してメモリが割り当てられたため、「argbs」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 gdiplusbrush.h 615
- V668「new」演算子を使用してメモリが割り当てられたため、「argbs」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 gdiplusbrush.h 645
- V668「new」演算子を使用してメモリが割り当てられたため、「argbs」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 gdipluspath.h 1196
- V668「new」演算子を使用してメモリが割り当てられたため、「argbs」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 gdipluspath.h 1231
- V668「new」演算子を使用してメモリが割り当てられたため、「argbs」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 gdipluspath.h 1372
- V668「new」演算子を使用してメモリが割り当てられたため、「argbs」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 gdipluspath.h 1405
- V668「new」演算子を使用してメモリが割り当てられたため、「newLineCap」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 gdipluslinecaps.h 153
- V668「new」演算子を使用してメモリが割り当てられたため、nullに対して「nativeRegions」ポインターをテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 gdiplusgraphics.h 1415
- V668「new」演算子を使用してメモリが割り当てられたため、「newRegion」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 gdiplusregion.h 89
- V668メモリは「new」演算子を使用して割り当てられたため、「nativeFamilyList」ポインターをnullに対してテストする意味はありません。 メモリ割り当てエラーの場合、例外が生成されます。 gdiplusfontcollection.h 57
- V668「new」演算子を使用してメモリが割り当てられたため、「newImage」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 gdiplusbitmap.h 334
- V668「new」演算子を使用してメモリが割り当てられたため、「bitmap」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 gdiplusbitmap.h 819
- V668「new」演算子を使用してメモリが割り当てられたため、「bitmap」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 gdiplusbitmap.h 862
- V668「new」演算子を使用してメモリが割り当てられたため、「m_pData」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 spcollec.h 266
- V668「new」演算子を使用してメモリが割り当てられたため、「pNewData」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 spcollec.h 325
そして、これはインライン関数のみです! * .cppファイルで何が起こっているか想像してみてください。 :)
ご注意
この記事を書き終えた瞬間に、Embarcadero C ++ Builder XE4が登場しました。 ただし、これは分析の利点を無効にするものではなく、PVS-Studioの機能を十分に実証します。 加えて、私はまだこの記事の公開を少し待っていました。 本日、PVS-Studioの新しいバージョンをリリースしました。これはすでにC ++ Builder XE4をサポートしています。 これは、コードアナライザーを公開して試してみることをお勧めする正当な理由です。
おわりに
ご清聴ありがとうございました。 C ++ Builderの開発者が私たちに注意を払い、PVS-Studioを使用してコンパイラとライブラリのソースコードを確認したいと思います。 結論として、いくつかの便利なリンクを提供したいと思います。
- PVS-Studioツールの説明 。 完全に機能する試用版をダウンロードできます。
- アンドレイ・カルポフ。 C ++ Builder、64ビットアプリケーションのアセンブリおよびViva64ルネサンス 。
- ツイッター@Code_Analysis 。 C / C ++のテーマに関する多くの興味深いリンクを公開しています。
- PVS-Studioでできることについて。 静的分析を使用してPVS-Studio開発者がオープンソースプロジェクトで検出したエラー 。