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;
}

你喜欢方法一还是方法二?我查看了 gcc 生成的程序集,似乎方法 2 使用的指令比方法 1 少。这是否意味着在这种情况下方法 2 更可取?谢谢你。

4

3 回答 3

15

在复制结构时,我想不出任何使用memcpy()而不是赋值的充分理由(只要您不需要进行深度复制或涉及struct hack灵活数组成员的事情,这些都不适用这个案例)。

它们具有完全相同的语义,并且赋值 (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

我试图用谷歌的基准运行它:

#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

在原始示例中,只有两个字段,时间大致相同。

于 2020-11-20T12:17:40.470 回答