関数foo(int[] nums)
があり、これは基本的にfoo(int* nums)
と同等であると理解しています。 foo
の中では、nums
inが指す配列の内容をいくつかのint[10]
は、foo
のスコープ内で宣言されています。次の内容が無効であることを理解しています。
void foo (int[] nums)
{
myGlobalArray = *nums
}
アレイをコピーする適切な方法は何ですか? memcpyを次のように使用する必要があります。
void foo (int[] nums)
{
memcpy(&myGlobalArray, nums, 10);
}
またはforループを使用する必要がありますか?
void foo(int[] nums)
{
for(int i =0; i < 10; i++)
{
myGlobalArray[i] = nums[i];
}
}
欠けている3番目のオプションはありますか?
Memcpyはおそらくより高速ですが、それを使用すると間違いを犯す可能性が高くなります。それは最適化コンパイラがどれだけ賢いかに依存するかもしれません。
ただし、コードは正しくありません。そのはず:
memcpy(&myGlobalArray, nums, 10 * sizeof(int) );
はい、3番目のオプションはC++コンストラクトを使用することです。
_std::copy(&nums[0], &nums[10], myGlobalArray);
_
正常なコンパイラでは、次のことを行います。
memcpy()
にコンパイルされます)、一般的に、最悪のシナリオは、memcpy
がインライン化されず、forループに対して少数の追加命令に相当する追加の健全性/アサートチェックを実行する、最適化されていないデバッグビルドです。
ただし、memcpy
は一般に、組み込み関数などを活用するために適切に実装されますが、これはターゲットのアーキテクチャとコンパイラによって異なります。 memcpy
がforループ実装よりも悪いことはまずありません。
人々はしばしばmemcpyのサイズがバイト単位であるという事実につまずき、次のようなことを書きます。
// wrong unless we're copying bytes.
memcpy(myGlobalArray, nums, numNums);
// wrong if an int isn't 4 bytes or the type of nums changed.
memcpy(myGlobalArray, nums, numNums);
// wrong if nums is no-longer an int array.
memcpy(myGlobalArray, nums, numNums * sizeof(int));
ある程度のリフレクションを可能にする言語機能を使用して、ここで身を守ることができます。つまり、一般的な関数では一般に何も知らないため、データについて知っていることではなく、データ自体に関して物事を行います。データについて:
void foo (int* nums, size_t numNums)
{
memcpy(myGlobalArray, nums, numNums * sizeof(*nums));
}
配列は自動的にポインターに減衰するため、「myGlobalArray」の前に「&」を付けたくないことに注意してください。実際には、myGlobalArray [0]へのポインタが保持されているメモリ内のアドレスに「nums」をコピーしていました。
(メモを編集:typo'dしたint[] nums
意味しない場合int nums[]
しかし、 C array-pointer-equivalence chaosを追加しても誰も役に立たないと決めたので、今ではint *nums
:))
オブジェクトでmemcpy
を使用するのは危険です。
struct Foo {
std::string m_string;
std::vector<int> m_vec;
};
Foo f1;
Foo f2;
f2.m_string = "hello";
f2.m_vec.Push_back(42);
memcpy(&f1, &f2, sizeof(f2));
これは、POD(プレーンな古いデータ)ではないオブジェクトをコピーする間違った方法です。 f1とf2の両方に、「hello」を所有していると考えるstd :: stringがあります。そのうちの1つは、破壊するとクラッシュし、両方とも42を含む同じ整数ベクトルを所有していると考えます。
C++プログラマのベストプラクティスは、std::copy
:
std::copy(nums, nums + numNums, myGlobalArray);
Remy LebeauごとのメモまたはC++ 11以降
std::copy_n(nums, numNums, myGlobalArray);
これにより、memcpy
またはmemmove
を使用し、可能であればSSE/vector命令を使用するなど、何を行うかについてコンパイル時に決定できます。もう1つの利点は、次のように書くことです。
struct Foo {
int m_i;
};
Foo f1[10], f2[10];
memcpy(&f1, &f2, sizeof(f1));
後でFooを変更してstd::string
、コードが壊れます。代わりに書く場合:
struct Foo {
int m_i;
};
enum { NumFoos = 10 };
Foo f1[NumFoos], f2[NumFoos];
std::copy(f2, f2 + numFoos, f1);
コンパイラーはコードを切り替えて、追加作業なしで正しいことを行います。コードはもう少し読みやすくなります。
パフォーマンスのために、memcpy(または同等のもの)を使用します。大量のデータを高速で迂回するための高度に最適化されたプラットフォーム固有のコードです。
保守性のために、何をしているのかを考えてください-forループは読みやすく、理解しやすいかもしれません。 (memcpyを間違って取得することは、クラッシュへの高速ルートまたはさらに悪いことです)
基本的に、int、unsigned int、ポインター、データのみの構造体などのPODタイプ(Plain Ol 'Data)を扱っている限り、mem *を使用しても安全です。
配列にオブジェクトが含まれている場合、forループを使用します。適切な割り当てを保証するために=演算子が必要になる場合があります。