0

In which condition is the statement in the title true? For which version of C and with which compiler options?

My question origins from Steve's statement: "You should notice now - that since our FooSubclass's first member is, in fact, a Foo struct - that any reference to a FooSubclass is also a valid reference to a Foo - meaning it can be used as such pretty much anywhere. " (MVC implemented in pure C) It's the first time I see anyone mention such a thing. The following code raises warnings hence my questioning about the validity of this statement.

#include <stdio.h>
#include <assert.h>

typedef struct Foo {
    int weight;
} Foo;

Foo foo_init(int weight) {
    Foo t;
    t.weight = weight;
    return t;
}

int foo_weight(const Foo *this) {
    return this->weight;
}

typedef struct Bar {
    Foo base;
    int size;
} Bar;

Bar bar_init(int weight, int size) {
    Bar w;
    w.base = foo_init(weight);
    w.size = size;
    return w;
}

int bar_weight(const Bar *this) {
    return foo_weight(this);
}

int bar_size(const Bar *this) {
    return this->size;
}

int main (int argc, char *argv[]) {
    Foo t = foo_init(22);
    Bar w = bar_init(20,14);

    assert(foo_weight(&t) == 22);
    assert(bar_weight(&w) == 20);
    assert(bar_size(&w) == 14);

    return 0;
}

Result:

> gcc main.c

main.c: In function 'bar_weight':
main.c:31:20: warning: passing argument 1 of 'foo_weight' from incompatible pointer type [-Wincompatible-pointer-types]
 return foo_weight(this);
                ^~~~
main.c:14:5: note: expected 'const Foo * {aka const struct Foo *}' but argument is of type 'const Bar * {aka const struct Bar *}'
 int foo_weight(const Foo *this) {
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
aganm
  • 1,245
  • 1
  • 11
  • 30
  • 1
    You need to use casting, but otherwise it's valid and guaranteed to work. – Some programmer dude Dec 24 '18 at 10:55
  • 1
    `bar_weight` should `return foo_weight(&this->base);` (no casting required) – David C. Rankin Dec 24 '18 at 11:19
  • Going from a pointer to a struct to a pointer to its first (or any other) member is trivial, no casting or special magic in the language standard are required. `&this->base` does the job. Going **back** from a pointer to the first member to a pointer to the entire struct is the dirty trick. It requires a cast, which relies on a special magic promise in the standard that the cast will work as expected. – n. m. could be an AI Dec 24 '18 at 11:38

1 Answers1

2

In which condition is the statement in the title true?

In no condition. The correct statement is "Bar's first member is a Foo struct, any reference to a Bar can be cast to a valid reference to a Foo".

This is documented in the C standard at 6.7.2.1p15:

6.7.2.1 Structure and union specifiers

5 A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a structure object, but not at its beginning.

And has been true since about the conception of C.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458