开发者

how to pass the union pointer as parameter in C? and what is the inside of union?

开发者 https://www.devze.com 2023-04-11 14:48 出处:网络
I have a question about union in c. The following code is the server side, we receive Signal2 from the client side, in the service function,can I check the signal type like this: data开发者_运维问答_p

I have a question about union in c. The following code is the server side, we receive Signal2 from the client side, in the service function, can I check the signal type like this: data开发者_运维问答_p->header.type? and in the switch case, how could I pass the parameter to function printInfo(), the parameter of it is the pointer to the union, how to pass? And the last question is the pointer to the union, point to what? and what is inside of the union, when these three structs are allocated, how could they fit into the union(with the size of the large struct)?

   union
    {
        Header  header;
        Signal1 signal1;
        Signal2 signal2;
    } Data;

struct Header 
{   
    int type:
    int protocol;
    int clientId;
    int serverId;
};


struct Signal1
{
    int type:
    int protocol;
    int clientId;
    int serverId;
    char[80] something;
};


struct Signal2
{
    int type:
    int protocol;
    int clientId;
    int serverId;
    int[100] something;
};


void service(Data* data_p) {

    switch(data_p->header.type) {
        case 1:
            printInfo(&data_p->header);
        case 2:
            printInfo(data_p->header);
        case 3:
            data_p->signal2->something[5];
    }   
}


void printInfo(Data* data_p) 
{
    data_p->header.primitive;
}


In your example it looks like you want to rely on the fact that the type, protocol, clientId, serverId fields from Header, Signal1, Signal2 will be aligned the same way. But nothing in the C or C++ standard guarantees that AFAIK. So it would probably work with most C compilers but it is a hack, where such a hack is not necessary.

It seems like it would be better to do things as follows :

struct Header 
{
    int type:
    int protocol;
    int clientId;
    int serverId;
};

struct Signal1
{
    char something[80];
};

struct Signal2
{
    char something[100];
};

struct Message
{
    struct Header header;
    union
    {
        struct Signal1 signal1;
        struct Signal2 signal2;
    } payload;
};

As for your last question if I understand it correctly, the union is allocated enough memory for the largest of it's members, and aligned at the strictest of it's members' boundaries, and at any point in time contains valid data for one of it's members (but nothing indicating which is the correct one). And the pointer to union is the address of the start of that memory area.


I think your declaration of the union is the part that isn't working as expected.

Try the following.

union Data
{
    Header  header;
    Signal1 signal1;
    Signal2 signal2;
};

This should make a union type called Data instead of a single variable of an anonymous union type.

From here you should be able access the Header even when passed a Signal2 object via data_p->header.Type.


Something like

enum{Header,Signal1,Signal2} type = Header;

....
union Data
{
    Header header;
    Signal1 signal1;
    Signal2 signal2;
};

struct Packet
{
    type data_type; // points to one of Header, Signal1 or Signal2
    union Data data;
};

could be a good way of passing the pointer to what the union holds.

Or another argument to the function that processes the union, or a global variable, or a configuration parameter. As far as I know, what a union holds has to be explicitly told.


I think the header structure in the union is meant to be the common prefix of variables. so you would check the type of this union like this:

 switch(data_p->header.type) {
     case 1:
         printInfo(data_p);
     case 2: //unknown type?
         printInfo(data_p);
     case 3: // type is obviously signal2
         data_p->signal2->something[5];
 }   

So yes you can check with data_p->header.type. it seems to me that you could rewrite the code a little to get it more obvious

enum { 
  HEADER_TYPE  = 1, // fill in the correct type numbers here to match the documentation
  SIGNAL1_TYPE = 2,
  SIGNAL2_TYPE = 3
};
switch(data_p->header.type) {
    case 1:
        printInfo(data_p);
    case 2: //unknown type?
        printInfo(data_p);
    case SIGNAL2_TYPE: // type is obviously signal2
        data_p->signal2->something[5];
}   

the other thing i notices in printInfo header doesn't have a variable called primitive from the switch statement you would call use printInfo(data_p) to call it correctly.

The most important thing here is that the programmer has to make sure that the type variable is always set to the correct value, corresponding to the right struct of the union part. i.e. if the struct is a signal1 its 1 and if it is a signal2 its 2 and so on

But the use of the union is quite useless here, since all fields are identical except the last one and the header is obviously meant to access the common fields.

struct Header
{
  int type:
  int protocol;
  int clientId;
  int serverId;
};

struct Signal1
{
  struct Header header;
  int[80] something;
};

struct Signal2
{
  struct Header header;
  int[100] something;
};

enum { 
  SIGNAL1_TYPE = 0,
  SIGNAL2_TYPE
};

void service(struct Header* data_p) {

  switch(data_p->header.type) {
    default:
        printInfo(data_p);
      break;
    case SIGNAL1_TYPE:
        /* your code here */
        call_func( (struct Signal1 *) data_p );
      break;
    case SIGNAL2_TYPE:
        /* your code here */
        call_func( (struct Signal2 *) data_p );
      break;
  }   

}


The syntax of accessing union members is very similar to that used to access structure members. One uses the . operator to access a union field through a plain union and the -> to access a union field through a pointer to union. Assuming the Data union in your example:

Data v;
Data *p = &v;
int ftype;

ftype = v.header.type;
ftype = p->header.type;

As to the parameter to printinfo, the parameter type shown in your code is correct, it should be a pointer to Data. However, the code inside printInfo is not correct, as pointed out elsewhere. I would expect it to contain a switch on the header type field similar to the one in the service method, as in the following:

void printInfo(Data* data_p) 
{
    printf("Type %d, protocol %d, clientId %d, serverId %d ",
         data_p->header.type, data_p.header.protocol,
         data_p->header.clientId, data_p->header.serverId);

    switch(data_p->header.type) {
        case 1:
            // Signal 1
            printf("signal 1 (string): %s", data_p->signal1.something);
        case 2:
            // Signal 2
            printf("signal 2 (int array): %d", data_p->signal2.something[0]);
        // and so on
    }
    printf("\n");
}

As has also been said elsewhere, a union only contains one of its members at a time. The compiler gives it enough space to contain the largest possible member. Storing one of the possible data types in a union overlays the data on whatever was there before.

The programmer must keep track of which of the possible elements has been put into a union, and access it appropriately. In the Data union in your example, the header.type field is used for this purpose.


you need to specialize printinfo()

like void printinfo(Header*)

void printinfo(Signal1*)

void printinfo(Signal2*)

then pass it like case 2: printInfo(&data_p->signal1); // pass the right union member to the right function

so really you are not passing the union, you are passing the data inside the union.

second question: Unions are not structs. unions have the size of the largest member. you shoudl read sizeof a union in C/C++

good luck

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号