87

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

2
  • 7
    There is no such thing as inheritance in C Commented Jul 11, 2009 at 18:30
  • 49
    ...Unless you implement it. Commented Mar 20, 2013 at 15:28

12 Answers 12

114

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++!

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

8 Comments

Well, that's assuming a C++ compiler is available for your platform!
If a C compiler is available, then so is a C++ compiler - just use one that produces C as its output.
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.
You san see uses of that in complex real projects (like git). See log-tree.c. struct tag "inherits" from struct object
@entonio C is so low-level that the pointer is just that, a pointer. It has no type. That is absolutely not true. And common initial sequences are restricted to members of the same union. 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 stackoverflow.com/questions/34616086/…
|
50

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;

4 Comments

As far as I know, you can have a struct/class member inside another in C++ as well.
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
@JohannesSchaub-litb your comment was a better explanation than the answer itself :)
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.
43

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;

7 Comments

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.
Interesting approach. However, as soon as you type leopard.base, the whole point of inheritance/polymorphism is eliminated.
Don' you get a diamond inheritance problem here? leopard.base.felidae.base.animal.weight and leopard.base.animal.weight?
@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.
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.
|
11

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.

4 Comments

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.
This doesn't actually work in standard C (not even C11).
@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 it does work but it isn't in the standard. See this 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.
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.

9 Comments

Isn't this composition?
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.
Is this possible only in C, not in C++? If no, then please visit This.
It is only C. The syntax is illegal in C++. Although C++ has proper inheritance of structs just like classes.
Now I am feeling much better!
|
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

4 Comments

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.
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.
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.
Can you please shorten your answer?
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.

5 Comments

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 gustedt.wordpress.com/2016/08/17/effective-types-and-aliasing) Without dynamic memory, the union approach is probably better.
@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.
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 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++.
CPython also use syntax like this for constructing python Types see docs.python.org/2/extending/newtypes.html, here PyObject_HEAD defines the common fields required for python types.
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.

Comments

-3

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

Comments

-3

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

1 Comment

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)
-4

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

1 Comment

You can, but yes, the ADT pattern is the best pattern in C for this type of work.
-6

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

1 Comment

doesn't support but doesn't get in the way either.

Your Answer

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