10

既知の構造体をメモリにコピーする場合、memcpyまたは逆参照を使用しますか?なぜ?具体的には、次のコードで:

#include <stdio.h>
#include <string.h>

typedef struct {
    int foo;
    int bar;
} compound;

void copy_using_memcpy(compound *pto, compound *pfrom)
{
    memcpy(pto, pfrom, sizeof(compound));
}
void copy_using_deref(compound *pto, compound *pfrom)
{
    *pto = *pfrom;
}

int main(int argc, const char *argv[])
{
    compound a = { 1, 2 };
    compound b = { 0 };
    compound *pa = &a;
    compound *pb = &b;

    // method 1
    copy_using_memcpy(pb, pa);
    // method 2
    copy_using_deref(pb, pa);
    printf("%d %d\n", b.foo, b.bar);

    return 0;
}

方法1と方法2のどちらがいいですか?gccによって生成されたアセンブリを調べたところ、方法2は方法1よりも少ない命令を使用しているようです。この場合、方法2の方が望ましいということですか?ありがとうございました。

4

3 に答える 3

15

構造体をコピーするときに、割り当てではなく使用する正当な理由を考えることはできません(深いコピーや、構造体のハック柔軟な配列メンバーを含む何かをmemcpy()行う必要がない限り、いずれも当てはまりません)この場合)。

それらはまったく同じセマンティクスを持っており、割り当ては(a)コンパイラーに最適化の機会を与える可能性が高く、(b)サイズを間違えるリスクが少なくなります。

一部の非常に古いCコンパイラは、おそらく構造体の割り当てをサポートしていませんでしたが、それはもはや重要な問題ではありません。

(C ++での割り当てを好む追加の理由がありますが、あなたの質問はCについてです。)

ちなみに、かっこは

(*pto) = (*pfrom);

不要です。単項*結合は、次のように十分に緊密にバインドされます。

*pto = *pfrom;

ほとんどの読者にとって正しく、十分に明確です。

于 2012-09-11T06:30:59.050 に答える
3

あなたが言及したのとまったく同じ理由で、私は方法2(逆参照法)を好みます。Memcpyはバイトごとのコピーを実行し、関数呼び出しのオーバーヘッドがありますが、間接参照はコピーのみを実行し、余分なオーバーヘッドはありません。

逆参照と割り当ても読みやすくなります(特に、余分な括弧を省略した場合:

*dest = *src;

)。

于 2012-09-11T06:31:42.157 に答える
1

私はこれをGoogleのベンチマークで実行しようとしました:

#include <benchmark/benchmark.h>
#include <stdio.h>
#include <string.h>

typedef struct {
    int foo;
    int bar;
    int a;
    int b;
    int c;
    int d;
    int e;
    int f;
    int g;
} compound;

static void copy_using_memcpy(benchmark::State& state) {
    compound a = {0, 0, 0, 0, 0, 0, 0, 0, 0};
    compound b = {0, 0, 0, 0, 0, 0, 0, 0, 0};
    compound* pa = &a;
    compound* pb = &b;
    for (auto _ : state) memcpy(pa, pb, sizeof(compound));
}
static void copy_using_deref(benchmark::State& state) {
    compound a = {0, 0, 0, 0, 0, 0, 0, 0, 0};
    compound b = {0, 0, 0, 0, 0, 0, 0, 0, 0};
    compound* pa = &a;
    compound* pb = &b;
    for (auto _ : state) *pa = *pb;
}

BENCHMARK(copy_using_memcpy);
BENCHMARK(copy_using_deref);

BENCHMARK_MAIN();

結果は次のようになります。

> g++ benchmark.cc -lbenchmark -lpthread && ./a.out
2020-11-20T20:12:12+08:00
Running ./a.out
Run on (16 X 1796.56 MHz CPU s)
CPU Caches:
  L1 Data 32 KiB (x8)
  L1 Instruction 32 KiB (x8)
  L2 Unified 512 KiB (x8)
  L3 Unified 4096 KiB (x1)
Load Average: 0.29, 0.15, 0.10
------------------------------------------------------------
Benchmark                  Time             CPU   Iterations
------------------------------------------------------------
copy_using_memcpy       2.44 ns         2.44 ns    282774223
copy_using_deref        1.77 ns         1.77 ns    389126375

元の例では、フィールドが2つしかないため、時間はほぼ同じです。

于 2020-11-20T12:17:40.470 に答える