tags:

views:

64

answers:

6

Hi I have the following union which is part of a larger struct and I want to store a uint64_t (64 bits size) data in this union. However i want to store it by accessing the id_data field since the others are not large enough for a complete uint64_t. But i dont know how to assign my uint64_t data into this id_data field.

I know how to read the uint64_t data from this field.But somehow dont know how to assign values to it.

Any help will be appreciated.

Thanks.

union {
    struct id_ts {          /* ICMP Timestamp */
        uint32_t otime; /* Originate */
        uint32_t rtime; /* Receive */
        uint32_t ttime; /* Transmit */
    } id_ts;
    struct id_ip  {
        struct xyz_abc idi_ip;
        /* options and then 64 bits of data */
    } id_ip;
    struct ra_addr id_radv;
    uint32_t id_mask;
    char    id_data[1];
} icmp_nnn;
A: 

Err... why not just make a uint64_t member of the union you can assign to?

As written, you can't write it into the id_data field because that field is only one byte long. Using more space than that (I believe) is implementation defined.

That said, pretty much anything you do with a union is implementation defined anyway, so if you want the nasty nasty gross insane platform dependent way to do it, you use bit shifting to get the desired effect. Note that you may need to change

size_t idx = 0;
uint64_t myData = /* ... */;
union icmp_nnn structure;
for (; idx < 8; ++idx)
{
    // For big endian machines
    structure.id_data[idx] = (myData >> 8*idx) & 0xFF;
    // For little endian machines
    structure.id_data[7-idx] = (myData >> 8*idx) & 0xFF;
}

Billy3

Billy ONeal
Because I am not using this union which is part of a larger struct anywhere else in my code.Also the struct is being used everywhere else in the whole source code,so Im not sure if i can change the size of my union without disturbing other things.
The Stig
@The Stig: If your union isn't large enough to hold 64 bits, than it's not going to be large enough to hold it no matter what you do. As written, the only thing that might change by adding a uint64_t member would be some issues with memory alignment on x64 systems.
Billy ONeal
@The Stig it sounds like maybe you don't understand what a union is? This union has 3 uint32_ts in it, so it is already 96 bits. Adding a uint64_t does not change the size, since it is a union and not a struct.
SoapBox
Also, even though unions can be useful at times, as per the MISRA-C standard, it's best to avoid using unions if possible, as there is a risk that the data may be misinterpreted, when memory is being reused for related purposes.Rule 18.4(required): Unions shall not be used.
IntelliChick
Thanks Soapbox and Billy but I didnt want to add any fields to the union.I wanted to use this union since it was unused and wanted to acesss it thru the already present fields in the union.
The Stig
@The Stig: That doesn't make any sense.
Billy ONeal
A: 

If you can't change the union declaration, memcpy should still let you do what you want.

Alex Martelli
Thanks Alex memcpy worked beautifully.Had not used this function earlier, so was not aware of it.
The Stig
+1  A: 

You could use memcpy:

uint64_t val;
memcpy((void*)&icmp_nnn.id_data[0], (void*)&val, sizeof(val));

You aren't really assigning it to the id_data member. Just memcpy into the memory space used by the union. id_data[0] will correspond to the start of the union.

D.Shawley
Memcpy worked perfectly..Thanks Shawley..
The Stig
A: 

Is there a reason why you can't declare a uInt64_t member of the union?

You say you know how to read the uint64_t data from the id_data field? How are you doing that exactly? The get should not be much different to the set?

IntelliChick
Well to read from the union you get the address and type cast it to the pointer type you want and then read the value from it, as shown by Soapbox below.
The Stig
A: 

To read from the field:

uint64_t ui = *(uint64_t*)icmp_nnn.id_data;

To write to the field;

*(uint64_t*)icmp_nnn.id_data = 12345;

But really you should just add a uint64_t member to the union if it is your own code (i.e. not a library).

SoapBox
Thanks Soapbox ..
The Stig
A: 

You should use memcpy(), but there's really no reason to use the id_data field as the destination - just use the entire union itself, which more clearly expresses your intent.

uint64_t src;
memcpy(&icmp_nnn, &src, sizeof src);
caf
Thanks Caf......
The Stig