gcc 5.1の誤った配列サイズの計算に関する新しい警告

gccユーザーへの朗報-gcc 5.1以降を使用している場合、関数パラメーターとして宣言された配列のサイズを計算する際に、次の一般的なエラーをすばやく見つけやすくなります。



void something( char arr[100] ) { // this loop is broken for( size_t index = 0; index < sizeof(arr)/sizeof(arr[0]); index++ ) { //WHATEVER } }
      
      





パラメーターは既知のサイズの配列として宣言されますが、CおよびC ++コンパイラーの観点からは、 char *型のポインターであるため、 sizeof(arr)sizeof(char *)と同じ値を与えます-4または8の可能性が高い合計は期待どおりに機能しません。

別のオプション:

 void something( char encryptionKey[9000] ) { // WHATEVER, PROFIT // this call is broken SecureZeroMemory( encryptionKey, sizeof(encryptionKey)); // erase the key }
      
      





ここでは、開発者は一部のデータをゼロ上書きしたかったのですが、エラーのため、最初の数バイトのみが上書きされます。 このようなエラーは、最初のエラーよりもテストで見つけるのが困難です。



gcc 5.1以降では、このようなコードを見つけやすくするために、そのようなコードに対して警告が発行され、デフォルトで有効になっています。



一部の読者は、上記の例からコードの作者の手の曲率についてコメントすることに急いでいます。 ただし、この問題は非常に一般的であるため、C ++コードでは、テンプレート関数で次のフォーカス( ここから )を使用することをお勧めします。



 template<typename StoredType, size_t Size> char ( &ArrayElementsCountHelper(StoredType( &Array )[Size]) )[Size]; #define countOfElements(Array) sizeof(ArrayElementsCountHelper (Array))
      
      





上記のコードでcountOfElements()を使用すると、コンパイルエラーが発生しますが、このコードは次のとおりです。



 char arr[100] for( size_t index = 0; index < countOfElements(arr); index++ ) { //WHATEVER }
      
      





コンパイルし、正しく動作します。



sizeof(smth)/ sizeof(smth [0])を明示的に指定することに加えて、マクロも使用します。



 // in a header far, far away... #define errorProneCountOfElements( arr ) (sizeof(arr)/sizeof((arr)[0])) for( size_t index = 0; index < errorProneCountOfElements (arr); index++ ) { //WHATEVER }
      
      





上記のケースで新しい警告がどのように機能するかを見てみましょう。 gcc.godbolt.org試しみます



まず、コンパイラーとしてgcc 4.9.2を選択します。デフォルトのパラメーターを使用すると、どの例でもサイズの計算が正しくないという警告は表示されません。 次に、gcc 5.1.0に変更します-ループのある例では、ループタイトルのある行を取得します



警告:配列関数パラメーター 'arr'の 'sizeof'は 'char *'のサイズを返します[-Wsizeof-array-argument]



このコードでは:

 void somethingExplicitCount( char arr[] ) { for( size_t index = 0; index < sizeof(arr)/sizeof(arr[0]); index++ ) { //WHATEVER } }
      
      





同じ警告が発行されます。 同様にマクロコードで:

 void somethingMacroCount( char arr[9000] ) { for( size_t index = 0; index < errorProneCountOfElements(arr); index++ ) { //WHATEVER, PROFIT } }
      
      





書き直されたコードは警告も出します(デモンストレーションにのみポータブルmemset()を使用します):

 void somethingMemset( char key[9000] ) { //WHATEVER, PROFIT memset(key, 0, sizeof(key)); // don't use memset for sensitive data }
      
      





利益。



特に注意すべきなのは、clangバージョン3.0が同じ警告を発行する方法をすでに知っているという事実です。 これはかつて LLVMブログで、これはclang固有の警告であり、gccはそうではないと言われていました。 モーアはありません。



警告はデフォルトで有効になっています。開発者は問題コードを正しく修正するだけで済みます。



ドミトリー・メッシェリャコフ、

開発者製品部門



All Articles