プログラミング、リファクタリング、そしてすべてにおける究極の疑問: No. 31

同カテゴリーの次の記事

プログラミング、リファクタリング、そしてすべてにおける究極の疑問: No. 32

この記事は、インテル® デベロッパー・ゾーンに公開されている「The Ultimate Question of Programming, Refactoring, and Everything」の日本語参考訳です。


31. C および C++ では配列は値渡しされない

Wolf ゲームから抜粋した以下のコードについて考えてみます。このコードにはエラーが含まれています。PVS-Studio アナライザーは、次の診断を出力します。

V511 The sizeof() operator returns size of the pointer, and not of the array, in ‘sizeof (src)’ expression. (V511 ‘sizeof (src)’ 式において、sizeof() 演算子は配列ではなく、ポインターのサイズを返します。)

ID_INLINE mat3_t::mat3_t( float src[ 3 ][ 3 ] ) {
  memcpy( mat, src, sizeof( src ) );
}

説明

プログラマーは、C/C++ では関数へ配列を値渡しできないことを忘れることがあります。配列へのポインターを引数として渡されなければなりません。大括弧で囲まれた数字は意味がなく、渡される配列サイズをプログラマーに知らせるヒントでしかありません。実際、全く異なるサイズの配列を渡すことができます。例えば、次のコードは問題なくコンパイルされます。

void F(int p[10]) { }
void G()
{
  int p[3];
  F(p);
}

さらに、sizeof(src) 演算子は、配列サイズではなく、ポインターのサイズを評価します。その結果、memcpy() は配列の一部のみコピーします。ポインターのサイズに応じて、4 または 8 バイトがコピーされます (一般的でないアーキテクチャーではこの限りではありません)。

正しいコード

最も単純なバージョンは、次のようになります。

ID_INLINE mat3_t::mat3_t( float src[ 3 ][ 3 ] ) {
  memcpy(mat, src, sizeof(float) * 3 * 3);
}

推奨事項

コードをよりセキュアにするいくつかの方法があります。

配列サイズが分かっている場合: 関数に配列への参照を渡します。ただし、これが可能であることを知っているプログラマーは少なく、その記述方法を知っているプログラマーはさらに少数です。そのため、この例が皆さんの役に立つことを願っています。

ID_INLINE mat3_t::mat3_t( float (&src)[3][3] )
{
  memcpy( mat, src, sizeof( src ) );
}

これで、適切なサイズの配列のみ関数に渡すことができます。最も重要なことは、sizeof() 演算子がポインターではなく、配列のサイズを評価することです。

この問題を解決する別の方法は、std::array (英語) クラスを使用することです。

配列サイズが不明な場合: プログラミングに関する書籍の著者の中には、std::vector (英語) クラスやその他の類似するクラスの使用をアドバイスする方がいますが、実際には常に役立つとは限りません。

単純なポインターのほうが良い場合があります。その場合、ポインターと要素数の 2 つの引数を関数へ渡すべきです。しかし、一般にこれは不適切な手法で、多くの問題を引き起こす可能性があります。

そのような場合、「C++ コア・ガイドライン」 (英語) で紹介されている考察が参考になります。また、「配列を単一のポインターとして渡さない」 (英語) もお読みになることを推奨します。「C++ コア・ガイドライン」は、時間のあるときに目をとおしておくと良いでしょう。多くの役立つアイデアが含まれています。

コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。

関連記事

  • Parallel Universe マガジンParallel Universe マガジン Parallel Universe へようこそ。 米国インテル社が四半期に一度オンラインで公開しているオンラインマガジンです。インテルの技術者によるテクノロジーの解説や、最新ツールの紹介など、並列化に関する記事を毎号掲載しています。第1号からのバックナンバーを PDF 形式で用意しました、ぜひご覧ください。 12 […]
  • インテル® AVX 命令を使用した complex float データ型の IIR フィルターの実装インテル® AVX 命令を使用した complex float データ型の IIR フィルターの実装 この記事は、インテル® ソフトウェア・ネットワークに掲載されている「Intel® AVX Realization Of IIR Filter For Complex Float Data」の日本語参考訳です。 はじめに この記事では、インテル® AVX の SIMD (Single Instruction […]
  • 比較関数の罠比較関数の罠 この記事は、インテル® デベロッパー・ゾーンに公開されている「The Evil within the Comparison Functions」の日本語参考訳です。 この記事の PDF […]
  • インテル® AVX を使用した IIR ガウスぼかしフィルターの実装インテル® AVX を使用した IIR ガウスぼかしフィルターの実装 この記事は、インテル® ソフトウェア・ネットワークに掲載されている「IIR Gaussian Blur Filter Implementation using Intel® Advanced Vector Extensions」の日本語参考訳です。 ソースのダウンロード: gaussian_blur.cpp […]
  • インテル® IPP サンプル – エラーの修正インテル® IPP サンプル – エラーの修正 この記事は、インテル® ソフトウェア・ネットワークに掲載されている「Intel IPP Samples for Windows - error correction」の日本語参考訳です。 この記事は、PVS-Studio を使用することでプログラムがどのように安全になるかを説明した記事の 1 […]