Extension of structure in C

I recently met a colleague's code that looked like this:

typedef struct A { int x; }A; typedef struct B { A a; int d; }B; void fn(){ B *b; ((A*)b)->x = 10; } 

His explanation was that since struct A was the first member of struct B , so b->x will be the same as b->ax , and provides better readability. It makes sense, but is it considered good practice? And will it work on different platforms? This currently works great on GCC.

+60
c struct
Mar 06 '14 at 8:10
source share
11 answers

Yes, it will work cross-platform, but that does not necessarily make it a good idea.

In accordance with ISO C (all citations from C11), 6.7.2.1 Structure and union specifiers /15 , before the first element of the structure

filling is not allowed,

In addition, 6.2.7 Compatible type and composite type states that:

Two types have a compatible type if their types are the same

and it is undeniable that types A and A-within-B identical.

This means that the memory, referring to the fields A , will be the same for types A and B , as well as the more reasonable b->ax , which is likely to be used if you have concerns about maintainability in the future.

And, although you usually have to worry about a strict type alias, I don't think this applies here. This is illegal for alias pointers, but the standard has special exceptions.

6.5 Expressions /7 contains some of these footnote exceptions:

The purpose of this list is to indicate the circumstances under which an object may or may not be smoothed.

The listed exceptions:

  • a type compatible with the effective type of the object ;
  • some other exceptions that we are not interested in here; and
  • an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union) .

This, in combination with the above rules for laying the structure, including the phrase:

A pointer to a structure object, properly transformed, points to its initial member

this example seems to be specifically allowed. Here we must remember that the type of expression ((A*)b) is A* , not B* . This makes the variables compatible for unlimited smoothing.

To my reading of the relevant parts of the standard, I was wrong before (a) but in this case I doubt it.

So, if you have a real need for this, everything will be fine, but I will document any restrictions in the code very close to the structures so as not to bite in the future.




(a) As my wife will tell you, often and without much clue :-)

+62
Mar 06 '14 at 8:17
source share

I will go on a limb and will be against @paxdiablo : I think this is a great idea, and this is very common in large production quality code.

This is basically the most obvious and enjoyable way to implement object-oriented data structures based on inheritance. C. Running a struct B declaration with an instance of struct A means that "B is a subclass of class A". The fact that the first member of the structure is guaranteed to be 0 bytes from the beginning of the structure is what makes it work safely, and, in my opinion, it is beautiful on the edge.

It is widely used and deployed in code based on the GObject library, such as the GTK + user interface toolkit and the GNOME desktop environment.

Of course, this requires you to β€œknow what you are doing,” but usually this always happens when implementing complex type relationships in C. :)

In the case of GObject and GTK +, there is a lot of support infrastructure and documentation to help with this: it's pretty hard to forget about it. This may mean that creating a new class is not something you do as fast as in C ++, but this might be expected, since C classes do not have built-in support.

+33
Mar 06 '14 at 9:23
source share

Anything that bypasses type checking should usually be avoided. This hack relies on the order of declarations, and neither the compiler nor this order can be applied by the compiler.

It should work cross-platform, but I do not think this is a good practice.

If you really have deep nested structures (perhaps you should wonder why, however), then you should use a temporary local variable to access the fields:

 A deep_a = e->dcba; deep_a.x = 10; deep_a.y = deep_a.x + 72; e->dcba = deep_a; 

Or, if you do not want to copy a along:

 A* deep_a = &(e->dcba); deep_a->x = 10; deep_a->y = deep_a->x + 72; 

This shows where a comes from, and does not require a throw.

Java and C # also regularly expose constructs such as "cba", I don’t understand what the problem is. If what you want to mimic is an object-oriented behavior, then you should consider using an object-oriented language (like C ++), since the "extension of structures" in what you suggest does not provide encapsulation and polymorphism during execution (although it can be argued that ((A *) b) is akin to "dynamic casting").

+12
Mar 06 '14 at 9:01
source share

This is a terrible idea. As soon as someone comes in and inserts another field in front of structure B, your program explodes. And what's wrong with bax ?

+11
Mar 06 '14 at 8:15
source share

I regret that I do not agree with all the other answers here, but this system does not comply with the C standard. It is unacceptable to have two pointers with different types that point to the same place at the same time, this is called aliasing and not allowed by strict anti-aliasing rules in C99 and many other standards. It was less ugly to do this in order to use the getter built-in functions, which then should not look neat. Or maybe this is work for the union? In particular, it is allowed to hold one of several types, however, there are many other disadvantages.

In short, such a dirty casting to create polymorphism is not allowed by most C standards just because it seems to work on your compiler, this does not mean that this is acceptable. See here for an explanation of why this is unacceptable and why compilers at high levels of optimization can violate code that does not comply with these rules http://en.wikipedia.org/wiki/Aliasing_%28computing%29#Conflicts_with_optimization

+7
Mar 06 '14 at 2:08
source share

Yes, that will work. And this is one of the basic principles of Object Oriented using C. See This Answer Object Orientation in C for more examples of extension (such as inheritance).

+5
Mar 06 '14 at 12:03
source share

This is completely legal and, in my opinion, quite elegant. For an example of this in production code, see GObject Docs :

Thanks to these simple conditions, you can determine the type of each instance of an object:

 B *b; b->parent.parent.g_class->g_type 

or, faster:

 B *b; ((GTypeInstance*)b)->g_class->g_type 

Personally, I think that unions are ugly and tend to lead to huge switch , which is a big part of what you tried to avoid by writing OO code. In this style, I write a significant part of the code. Typically, the first member of a struct contains pointers to functions that can be made to work like a vtable for the type in question.

+3
Mar 07 '14 at 7:53
source share

I see how it works, but I would not call this good practice. It depends on how the bytes of each data structure are stored in memory. Each time you throw one complex data structure into another (i.e. Struct), this is not a good idea, especially if the two structures do not have the same size.

+2
Mar 06 '14 at 8:17
source share

I think the OP and many commentators have locked up the idea that the code extends the structure.

This is not true.

This is an example of composition. Very useful. (Get rid of typedefs, here is a more descriptive example):

 struct person { char name[MAX_STRING + 1]; char address[MAX_STRING + 1]; } struct item { int x; }; struct accessory { int y; }; /* fixed size memory buffer. The Linux kernel is full of embedded structs like this */ struct order { struct person customer; struct item items[MAX_ITEMS]; struct accessory accessories[MAX_ACCESSORIES]; }; void fn(struct order *the_order){ memcpy(the_order->customer.name, DEFAULT_NAME, sizeof(DEFAULT_NAME)); } 

You have a fixed size buffer that is nicely split. He sure surpasses the gigantic structure of one level.

 struct double_order { struct order order; struct item extra_items[MAX_ITEMS]; struct accessory extra_accessories[MAX_ACCESSORIES]; }; 

So, now you have a second structure that can be processed (a-inheritance) in the same way as the first with an explicit cast.

 struct double_order d; fn((order *)&d); 

This preserves compatibility with code that was written to work with a smaller structure. Linux kernel ( http://lxr.free-electrons.com/source/include/linux/spi/spi.h (look at struct spi_device)) and the socket bsd library ( http://beej.us/guide/bgnet/ output / html / multipage / sockaddr_inman.html ) use this approach. In cases with the kernel and sockets, you have a structure that runs in both general and differentiated sections of code. Not all that differs from the use case for inheritance.

I would not suggest writing such structures just for readability.

+1
Mar 13
source share

I think Postgres does this in some of its codes. Not that it makes it a good idea, but it does talk about how widely accepted it is.

0
Mar 11 '14 at 19:39
source share

Perhaps you can use macros to implement this function, the need to reuse a function or field in a macro.

-one
Aug 6 '17 at 16:34 on
source share



All Articles