Вопрос

I am somewhat unfamiliar with unions, have done some reading on them and am having trouble figuring this out.

Someone has defined a union for me:

union CANDATA   // Unionize for easier cooperation amongst types
{
    unsigned long long ull;
    signed long long   sll;
    u32        ui[2];
    u16        us[4];
    u8     uc[8];
    u8     u8[8];
    s32        si[2];
    s16        ss[4];
    s8         sc[8];
};

There is also a struct that has this union as one of its members:

struct CANRCVBUF        // Combine CAN msg ID and data fields
{ //                               offset  name:     verbose desciption
    u32 id;         // 0x00 CAN_TIxR: mailbox receive register ID p 662
    u32 dlc;        // 0x04 CAN_TDTxR: time & length p 660
    union CANDATA cd;   // 0x08,0x0C CAN_TDLxR,CAN_TDLxR: Data payload (low, high)
};

I am creating an instance of CANRCVBUF:

static struct CANRCVBUF increasingMessage = {
    0x44400000,     /* 11 bit id */
    0x00000002,     /* 2 data bytes */
    {
        0x0000
    }
};

What I want to do next, is create a loop that increments the data portion of increasingMessage. This is where I am having trouble. My attempt was:

if(increasingMessage.cd + 1 > 65535) {
    increasingMessage.cd = 0x0000;
} else {
    increasingMessage++;
}

I realize that by using increasingMessage.cd I am accessing the union, not the data in the union. My trouble is, how do I know which member of the union is used when creating increasingMessage?

Any tips are greatly appreciated

Это было полезно?

Решение

You need to supply which of the union members you are addressing. The compiler needs to know if you want to test ull (as unsigned long long) or sc[0] (as unsigned char), or any of the other members.

In your case you probably want to use

if(increasingMessage.cd.us[3] + 1 > 65535) {
  ...

-- that is, if this part of the union is the number you are looking for. (It's not entirely clear from your code.)

Union members are accessed the same as struct members, they only difference is that they get stored differently.


Note that the particular field I chose will never be >65535 ... So choose your test member carefully ...

Другие советы

In a union, all the fields are overlapping. Hence, the size of a union is equal to the size of the largest field in it.

As to your question:

You need to specify the name of the field on which you want to apply the operation, by adding a reference to it after increasingMessage.cd. For example, increasingMessage.cd.ull. But please note that all the other fields in your union will also be affected every time you change this field, as they are all overlapping each other.

Usually, in order to indicate the relevant field, the entire union is placed within a struct which contains an additional enum which is used in order to indicate that field. For example:

typedef enum
{
    MSG_1,
    MSG_2,
    MSG_3,
    ...
}
msg_type_e;


typedef struct
{
    msg_type_e type;
    union CANDATA cd; // 0x08,0x0C CAN_TDLxR,CAN_TDLxR: Data payload (low, high)
}
msg_t;

Usually when you use unions you should know which member has correct data in it. If it is not possible, you will need to add a variable to CANRCVBUF to indicate which member is correct. That's a C way to use inheritance.

The standard says it is the first named member of a union that receives a static initialiser, thus you are implicitly saying increasingMessage.cd.ull = 0x0000 when you declare it.

In general it is most robust to work on the largest member of a union in cases like this. If you don't know any better, then:

increasingMessage.cd.ull++;
increasingMessage.cd.ull &= 0xFFFF;

is the safest way to do a 16-bit count - if you don't know the endianness then you don't know which of cd.us[0] and cd.us[3] is the least significant 16 bits of a CANDATA so you might as well guarantee it's consistent in local endianness. Of course, if it has to be communicated to another system that's another matter, but documentation should clearly define any requirements there.

So, if I got your intention correctly, you want to loop through increasingMessage.cd, from 0 to 65535. As others stated, union's members overlap with each other, i.e. ull, sll, ui[2], ..., all point to the same memory. In other words, these different fields are in the same memory address and store the same value, but you can represent that same value with different types by accessing different fields.

Thus, a simple loop over eg ull can do the trick, like:

for (increasingMessage.cd.ull = 0;increasingMessage.cd.ull<65536;increasingMessage.cd.ull++)
{
}

Now, if you only want to loop through the lease significant bits of your data, you need to make the loop a little bit more complicated:

for (increasingMessage.cd.ull &= 0xFFFFFF00;(increasingMessage.cd.ull & (0x000000FF))<65536; increasingMessage.cd.ull++)
{
...
}

Finally, pay attention that how different fields of increasingMessage.cd is interpreted depends on the endianness of your target machine as well as your programs semantic. For example, if increasingMessage.cd.ull=0x11223344, this might mean that cd.us[0]=0x11 and cd.us[3] = 0x44, or alternatively cd.us[0]=0x44 and cd.us[3]=0x44, depending on the endianness.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top