-3

I want to cast a pointer to one of two structures depending on the value of a variable. I am trying this but it is not working -

struct typeA
{
     int a;
     int x;
}
struct typeB
{
     int b;
     int a;
     int x;
     int z;
}


int band=1;
void *ptr;

if(band == 1)
            ptr = (struct typeA *)malloc(sizeof(struct typeA));
else if(band == 2)
            ptr = (struct typeB *)malloc(sizeof(struct typeB));

printf("A:%d",ptr->a);//error: structure type required instead of void

What would be the best way to do this?

EDIT: Added structure definitions and error (in code comments) to make more sense.

The Prenx
  • 483
  • 4
  • 15
  • 2
    Cast is not necessary in C.(Also `ptr` is `void *`) Please explain the "not working". – BLUEPIXY Jul 07 '16 at 22:55
  • 1
    I downvoted because you're saying that your code doesn't work, but you're not saying why, what's the error or warning that the compiler gives you. Modify your question providing this info and I will remove my downvote. – nbro Jul 07 '16 at 22:59
  • Stop casting `malloc()` but do give a real type to `ptr`. If there is actually a good reason to have `ptr` be `void *` then why not just allocate the larger of the two sizes? And, ahem, *"it is not working"* is a terrible description of your problem. Include the error message! – DigitalRoss Jul 07 '16 at 23:02
  • Well gee, that won't work. The compiler needs to know what type `ptr` points to. I suppose you could cast `ptr` every time you use it, but wow would that be ugly. And strictly speaking, you must deref pointers to their original types. (A widely disregarded rule however.) That's advanced C anyway, for now, declare two pointers with two different types. – DigitalRoss Jul 07 '16 at 23:05
  • `if(band == 1){ ptr = malloc(sizeof(struct typeA)); /* ...set a... */ printf("A:%d\n", ((struct typeA*)ptr)->a); }else if(band == 2){ ptr = malloc(sizeof(struct typeB)); printf("A:%d\n", ((struct typeB*)ptr)->a); }` – BLUEPIXY Jul 07 '16 at 23:08
  • BTW `struct tag_id { ... } ;` <- need `;` – BLUEPIXY Jul 07 '16 at 23:15

5 Answers5

2

As others have suggested, you may need to change your design as it has some flaws.

However to resolve the compilation error you are getting you can do something like this:

 printf("A:%d",(band == 1) ? ((struct typeA *)ptr)->a : ((struct typeB *)ptr)->a);

You are getting error because you are trying to access member a from a void * when you do (ptr->a).

You need to cast it expicitly to say that ptr is pointing to a struct typeA or struct typeB.

sps
  • 2,720
  • 2
  • 19
  • 38
1

If you don't need to know what type of structure you have, use

if(band == 1)
        ptr = malloc(sizeof(struct typeA));
else if(band == 2)
        ptr = malloc(sizeof(struct typeB));

The return type of malloc is already void *.

However, this is a code smell --- do you really not need to know the size or type of the structure after you create it? I would rethink your design a bit.

Edit Based on the additional code you posted, I strongly recommend a new approach. For example, the fields in typeA and typeB are in a different order, so there is no way for ptr->a to refer to both of them regardless of the type of ptr. If you are stuck with plain C, consider one struct that has a and x and an optional pointer to a separate struct with b and z. Alternatively, just use typeB for everything, and remove typeA!

halfer
  • 19,824
  • 17
  • 99
  • 186
cxw
  • 16,685
  • 2
  • 45
  • 81
1

You should never cast the result of malloc, as doing so can mask other issues. It's also causing a warning here because the types don't match.

Get rid of the casts, and you should get no warnings.

EDIT:

The error is because you're trying to dereference a void *, which is illegal.

Instead of using one variable, use two (one of each type) and set the other to NULL. Alternately, if you know the type of the data the void * points to, you can cast it to the proper type then dereference it.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • Types don't match, but `void*` is the most generic type of pointer, so it should not be a problem to assign a pointer to any other type to a `void*`, or am I wrong? Not the downvoter. – nbro Jul 07 '16 at 22:57
1

The casts here buy you nothing, because the result of each cast is converted immediately to the type of ptr. Either way, the pointer to the malloc-ed object ends up stored in ptr, and ptr has exactly one static type which exactly matches what malloc returns. The only behavior which is different is the size of the malloc-ed region.

Your logic can be reduced to a one-liner:

void *ptr = malloc(band == 1 ? sizeof (struct typeA) : sizeof (struct typeB));

Much more context is required to see whether this is a good approach with regard to the surrounding code, or whether something more disciplined an "OOP-like" would be better, or whether a simple switch between two separate pieces of code working on separate structures.

The actual object cannot be accessed as a typeA or typeB without additional casts, so it can be imagined that a lot of ugly code follows, such as

if (band == 1)
   do_this_A_version((struct typeA *) ptr)->foo, x);
else
   do_this_B_version((struct typeB *) ptr)->bar, y);

There are good ways to avoid this sort of thing, out of scope for this question.

Kaz
  • 55,781
  • 9
  • 100
  • 149
1

[ EDIT ] Code snippet updated after the OP was edited.

This is second-guessing the actual question, but maybe the following is what you are after.

struct typeA
{
    int a;
    int x;
};
struct typeB
{
    int b;
    int a;
    int x;
    int z;
};

union { struct typeA *A; struct typeB *B; } ptr;
if(band == 1)
    ptr.A = malloc(sizeof(struct typeA));
else if(band == 2)
    ptr.B = malloc(sizeof(struct typeB));

/* ... later on ... */

if(band == 1)
    printf("A::a = %d", ptr.A->a); /* use ptr.A as a struct typeA pointer */
else if(band == 2)
    printf("B::a = %d", ptr.B->a); /* use ptr.B as a struct typeB pointer */
dxiv
  • 16,984
  • 2
  • 27
  • 49