83

Can I inherit a structure in C? If yes, how?

Cajunluke
  • 3,103
  • 28
  • 28

12 Answers12

109

The closest you can get is the fairly common idiom:

typedef struct
{
    // base members

} Base;

typedef struct
{
    Base base;

    // derived members

} Derived;

As Derived starts with a copy of Base, you can do this:

Base *b = (Base *)d;

Where d is an instance of Derived. So they are kind of polymorphic. But having virtual methods is another challenge - to do that, you'd need to have the equivalent of a vtable pointer in Base, containing function pointers to functions that accept Base as their first argument (which you could name this).

By which point, you may as well use C++!

Ziezi
  • 6,375
  • 3
  • 39
  • 49
Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
  • 12
    Well, that's assuming a C++ compiler is available for your platform! –  Jul 13 '09 at 01:22
  • 6
    If a C compiler is available, then so is a C++ compiler - just use one that produces C as its output. – Daniel Earwicker Jul 13 '09 at 06:29
  • 2
    Phew.. you saved my life. I usually code in Java and when faced with the code similar to what you posted I thought it's a composition and was confused like hell when they cast it. – Surya Wijaya Madjid Sep 11 '12 at 11:57
  • 2
    You san see uses of that in complex real projects (like git). See [log-tree.c](https://github.com/git/git/blob/master/log-tree.c#L133). [struct tag](https://github.com/git/git/blob/master/tag.h#L8) "inherits" from [struct object](https://github.com/git/git/blob/master/object.h#L27) – albfan Apr 06 '13 at 23:36
  • I used to do the exact same thing. Problem is, if you implement the indirection of "methods", you'll get pretty bad readability, like `personDAO->getPersonById(personDAO, personDTO->getId(personDTO));`. Unfortunately, I don't see another way around. – Powerslave Apr 09 '15 at 09:15
  • 1
    Excuse me, how is this ```Base *b = (Base *)d;``` possible. One is a ```Derived*``` and the other is a ```Base*```. Since ```Derived``` would have more members, the pointers are different. How can you do that cast? – Doga Oruc Sep 10 '20 at 12:35
  • @DogaOruc C is so low-level that the pointer is just that, a pointer. It has no type. As the answer says, if a pointer location starts with the contents of `Base`, it can sort of be used as a `Base`. In this case, the memory location of a `Derived` begins exactly the same as the memory location of a `Base`. (There may be errors in this comment, this is my interpretation.) – entonio Oct 21 '20 at 23:05
  • 1
    @entonio *C is so low-level that the pointer is just that, a pointer. It has no type.* That is [absolutely not true](https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule). And [common initial sequences are restricted to members of the same `union`](https://port70.net/~nsz/c/c11/n1570.html#6.5.2.3p6). This answer is wrong. "As Derived starts with a copy of Base, you can do this: `Base *b = (Base *)d;`" No, you can't safely do that. See https://stackoverflow.com/questions/34616086/union-punning-structs-w-common-initial-sequence-why-does-c-99-but-not – Andrew Henle Jun 28 '21 at 12:25
49

C has no explicit concept of inheritance, unlike C++. However, you can reuse a structure in another structure:

typedef struct {
    char name[NAMESIZE];
    char sex;
} Person;

typedef struct {
    Person person;
    char job[JOBSIZE];
} Employee;

typedef struct {
    Person person;
    char booktitle[TITLESIZE];
} LiteraryCharacter;
  • 1
    As far as I know, you can have a struct/class member inside another in C++ as well. – Tyler Millican Jul 11 '09 at 18:36
  • 71
    C says that no padding appears before the first member of a struct. So you can in fact (and are allowed) cast LiteraryCharacter* to Person*, and treat it as a person. +1 – Johannes Schaub - litb Jul 12 '09 at 01:06
  • 5
    @JohannesSchaub-litb your comment was a better explanation than the answer itself :) – Greg Feb 15 '12 at 11:49
  • 2
    It is important to note that you must only pass these types be reference. You can't copy them into a Person object or it will splice. – Kevin Cox Mar 16 '14 at 17:35
42

I like and used the idea of Typesafe inheritance in C.

For example:

struct Animal
{
    int weight;
};

struct Felidae
{
    union {
      struct Animal animal;
    } base;
    int furLength;
};

struct Leopard
{
    union {
      struct Animal animal;
      struct Felidae felidae;
    } base;

    int dotCounter;
};

Usage:

struct Leopard leopard;
leopard.base.animal.weight = 44;
leopard.base.felidae.furLength = 2;
leopard.dotCounter = 99;
Martin
  • 10,738
  • 14
  • 59
  • 67
  • I never thought of this. And if you make the union anonymous it is quite tidy. However the downside is that you need to list all parents in order to avoid nested variables. – Kevin Cox Mar 16 '14 at 17:34
  • 2
    Interesting approach. However, as soon as you type `leopard.base`, the whole point of inheritance/polymorphism is eliminated. – Powerslave Apr 09 '15 at 09:21
  • Don' you get a diamond inheritance problem here? `leopard.base.felidae.base.animal.weight` and `leopard.base.animal.weight`? – Alexander Torstling Feb 15 '16 at 23:01
  • 4
    @Alexander Torstling no, you won't. `leopard.base.felidae.base.animal.weight` is just another name for `leopard.base.animal.weight` - it is the same thing at the same place in memory. – Martin Feb 21 '16 at 21:30
  • This looks great. Thanks. – Petr Skocik Dec 15 '16 at 21:16
  • I'd argue that in some cases, you don't want polymorphic behavior, just the inherited abilities. However, you could still get polymorphism as every thing based on Animal should have a .base.animal namespace, although, it's a bit contrived. – Kurt E. Clothier Feb 05 '18 at 20:21
  • You'll run into problems if someone decides to cast the struct into a pointer of its initial member though, which C claims to be well-defined. And it would, if we use the correct type for the initial member. But `*(struct Animal*)&leopard`... oops, the first member is not a `struct Animal` but a union. And now we end up deep into the dark woods were language lawyers stalk and dangerous beasts lurk, such as strict aliasing, the common initial sequence rule or composite type rules. That's not a safe place to be. Maybe cook up something with _Generic or typeof instead. – Lundin Feb 09 '23 at 14:47
10

If your compiler supports anonymous structs, you can do this:

typedef struct Base
{
    // base members
} Base_t;

typedef struct
{
   struct Base;  //anonymous struct

   // derived members

} Derived_t;

This way, base stuct members can be acessed directly, which is nicer.

user836773
  • 335
  • 3
  • 5
  • 6
    The suffix `_t` is reserved in POSIX. Do whatever you want, just be advised that you're likely to run into conflicts if writing your code for a POSIX system (e.g. Linux) or someone eventually wants to port your code to a POSIX system. – L0j1k Feb 01 '14 at 08:09
  • 14
    This doesn't actually work in standard C (not even C11). – Chase Mar 10 '14 at 23:57
  • 1
    @Chase It does work, at least with GCC v12.2.0. Tried it with `gcc -std=c11 -pedantic -Wall -Wextra main.c -o test` and it worked fine. – Zakk May 23 '23 at 13:34
  • @Zakk it _does_ work but it isn't in the standard. See [this](https://stackoverflow.com/a/14248127) answer. In short, it's a Microsoft extension and you should be passing `-fms-extensions` to your compiler, but modern GCC seems to enable it automatically. – Sun of A beach Jul 31 '23 at 14:54
9

If you want to use some gcc magic (that I would assume would work with Microsoft's C compiler) you can do something like:


struct A
{
   int member1;
};

struct B
{
   struct A;
   int member2;
}

With gcc you can compile this with -fms-extensions (Allows for unnamed struct members like Microsofts compiler does). This is similar to the solution given by Daniel Earwicker except that it allows you to access memeber1 on a struct B instance. i.e B.member1 instead of B.A.member1.

This is probably not the most portable approach and will not work if using a C++ compiler (different language semantics mean that it is redeclaring/defining struct A instead of instantiating it).

If however you live in the gcc/C land only it will work and do exactly what you want.

Matt
  • 1,841
  • 2
  • 25
  • 30
  • Isn't this composition? – Sumit Gera Sep 20 '13 at 20:08
  • 1
    Nope, it is proper inheritance. Assuming you have a struct of type struct B named b, b.member1 will compile and work as you would expect. Composition would be something like b.base.member1. GCC performs this magic for you. It actually has the definition of struct B as two integers in this case. – Matt Sep 21 '13 at 01:11
  • Is this possible only in C, not in C++? If no, then please visit [This](http://www.learncpp.com/cpp-tutorial/102-composition/). – Sumit Gera Sep 21 '13 at 08:53
  • It is only C. The syntax is illegal in C++. Although C++ has proper inheritance of structs just like classes. – Matt Sep 21 '13 at 12:55
  • Now I am feeling much better! – Sumit Gera Sep 21 '13 at 15:44
  • Is that standard C or just GCC? – Zorgatone Nov 12 '15 at 19:12
  • It might be standardized in c11 I am not sure. It should work in msvc gcc and clang though – Matt Nov 12 '15 at 23:08
  • Describing this as "GCC magic" that might work in MSVC when it's copied from MSVC and requires the `-fms-extensions` flag to enable is very odd – Michael Mrozek May 26 '20 at 20:20
  • Hello, I can't make this work in GCC ([compiler I used](https://www.onlinegdb.com/online_c_compiler)) even with `-std=c11` flag, can I ask what flags did you use? – WENDYN Jul 23 '21 at 22:54
4

You can do the above mentioned

typedef struct
{
    // base members

} Base;

typedef struct
{
    Base base;

    // derived members

} Derived;

But if you want to avoid pointer casting, you can use pointers to a union of Base and Derived.

Ziezi
  • 6,375
  • 3
  • 39
  • 49
MighMoS
  • 1,228
  • 8
  • 18
4

A slight variation to the answer of anon (and others' similar). For one level deep inheritance one can do the following:

#define BASEFIELDS              \
    char name[NAMESIZE];        \
    char sex

typedef struct {
    BASEFIELDS;
} Person;

typedef struct {
    BASEFIELDS;
    char job[JOBSIZE];
} Employee;

typedef struct {
    BASEFIELDS;
    Employee *subordinate;
} Manager;

This way the functions accepting pointer to Person, will accept pointer to Employee or Manager (with casting), same as in other answers, but in this case the initialisation will be natural as well:

Employee e = {
    .name = "...";
    ...
};

vs

# as in anon's answer
Employee e = {
    .person.name = "...";
    ...
};

I believe this is how some popular projects do that (eg. libuv)

UPDATE: also there are some good examples of similar (but not the same) concept in libsdl events implementation using structs and unions.

Alex
  • 948
  • 1
  • 13
  • 17
  • 2
    This has tricky aliasing consequences. As far as I understand, you can only recast across the inheritance chain as long as the structs are in dynamic memory. (See https://gustedt.wordpress.com/2016/08/17/effective-types-and-aliasing/) Without dynamic memory, the union approach is probably better. – Petr Skocik Dec 15 '16 at 21:10
  • @PSkocik Obviously care should be taken when playing with things like this as a lot of things can go wrong. I do believe the example will work for pointers to stack variables as well though. With that being said I do agree that when it comes to casting in scenarios like this one the Union is always a better solution. – Alex Dec 15 '16 at 21:44
  • It doesn't matter how the objects are allocated (automatic, static, dynamic). One example of potential problems is that different objects may have different amount of padding between `name` and `sex` fields, but there are others possibilities. The main issue is that there are no guarantees that this works, making this code unreliable, and thus unusable. – user694733 Dec 16 '16 at 07:37
  • 1
    @user694733 I disagree with the padding statement. We only care about the BASEFIELDS and padding for those is deterministic on every given platform, provided they are always placed at the beginning of the struct. Obviously in real life scenario the BASEFIELDS will need to contain the type identifier to ensure proper treatment of the fields and things will get messy pretty soon. I agree though that this sort of hackery isn't nice and if one needs C++ style inheritance - they should use C++. – Alex Dec 16 '16 at 10:50
  • CPython also use syntax like this for constructing python Types see https://docs.python.org/2/extending/newtypes.html, here PyObject_HEAD defines the common fields required for python types. – Glen Fletcher Mar 05 '17 at 01:30
4

This works compiling with -fms-extensions

Diagram image

main.c

#include "AbstractProduct.h"
#include "Book.h"
#include "Product.h"
#include "TravelGuide.h"

/***********************/

int main() {

    Product p = Product_new();  
    p.set_id(&p, 2);
    p.set_name(&p, "name2");
    p.set_description(&p, "description2");
    p.set_price(&p, 2000);  
    p.display(&p);

    TravelGuide tg = TravelGuide_new(); 
    tg.set_id(&tg, 1);
    tg.set_name(&tg, "name1");
    tg.set_description(&tg, "description1");        
    tg.set_price(&tg, 1000);
    tg.set_isbn(&tg, "isbn1");
    tg.set_author(&tg, "author1");
    tg.set_title(&tg, "title1");
    tg.set_country(&tg, "country1");
    tg.display(&tg);

}

AbstractProduct.c

#include "AbstractProduct.h"

/*-------------------------------*/

static void set_id(AbstractProduct *this, int id) {
    this->id = id;
}

/*-------------------------------*/

static void set_name(AbstractProduct *this, char *name) {
    strcpy(this->name, name);
}

/*-------------------------------*/

static void set_description(AbstractProduct *this, char *description) {
    strcpy(this->description, description);
}

/*-------------------------------*/

static int get_id(AbstractProduct *this) {
    return this->id;    
}

/*-------------------------------*/

static char *get_name(AbstractProduct *this) {
    return this->name;  
}

/*-------------------------------*/

static char *get_description(AbstractProduct *this) {
    return this->description;   
}

/*-------------------------------*/

static void display(AbstractProduct *this) {

    printf("-AbstractProduct- \n"); 
    printf("id: %d\n", this->get_id(this)); 
    printf("name: %s\n", this->get_name(this)); 
    printf("description: %s\n", this->get_description(this));   
    printf("\n");
}

/*-------------------------------*/

void AbstractProduct_init(AbstractProduct *obj) {

    obj->set_id = set_id;
    obj->set_name = set_name;
    obj->set_description = set_description; 
    obj->get_id = get_id;
    obj->get_name = get_name;
    obj->get_description = get_description;
    obj->display = display;

}

/*-------------------------------*/

AbstractProduct AbstractProduct_new() {

    AbstractProduct aux;
    AbstractProduct_init(&aux);
    return aux;
}

AbstractProduct.h

#ifndef AbstractProduct_H
#define AbstractProduct_H

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/***********************/

typedef struct AbstractProduct{

    int id;
    char name[1000];
    char description[1000];

    void (*set_id)();
    void (*set_name)();
    void (*set_description)();  
    int (*get_id)();    
    char *(*get_name)();    
    char *(*get_description)(); 
    void (*display)();  

} AbstractProduct;

AbstractProduct AbstractProduct_new();
void AbstractProduct_init(AbstractProduct *obj);

#endif

Book.c

#include "Book.h"

/*-------------------------------*/

static void set_isbn(Book *this, char *isbn) {
    strcpy(this->isbn, isbn);
}

/*-------------------------------*/

static void set_author(Book *this, char *author) {
    strcpy(this->author, author);
}

/*-------------------------------*/

static void set_title(Book *this, char *title) {
    strcpy(this->title, title);
}

/*-------------------------------*/

static char *get_isbn(Book *this) {
    return this->isbn;  
}

/*-------------------------------*/

static char *get_author(Book *this) {
    return this->author;    
}

/*-------------------------------*/

static char *get_title(Book *this) {
    return this->title; 
}

/*-------------------------------*/

static void display(Book *this) {

    Product p = Product_new();
    p.display(this);    

    printf("-Book- \n");
    printf("isbn: %s\n", this->get_isbn(this)); 
    printf("author: %s\n", this->get_author(this)); 
    printf("title: %s\n", this->get_title(this));   
    printf("\n");
}

/*-------------------------------*/

void Book_init(Book *obj) {

    Product_init((Product*)obj);

    obj->set_isbn = set_isbn;
    obj->set_author = set_author;
    obj->set_title = set_title; 
    obj->get_isbn = get_isbn;
    obj->get_author = get_author;
    obj->get_title = get_title; 
    obj->display = display;
}
/*-------------------------------*/

Book Book_new() {

    Book aux;   
    Book_init(&aux);
    return aux;
}

Book.h

#ifndef Book_H
#define Book_H

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "Product.h"

/***********************/

typedef struct Book{

    Product;
    char isbn[1000];
    char author[1000];
    char title[1000];

    void (*set_isbn)();
    void (*set_author)();
    void (*set_title)();    

    char *(*get_isbn)();
    char *(*get_author)();
    char *(*get_title)();   
    // void (*display)();   


} Book;

Book Book_new();
void Book_init(Book *obj);

#endif

Product.c

#include "Product.h"

/*-------------------------------*/

static void set_price(Product *this, double price) {
    this->price = price;
}

/*-------------------------------*/

static double get_price(Product *this) {
    return this->price; 
}

/*-------------------------------*/

static void display(Product *this) {

    AbstractProduct p = AbstractProduct_new();
    p.display(this);    

    printf("-Product- \n"); 
    printf("price: %f\n", this->get_price(this));   
    printf("\n");
}

/*-------------------------------*/

void Product_init(Product *obj) {

    AbstractProduct_init((AbstractProduct*)obj);

    obj->set_price = set_price;
    obj->get_price = get_price; 
    obj->display = display;

}

/*-------------------------------*/

Product Product_new() {

    Product aux;    
    Product_init(&aux);
    return aux;
}

Product.h

#ifndef Product_H
#define Product_H

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "AbstractProduct.h"

/***********************/

typedef struct Product{

    AbstractProduct;
    double price;

    void (*set_price)();
    double (*get_price)();  
    // void (*display)();   

} Product;

Product Product_new();
void Product_init(Product *obj);

#endif

TravelGuide.c

#include "TravelGuide.h"

/*-------------------------------*/

static void set_country(TravelGuide *this, char *country) {
    strcpy(this->country, country);
}

/*-------------------------------*/

static char *get_country(TravelGuide *this) {
    return this->country;   
}

/*-------------------------------*/

static void display(TravelGuide *this) {

    Book b = Book_new();
    b.display(this);

    printf("-TravelGuide- \n"); 
    printf("country: %s\n", this->get_country(this));   
    printf("\n");
}

/*-------------------------------*/

void TravelGuide_init(TravelGuide *obj) {

    Book_init((Book*)obj);
    obj->set_country = set_country;
    obj->get_country = get_country;
    obj->f = obj->display;
    obj->display = display;

}

/*-------------------------------*/

TravelGuide TravelGuide_new() {

    TravelGuide aux;
    TravelGuide_init(&aux);
    return aux;
}

TravelGuide.h

#ifndef TravelGuide_H
#define TravelGuide_H

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "Book.h"

/***********************/

typedef struct TravelGuide{

    Book;
    char country[1000];
    void (*f)();

    void (*set_country)();
    char *(*get_country)();
    // void *(*display)();

} TravelGuide;

TravelGuide TravelGuide_new();
void TravelGuide_init(TravelGuide *obj);

#endif

Makefile

.PHONY: clean
define ANNOUNCE_BODY

    ***********************************************
    ************          start make **************
    ***********************************************
endef

all:
    $(info $(ANNOUNCE_BODY))    

    clear;
    if [ -f binary/main ]; then rm binary/main; fi;

# compiler 

    gcc $(INC) -c -fms-extensions main.c -o binary/main.o
    gcc $(INC) -c -fms-extensions AbstractProduct.c -o binary/AbstractProduct.o
    gcc $(INC) -c -fms-extensions Product.c -o binary/Product.o
    gcc $(INC) -c -fms-extensions Book.c -o binary/Book.o
    gcc $(INC) -c -fms-extensions TravelGuide.c -o binary/TravelGuide.o

# linker    

    gcc binary/main.o \
        binary/AbstractProduct.o \
        binary/Product.o \
        binary/Book.o \
        binary/TravelGuide.o \
        -o \
        binary/main
MoaLaiSkirulais
  • 125
  • 2
  • 8
  • nice for proof of concept, but if you have to go all this way to achieve OOP in C, you'd be better off with C++. IMHO C is not designed for OOP and if it really needs to be pure OOP (with a lot of effort spent to achieve that), you're using the wrong language. I for one wouldn't want to maintain such code. – Alex Apr 12 '16 at 16:53
  • Also, with this approach I think you're wasting some memory. Unlike C++ there will be additional sizeof(function pointer) * number of methods for each instance. In C++ sizeof(class) does not include the method pointers and in case there are virtual methods - contains 1 additional pointer to vtable. – Alex Jun 29 '16 at 20:57
  • Calling methods with "." makes it easier, and it doesn't pollute the name space. Separating new and init makes a lot of sense. It is the best answer, I don't know how it's so low. – Vélimir May 02 '22 at 21:58
  • Can you please shorten your answer? – MAChitgarha Aug 13 '23 at 10:20
-3

C is not an object-oriented language and hence has no inheritance.

txwikinger
  • 3,006
  • 1
  • 25
  • 33
-3

You can simulate it, but you can't really inherit.

luiscubal
  • 24,773
  • 9
  • 57
  • 83
  • 4
    what's _reality_? C++ is just a very simple runtime library for dispatching and a lot of compiler syntax to call it when needed. the original C++ compilers produced C code, after all. (and very readable C in fact) – Javier Jul 11 '09 at 19:51
-4

No, you cant. imo the best approach to OOP in C is using ADT.

Macarse
  • 91,829
  • 44
  • 175
  • 230
-6

No you cannot. C does not support the concept of inheritance.

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454