I have an API with a publicly facing struct A and an internal struct B and need to be able to convert a struct B into a struct A. Is the following code legal and well defined behavior in C99 (and VS 2010/C89) and C++03/C++11? If it is, please explain what makes it well-defined. If it's not, what is the most efficient and cross-platform means for converting between the two structs?
struct A {
uint32_t x;
uint32_t y;
uint32_t z;
};
struct B {
uint32_t x;
uint32_t y;
uint32_t z;
uint64_t c;
};
union U {
struct A a;
struct B b;
};
int main(int argc, char* argv[]) {
U u;
u.b.x = 1;
u.b.y = 2;
u.b.z = 3;
u.b.c = 64;
/* Is it legal and well defined behavior when accessing the non-write member of a union in this case? */
DoSomething(u.a.x, u.a.y, u.a.z);
return 0;
}
UPDATE
I simplified the example and wrote two different applications. One based on memcpy and the other using a union.
Union:
struct A {
int x;
int y;
int z;
};
struct B {
int x;
int y;
int z;
long c;
};
union U {
struct A a;
struct B b;
};
int main(int argc, char* argv[]) {
U u;
u.b.x = 1;
u.b.y = 2;
u.b.z = 3;
u.b.c = 64;
const A* a = &u.a;
return 0;
}
memcpy:
#include <string.h>
struct A {
int x;
int y;
int z;
};
struct B {
int x;
int y;
int z;
long c;
};
int main(int argc, char* argv[]) {
B b;
b.x = 1;
b.y = 2;
b.z = 3;
b.c = 64;
A a;
memcpy(&a, &b, sizeof(a));
return 0;
}
Profiled Assembly [DEBUG] (Xcode 6.4, default C++ compiler):
Here is the relevant difference in the assembly for debug mode. When I profiled the release builds there was no difference in the assembly.
Union:
movq %rcx, -48(%rbp)
memcpy:
movq -40(%rbp), %rsi
movq %rsi, -56(%rbp)
movl -32(%rbp), %edi
movl %edi, -48(%rbp)
Caveat:
The example code based on union produces a warning regarding variable 'a' being unused. As the profiled assembly is from debug, I don't know if there is any impact.
struct C
is an incomplete type. Which makesstruct B
an incomplete type. Which makesunion U
an incomplete type. There is no way (AFAIK) that you can expose U/B and hide C like that. You can only have pointers to incomplete types. – Saturatestd-discussion
both of those should be considered authoritive. Although thestd-discussion
thread makes it clear there is a lot of unspecified behavior but what it clear is that the alternativememcpy
is well defined and equally efficient. – Mcclaryvolatile struct...
- changes to one structure cached in registers, not flushed to RAM, then the (old) RAM values read through the "other side". – Joinerint a = 5; a = 10; printf("%d",a)
- you will never get "anything else than "10" as result because the system guarantees the write will end (for all practical purposes) before the next command. This won't work with writing to memory underlying the variable.const int a=5; int* b = (int*) &a; *b = 10; printf("%d",a);
. You're most likely getting a "5". But makevolatile const int a
and the atomicity within a single thread is enforced. – Joiner