58

I have two identical (but differently named) C structures:

typedef struct {
      double x;
      double y;
      double z;
} CMAcceleration;


typedef struct {
    double x;
    double y;
    double z;   
} Vector3d;

Now I want to assign a CMAcceleration variable to a Vector3d variable (copying the whole struct). How can I do this?

I tried the following but get these compiler errors:

vector = acceleration;           // "incompatible type"
vector = (Vector3d)acceleration; // "conversion to non-scalar type requested"

Of course I can resort to set all members individually:

vector.x = acceleration.x;
vector.y = acceleration.y;
vector.z = acceleration.z;

but that seems rather inconvenient.

What's the best solution?

Ortwin Gentz
  • 52,648
  • 24
  • 135
  • 213
  • 1
    Can't you just typedef (say typedef struct CMAcceleration Vector3d) ? Ooops, someone had already pointed out... – Nyan Oct 22 '10 at 11:37

9 Answers9

66

That's your only solution (apart from wrapping it into a function):

vector.x = acceleration.x;
vector.y = acceleration.y;
vector.z = acceleration.z;

You could actually cast it, like this (using pointers)

Vector3d *vector = (Vector3d*) &acceleration;

but this is not in the specs and therefore the behaviour depends on the compiler, runtime and the big green space monster.

Georg Schölly
  • 124,188
  • 49
  • 220
  • 267
  • 4
    +1: Good answer. Describes both the only method that's guaranteed to work, and the method that will usually work in practice, and the reason why this method is technically undefined. – Oliver Charlesworth Oct 22 '10 at 11:18
  • 3
    +1 I would only add that the casting technique is pretty common - it is not like its truly evil. – Prof. Falken Oct 22 '10 at 11:41
  • 3
    +1 for wrapping it in a function. Even something as trivial as this is well worth making a subroutine for. – alesplin Oct 22 '10 at 15:43
  • What happens if we declare CMAcceleration as `struct { Vector3d vec; };`? Then CMAcceleration instances will have Vector3d in first `sizeof(Vector3d)` bytes. Would that eliminate strict aliasing when performing pointer casting? – holgac Apr 11 '15 at 09:03
  • Then we wouldn't need to cast pointers anymore. We could just straightfowardly assign `vector = acc.vec;`. – Hermann Döppes Jan 09 '16 at 10:40
16

You could use a pointer to do the typecast;

vector = *((Vector3d *) &acceleration);
David Gelhar
  • 27,873
  • 3
  • 67
  • 84
  • 6
    It should be pointed out that the compiler is not obliged to ensure that both structs are packed and aligned in the same way. – Oliver Charlesworth Oct 22 '10 at 11:10
  • 15
    This is undefined behaviour because of strict aliasing. http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html – Secure Oct 22 '10 at 11:12
  • 1
    @Secure That's a shame because I would like to use this technique to not copy but actually alias (change the type of) a struct. – Michael Oct 22 '14 at 02:40
  • 1
    @Michael: Specify that when using gcc or clang, the code must be compiled with the `-fno-strict-aliasing` flag (other compilers may use a flag with the same name, or may be less aggressive in their aliasing optimizations than gcc or clang). – supercat Sep 12 '17 at 14:58
7

memcpy(&vector, &acceleration, sizeof(Vector3d));

Please note that this works only, if the physical layout of the structs in memory are identical. However, as @Oli pointed out, the compiler is not obliged to ensure this!

groovingandi
  • 1,986
  • 14
  • 16
  • 4
    It should be pointed out that the compiler is not obliged to ensure that both structs are packed and aligned in the same way. – Oliver Charlesworth Oct 22 '10 at 11:17
  • @Oli Charlesworth: you're right and I updated the answer accordingly – groovingandi Oct 22 '10 at 11:33
  • @OliverCharlesworth: it would take a pathologically perverse compiler to break this assumption, esp. considering the accepted answer to this question: https://stackoverflow.com/questions/19804655/are-c-structs-with-the-same-members-types-guaranteed-to-have-the-same-layout-in – chqrlie Sep 12 '17 at 19:46
  • 1
    @chqrlie The accepted answer you linked only says that a compiler must make sure the layout is equal, if both structs are used as part of the same union. But it says nothing about structs being used outside a union context. So a compiler may enforce equal layout in a union case, since it has to according to standard, yet choose different layouts in other cases because of some strange platform specific optimization for example. – Mecki Jun 05 '18 at 14:26
6

You use an utility function for that:

void AccelerationToVector( struct CMAcceleration* from, struct Vector3d* to )
{
     to->x = from->x;
     to->y = from->y;
     to->z = from->z;
}
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
sharptooth
  • 167,383
  • 100
  • 513
  • 979
6

Another version of the utility function making use of C99:

static inline struct Vector3d AccelerationToVector(struct CMAcceleration In)
{
    return (struct Vector3d){In.x, In.y, In.z};
}

With the compiler optimization turned up (e.g., -Os), this should turn into absolutely no object code when invoked.

lundblade
  • 91
  • 1
  • 3
5

Why dont you use.

typedef CMAcceleration Vector3d;

(instead of creating a whole new structure)

in that case vector = acceleration; compiles just fine.

SysAdmin
  • 5,455
  • 8
  • 33
  • 34
  • I get a `warning: 'typedef struct Vector3d Vector3d' does not refer to the unqualified type, so it is not used for linkage`. Also in this case, `CMAcceleration` is in a weakly linked framework, so I refrain from using it in my .h file. – Ortwin Gentz Oct 22 '10 at 11:50
  • 6
    If the `CMAcceleration` struct is coming from a separate framework, you would be best advised to do the field-by-field copy, instead of the memcpy or type-punning tricks, to make your code robust in the event of any future changes in the other framework. (Even if you know the struct layouts are identical today, maybe they won't remain that way in subsequent releases.) – David Gelhar Oct 22 '10 at 13:03
4

This is achieved easily through a union:

typedef struct {
      double x;
      double y;
      double z;
} CMAcceleration;

typedef struct {
    double x;
    double y;
    double z;
} Vector3d;

typedef union {
    CMAcceleration acceleration;
    Vector3d vector;
} view;

int main() {
    view v = (view) (Vector3d) {1.0, 2.0, 3.0};
    CMAcceleration accel = v.acceleration;
    printf("proof: %g %g %g\n", accel.x, accel.y, accel.z);
}
johnco
  • 41
  • 4
  • 2
    Do note that if you do this, you are no longer adhering to standard C. Yes, GCC and Clang both support access to the non-current union members. No, the standard does not allow this. – Qqwy Oct 20 '18 at 14:38
  • 1
    @Qqwy I know I'm late to the party, but I think this actually _is_ standard-conforming. See [C99§6.5.2.3/5](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf#page=85&zoom=auto,-66,518) (as well as footnote #82 at the bottom of that page). – TSmith Mar 30 '23 at 17:47
  • @TSmith Thank you! Since posting my last message more than 5 years ago, I have learned that I was confusing the semantics of C (where it is fine) and C++ (where it is Undefined Behaviour, though supported by G++ and Clang++). One of the subtle places where the languages differ... See [this question/answer](https://stackoverflow.com/a/11996970/1067339) for more info. – Qqwy Mar 31 '23 at 21:52
2

A safe (albeit somewhat convoluted) way to do it would be to use a union:

union { CMAcceleration a, Vector3d v } tmp = { .a = acceleration };
vector = tmp.v;

Values are reinterpreted (since C99) when the accessed member is not the last set one. In this case, we set the acceleration and then we access the vector, so the acceleration is reinterpreted.

This is the way the NSRectToCGRect function is implemented, for example.

0

You could create a union with pointers that way you avoid copying data.

example :

struct Ocp1SyncHeader
{
    aes70::OCP1::OcaUint8 syncVal;          
    aes70::OCP1::Ocp1Header header;
};

struct convertToOcp1SyncHeader
{
    union
    {
        Ocp1SyncHeader* data;
        const uint8_t* src;
    };
    convertToOcp1SyncHeader& operator=(const uint8_t* rvalue) 
    { 
      src = (const uint8_t*)rvalue; 
      return *this; 
    }
};

** access like this:

convertToOcp1SyncHeader RecvData;
RecvData = src;  // src is your block of data that you want to access

** access members like this :

RecvData.data.header
serup
  • 3,676
  • 2
  • 30
  • 34