web-dev-qa-db-ja.com

memcpy()、サイズパラメータの値はどうあるべきですか?

Int配列を別のint配列にコピーしたい。それらは長さに同じ定義を使用するため、常に同じ長さになります。

Memcpy()のサイズパラメータの次の2つの代替案の長所と短所は何ですか?

memcpy(dst, src, ARRAY_LENGTH*sizeof(int));

または

memcpy(dst, src, sizeof(dst));

2番目のオプションは常に機能しますか?内容に関係なく?

最後のものを支持する1つのことは、配列が変更された場合、memcpy()を更新するためのハウスキーピングになることです。

ありがとう

19
Tomas

dstがサイズを持つ配列として宣言されている限り、sizeofはその配列のサイズをバイト単位で返します。

int dst[ARRAY_LENGTH];

memcpy( dst, src, sizeof(dst) ); // Good, sizeof(dst) returns sizeof(int) * ARRAY_LENGTH

dstがそのような配列の最初の要素(配列自体と同じ型)へのポインターである場合、機能しません。

int buffer[ARRAY_LENGTH];
int* dst = &buffer[0];

memcpy( dst, src, sizeof(dst) ); // Bad, sizeof(dst) returns sizeof(int*)
28
Timbo

配列(実際のもの)がある場合は、sizeof(array)トリックを使用できますが、コードをリファクタリングして、配列がポインターに減衰した場所にコードをプッシュすると(またはメモリが最初はポインタ(malloc/new)に割り当てられており、既知のサイズを渡す必要があります。

ソースと宛先の相対的なサイズを無視します。つまり、以降の説明でそれらが同じであると仮定して、C++を使用している場合は、配列のタイプセーフサイズカウントを提供し、失敗するメタプログラミングトリックをお勧めします。ポインタで使用しようとする場合はコンパイルします。

template <typename T, int N>
inline int array_memory_size( T (&a)[N] ) { return sizeof a; }

そのように:

int main() {
   int array[10];
   int *ptr = array;
   int orig[10] = { 0 };
   memcpy( array, orig, array_memory_size(array) ); // ok
   //memcpy( ptr, orig, array_memory_size(ptr) ); // compilation error
}

いつでもリファクタリングし、配列が減衰した場所にコードが移動した場合(または静的に割り当てられた配列を動的に割り当てられた配列に置き換えた場合)、サイズ計算を修正する必要があることがコンパイラーから通知されます。

sizeof(dst)は、dstがコンパイル時にサイズがわかっている配列である場合にのみ正しいです:_int arr[ARRAY_LENGTH]_またはC99可変長配列などそれ以外の場合は、宛先配列の長さではなく、ポインタのサイズを返します。

将来のバグを回避するには、一貫性を保ち、最初の形式であるタイプのサイズ*長さを優先します。

5
Gregory Pakosz

Mallocを使用して割り当てた場合は、配列のサイズを指定する必要があります

int * src = malloc(ARRAY_LENGTH*sizeof(*src));
int * dst1 = malloc(ARRAY_LENGTH*sizeof(*dst));
memcpy(dst1,src,ARRAY_LENGTH*sizeof(*dst));

静的配列で割り当てた場合は、sizeofを使用できます

int dst2[ARRAY_LENGTH];
memcpy(dst2,src,sizeof(dst2));
3
Scott Wales

2番目のオプションは常に機能しますか?内容に関係なく?

2番目のオプションは、欠落している_)_ anddstが静的配列(つまり、タイプ_int[123]_)である場合にのみ機能します。

dstが不明なサイズ(つまり、_int[]_)である場合、dstはポインターに減衰しているため、_sizeof dst_はポインターサイズのみを返します。この場合、sizeof(*dst)*ARRAY_LENGTHを使用する必要があります。

3
kennytm

2番目のオプションは常に機能しますか?内容に関係なく?

両方の条件が満たされた場合にのみ機能します。

  • dstはポインターではなく通常の配列です
  • srcdstは同じサイズです
1
el.pescado

sizeof(X)は常にXのバイト数を提供します。Xが10のuint16_t配列の場合、sizeof(X)は20を返します

uint16_t X[10]={0};
cout<<"sizeof x: "<<sizeof(X);

$> sizeof x: 20

要素の数が必要な場合は、バイト演算を少し行う必要があります。
8ビット= 1バイト
16ビット= 2バイト
32ビット= 4バイト
64ビット= 8バイト

そのため、実行できる要素の数を取得するには:

 numb_of_elements = ( sizeof(X)/sizeof(X[0]) );

その結果:

uint32_t source[100]={0};
memcpy((void*) dest, (void*) source, ( sizeof(source)/sizeof(source[0]) ));

もちろん、(sizeof(X)/ sizeof(X [0]))を定数/変数にして、毎回計算しないようにすることをお勧めします。(コンパイラが常にこれを最適化するかどうかはわかりません)

1
AlexC_JEng

Dstの型がint *であると仮定すると、sizeof(dst)はポインタ自体のサイズ(つまり、32ビットシステムでは4、64ビットシステムでは8)を返すため、2番目の例では、この多くのバイトのすべてのコピーのみが実行されます。最初のものはコンテンツの実際のサイズを正しく使用します。

1
Mark_Carrington

場合によります。 arrとpointerはどちらも配列ですが、sizeof()は、コンパイル時に宣言されたarrの正しいサイズのみを返します。

int main() {
        int arr[10];
        int * pointer;
        pointer = (int *) malloc(10 * sizeof(int));
        printf("%d\n", sizeof(arr)); // 40
        printf("%d\n", sizeof(pointer)); // 4 or 8
        free(pointer);
}
0
Sjoerd

Dstが(たとえば、mallocを使用して)ヒープから割り当てられた場合、2番目のソリューションは機能しません。 sizeof(dst)は、コンパイラに認識されている場合にのみ機能します。たとえば、次の例は、sizeof(dst)がポインタのサイズ(4〜8バイト)と等しいため失敗します。

#define ARRAY_LENGTH 10
int *dst;

dst = malloc(ARRAY_LENGTH*sizeof(int));
memcpy(dst, src, sizeof(dst)); // sizeof dst in this case would be 4 bytes on 32 bit system

このコードセグメントは常に機能します。

#define ARRAY_LENGTH 10
int *dst;

dst = malloc(ARRAY_LENGTH*sizeof(int));
memcpy(dst, src, ARRAY_LENGTH*sizeof(int)); // sizeof would be 40 bytes
0

どう?

memcpy(dst, src, &src[ARRAY_LENGTH] - &src[0]);

これは、個々の要素のサイズが実際の配列の各項目が取るサイズよりも小さい場合でも機能するはずです。

0
squelart

memcpy()、サイズパラメータの値はどうあるべきですか?

ソースバッファーのサイズと宛先バッファーのサイズの間の最小値でなければなりません。

従来、ソースバッファーのサイズが使用されていました。それは宛先バッファをオーバーフローすることがありました...したがって、ソースと宛先の両方のバッファサイズを指定する関数の「より安全な」バージョンを使用する方が良いです。

ISO/IEC TR24731 を介して「より安全な」機能を利用できます。これには、一貫した戻り値や一貫した文字列処理動作など、さらに多くの機能があります。

「より安全な」関数は現在C標準の一部なので、どこでも利用できるようになっています。したがって、memcpy_s

Linuxでは機能が提供されていないため、これを使用することはできません(標準への準拠に関するマーケティングの誇大宣伝を信じないでください))。 Linuxでは、「独自の」ラッパーをロールする必要があります。

誰もがより安全な機能のファンであるとは限りません。たとえば、 TR 24731の「安全な」機能を使用していますか? を参照してください。それについて私が言えることのすべては、 複数のlibunpバッファオーバーフロー です。何百万ものルーターとゲートウェイが複数の脆弱性の影響を受け、多くはパッチが適用されないままです。そして、それらはより安全な機能によって止められたであろうバグのためでした。 「このマイクロソフトのがらくたを使わないでください」と言っているすべての人への+1。

0
jww