0

I have two defined structures, which both have one field in common. typedef struct entry entry;

typedef struct example {
   entry* list_e;
   int nb_elements;
   int nb_mllc;
} example;

typedef struct mystruct {

   entry* list_e;
   int nb_elements;
   int nb_mllc;
} mystruct;

Is is possible that I write a function such that either mystruct or example can be the type of one variable? In that function i am filling the list "entry", so I know I could do it by writing the method as

 void add_entry(entry** list_e, int* nb, int* nb_mllc){

        //fill the entries
        if (*nb == *nb_mllc) {
           //realloc list_e
        }
}

but however this looks like a dirty solution to me as then I will have to call the function like

     example* example; //malloc and null pointer checking left out

     add_entry(&example->list_e, &example->nb_elements, &example->nb_mllc);

and then also for mystruct, but I would rather have not splitted the variables of a structure as function arguments. Is it possible to make somehow example and mystruct the same type so I can then give the whole structure as an argument

3
  • Your types example and mystruct appear to be identical except for their tags. Why not just choose one and use it everywhere? Commented Dec 5, 2016 at 18:56
  • You said the structs shared only one field, but they appear to have the same exact structure. Was that a typo or something? Commented Dec 5, 2016 at 18:57
  • You have a heterogeneous linked list, is it true? How do you expect to recovery linked data without loosing its type? You're going to need to implement some kind of type introspection, right? Commented Dec 5, 2016 at 19:23

2 Answers 2

1

Is is possible that I write a function such that either mystruct or example can be the type of one variable?

No. Every function parameter has exactly one type. Even though your two structs have members of the same type, in the same order, with the same names, they are distinct, incompatible types as a result of having different struct tags. (The difference in typedefed names, on the other hand, is irrelevant for this purpose.) A function that accepts an argument of one of those types, or a pointer to that type, cannot accept an argument of the other type into the same parameter.

It is possible that someone will recommend casting a pointer to one of those types to be a pointer to the other type, so as to be able to pass it to your hypothetical function. Whereas in practice that may yield the desired result, it nevertheless violates C's strict aliasing rule, and therefore produces undefined behavior. That kind of behavior can get you into trouble with an aggressively-optimizing compiler.

You have at least three options:

Use the same structure

There is no reason apparent from your question why you need distinct types, as the two types in question differ only in their struct tags. If you simply choose one and use it everywhere, then the problem disappears. You can typedef multiple aliases for that type if you wish; it does not then affect your code's validity which of those you use in any particular place.

Embed the same structure

It may be that you have oversimplified your code by removing elements of your two types that actually differ. In that case, you could embed a common structure containing those values into the larger structure, like so:

struct list_data {
    entry* list_e;
    int nb_elements;
    int nb_mllc;
};

typedef struct example {
   struct list_data list_data; // must be first
   char *characters;
} example;

typedef struct mystruct {
   struct list_data list_data; // must be first
   int x, y, z;
} mystruct;

C guarantees that the representation of the first member of a structure will start at the first byte of the overall structure's representation. Therefore, you can (in principle) safely cast back and forth between a pointer to the first member and a pointer to the overall structure, as long as you're careful in the latter case to cast to the correct type.

Use a node type with discriminator and union

Casting is generally a sign of poor design. If you cannot use the same type everywhere, then it might be a better plan to turn the embedding idea inside out:

typedef struct example {
   char *characters;
} example;

typedef struct mystruct {
   int x, y, z;
} mystruct;

struct list_data {
    entry* list_e;
    int nb_elements;
    int nb_mllc;
    _Bool is_mystruct;
    union {
        example *example;
        mystruct *mystruct;
    };
};

Now you have a single type, struct list_data, that you can link together into lists via a single set of functions. That type can reference (or embed, if you prefer) either an example or a mystruct, with member is_mystruct disambiguating which is actually stored. Or if it will be evident from context which type is stored, then you can drop is_mystruct.

Sign up to request clarification or add additional context in comments.

3 Comments

thank you really much for the detailed description and explanations here. However i think i might did not really understand how the second example should work. So I have two structures with the first element being the list_data. So did you try to say, if i have a mystruct* struct; that I can cast it to (list_data) struct
I never used unions before but I know that they are defined that they can be either of the given variables. But shouldnt I then assign a tag to the union, in order to access it over my structure? And did you miss to add the list_data as structure variables or is this wanted in this case?
@malajedala, unions and structs follow the same rules with regard tags: they can be tagged, but they do not need to be. Additionally, in C2011, those that appear untagged inside other structs or unions can also be anonymous, as is the case in my example code. The union members are then accessed as if they were direct members of the outer struct. This is primarily a convenience feature, and if you prefer, you can give the union a name and access the members through it. On the other hand, giving it a tag does nothing for you.
0

Many implementations allow functions written in C to call, or be called by, functions written in other languages. Such implementations will typically specify in detail (in a document called the ABI--Application Binary Interface) how various data types are laid out in memory, how arguments are passed into functions, and how return values are passed out. With few if any exceptions (I know of none) the way in which information is exchanged is dependent purely upon the actual storage formats of the types involved, and not upon what those types are called. Among other things, struct tags will be irrelevant for such purposes.

On an implementation with a documented ABI, a call through a function pointer that is qualified volatile will essentially always(*) be treated in the fashion described by that ABI. If the ABI doesn't care about structure tags, an implementation won't care about them either when processing calls through volatile function pointers.

(*) An implementation could, in theory, read the volatile function pointer, check whether it matches a function it knows about, and only behave as described by the ABI in cases where the pointer doesn't match any known function. Such behavior would be weird, since an implementation would have no way of knowing what a volatile pointer would be likely to contain, but a "clever" implementation might do it anyway.

Although declaring a function pointer volatile may sometimes have a slight adverse effect on performance, and although such a qualifier wouldn't usually be necessary, it would reduce the likelihood of compilers optimizing calls to "expected" functions in ways inconsistent with the ABI.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.