JUSEOK KO
JUSEOK KO

Reputation: 69

OOP and forward declaration of structure in C

I am studying C language and have recently learned how to write the OOP using C. Most part of it was not hard that much to understand for me except the name of structures type used to create new class.

My textbook used struct dummy_t for forward declaration and typedef struct {...} dummy_t for its definition. In my understanding, these are two different type because the former is struct dummy type and the later is struct type without a name tag but the sample code from the textbook worked well.

So I deliberately modified the sample code so that the difference in the names of structures will be much clearer. Below are the lines of code I tried.

//class.h

struct test_a;
struct test_a * test_init(void);
void test_print(struct test_a*);
//class.c
#include <stdio.h>
#include <stdlib.h>

typedef struct dummy{
    int x;
    int y;
} test_b;

test_b * test_init(void){
    test_b * temp = (test_b *) malloc(sizeof(test_b));
    temp->x = 10;
    temp->y = 11;
    return temp;
}

void test_print(test_b* obj){
    printf("x: %d, y: %d\n", obj->x, obj->y);
}
//main.c
#include "class.h"

int main(void){
    struct test_a * obj;
    obj = test_init();
    test_print(obj);

    return 0;
}
// It printed "x: 10, y: 10"

As you can see, I used struct test_a for forward declaration and typedef struct dummy {...} test_b for definition. I am wondering why I did not get the compile error and it worked.

Upvotes: 0

Views: 86

Answers (2)

4386427
4386427

Reputation: 44284

I am wondering why I did not get the compile error

When you compile main.c the compiler is told via a forward declaration from class.h that there is a function with the signature struct test_a * test_init(void);

The compiler can't do anything other than just trusting that, i.e. no errors, no warnings can be issued.

When you compile class.c there is no forward declaration but only the function definition, i.e. no errors, no warnings.

It's always a good idea to include the .h file into the corresponding .c file. Had you had a #include "class.h" in class.c the compiler would have been able to detect the mismatch.

..and it worked

What happens is:

  1. A pointer to test_b is assigned to a pointer to test_a variable

  2. The variable is then passed as argument to a function expecting a pointer to test_b

So once you use the pointer it is used as it was created (i.e. as pointer to test_b). In between you just stored in a variable of another pointer type.

Is that ok? No

Storing a pointer to one type in a object defined for another pointer type is not ok. It's undefined behavior. In this case it "just happened to work". In real life it will "just happen to work" on most systems because most systems use the same pointer layout for all types. But according to the C standard it's undefined behavior.

Upvotes: 1

koder
koder

Reputation: 2093

It 'worked' because you did not include class.h in class.c. So the compiler can't see the implementation does not match the declaration.

The proper way is (but without the typedef for clarity):

// class.h
struct test_a;
struct test_a* test_init(void);

//class.c
#include "class.h"
struct test_a {
    int x;
    int y;
};

struct test_a* test_init(void)
{
   ...
}

The struct test_a in the header file makes the name test_a known to the compiler as being a struct. But as it does not now what is in the struct you can only use pointers to such a struct.

The members are defined in the implementation file and can only be used there.

If you want to use a typedef:

// header
typedef struct test_a_struct test_a;
test_a* test_init(void);

//implementation
struct test_a_struct {
    int x;
    int y;
};
test_a* test_init(void)
{
    ...
}

Upvotes: 1

Related Questions