Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this code guaranteed by the C standard?

Tags:

c

struct

I have read that if you declare two structs like this:

struct Node {
   int a, b, c;
};

struct DerivedNode {
   struct Node base;
   int d, e, f;
};

Then you can use pointers to them like this:

struct DerivedNode myDerivedNode; 
struct Node *regularNode = (struct Node *) &myDerivedNode;

regularNode->a = 3;

In other words, the address offsets for a, b, c are the same within struct Node and struct DerivedNode. So you can get a kind of polymorphism out of it, where you can pass in a forcibly (struct Node *)-cast DerivedNode pointer wherever a Node pointer would normally be taken.

My question is whether this behavior is guaranteed. I know there are some weird memory alignment issues and that the compiler sometimes reorders fields to achieve better packing in memory. Will the base field ever be located anywhere but the beginning of struct DerivedNode?

like image 339
Rag Avatar asked Oct 11 '11 11:10

Rag


3 Answers

This is guaranteed to work by the standard. Members in structs are layed out sequentially in the order you specify and the first member always appears at offset 0.

Relevant excerpts from the ANSI C standard:

A structure is a type consisting of a sequence of members, whose storage is allocated in an ordered sequence.

This states that the members are laid out sequentially.

There may be unnamed padding within a structure object, but not at its beginning.

The means that the first member is placed at offset 0.

Note: Standard excerpts taken from section 6.7.2.1 of ISO/IEC 9899:TC3 September 2007 draft.

like image 64
David Heffernan Avatar answered Sep 25 '22 00:09

David Heffernan


As David states this is guaranteed as long as base remains the first element in DerivedNode.

But generally this is bad practice. I can't figure out much circumstances where you can't say

struct Node *regularNode = &myDerivNode.base;

which is much clearer and less error prone in case you modify your structures later on.

like image 25
Jens Gustedt Avatar answered Sep 23 '22 00:09

Jens Gustedt


This won't answer your question, but one who cares to write standard ANSI (ISO) C could compile his code with gcc -pedantic or -pedantic-errors. Those options should raise compiling warnings/errors on non-standard lines of code.

Note that this is not 100% effective, from man gcc:

[-pedantic] finds some non-ISO practices, but not all---only those for which ISO C requires a diagnostic, and some others for which diagnostics have been added.

like image 42
etuardu Avatar answered Sep 24 '22 00:09

etuardu