When copying a known struct in memory, would you prefer using memcpy or dereference? why?
Specifically, in the following code:
#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; }
Would you prefer method 1 or method 2? I looked at the assembly generated by gcc, and it seems that method 2 uses less instructions than method 1. Does it imply that method 2 is preferable in this case? Thank you.
I can't think of any good reason to use memcpy()
rather than an assignment when copying a struct (as long as you don't need to do a deep copy or something involving the struct hack or a flexible array member, none of which apply in this case).
They have exactly the same semantics, and the assignment (a) likely gives the compiler more opportunities for optimization, and (b) has less risk of getting the size wrong.
Some very old C compilers probably didn't support struct assignment, but that's no longer a significant concern.
(There are additional reasons to prefer assignment in C++, but your question is about C.)
Incidentally, the parentheses in
(*pto) = (*pfrom);
are unnecessary; the unary *
binds tightly enough that this:
*pto = *pfrom;
is both correct and sufficiently clear to most readers.
I tried to run this with Google's benchmark:
#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();
The result is like:
> 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
In the original example, with only two fields, the time is roughly the same.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With