开发者

Typedef and Struct in C and H files

开发者 https://www.devze.com 2022-12-24 02:34 出处:网络
I\'ve been using the following code to create various struct, but only give people outside of the C file a pointer to it.(Yes, I know that they could potentially mess around with it, so it\'s not enti

I've been using the following code to create various struct, but only give people outside of the C file a pointer to it. (Yes, I know that they could potentially mess around with it, so it's not entirely like the private keyword in Java, but that's okay with me).

Anyway, I've been using the following code, and I looked at it today, and I'm really surprised that it's actually working, can anyone explain why this is?

In my C file, I create my struct, but don't give it a tag in the typedef namespace:

struct LABall {
    int x;
    int y;
    int radius;
    Vector velocity;
};

And in the H file, I put this:

typedef struct LABall* LABall;

I am obviously using #include "LABall.h" in the c file, but I am NOT using #include "开发者_运维百科LABall.c" in the header file, as that would defeat the whole purpose of a separate header file. So, why am I able to create a pointer to the LABall* struct in the H file when I haven't actually included it? Does it have something to do with the struct namespace working accross files, even when one file is in no way linked to another?

Thank you.


A common pattern for stuff like that is to have a foo.h file defining the API like

typedef struct _Foo Foo;

Foo *foo_new();
void foo_do_something(Foo *foo);

and a foo.c file providing an implementation for that API like

struct _Foo {
   int bar;
};

Foo *foo_new() {
    Foo *foo = malloc(sizeof(Foo));
    foo->bar = 0;
    return foo;
}

void foo_do_something(Foo *foo) {
    foo->bar++;
}

This hides all the memory layout and size of the struct in the implementation in foo.c, and the interface exposed via foo.h is completely independent of those internals: A caller.c which only does #include "foo.h" will only have to store a pointer to something, and pointers are always the same size:

#include "foo.h"

void bleh() {
    Foo *f = foo_new();
    foo_do_something(f);
}

Note: The ISO C standard section on reserved identifiers says that all identifiers beginning with an underscore are reserved. So typedef struct Foo Foo; is actually a better way to name things than typedef struct _Foo Foo;.

Note: I have left freeing the memory as an exercise to the reader. :-)

Of course, this means that the following file broken.c will NOT work:

#include "foo.h"

void broken() {
    Foo f;
    foo_do_something(&f);
}

as the memory size necessary for actually creating a variable of type Foo is not known in this file.


Since you're asking a precise reason as to "why" the language works this way, I'm assuming you want some precise references. If you find that pedant, just skip the notes...

It works because of two things:

  • All pointer to structure types have the same representation (note that it's not true of all pointer types, as far as standard C is concerned).[1] Hence, the compiler has enough information to generate proper code for all uses of your pointer-to-struct type.

  • The tag namespace (struct, enum, union) is indeed compatible accross all translation units.[2] Thus, the two structures (even though one is not completely defined, i.e. it lacks member declarations) are one and the same.

(BTW, #import is non-standard.)

[1] As per n1256 §6.2.5.27:

All pointers to structure types shall have the same representation and alignment requirements as each other. Pointers to other types need not have the same representation or alignment requirements.

[2] As per n1256 §6.2.7.1:

two structure, union, or enumerated types declared in separate translation units are compatible if their tags and members satisfy the following requirements: If one is declared with a tag, the other shall be declared with the same tag. If both are complete types, then the following additional requirements apply: [does not concern us].


In

typedef struct A* B;

since all pointers' interfaces are the same, knowing that B means a pointer to a struct A contains enough information already. The actual implementation of A is irrelevant (this technique is called "opaque pointer".)

(BTW, better rename one of the LABall's. It's confusing that the same name is used for incompatible types.)

0

精彩评论

暂无评论...
验证码 换一张
取 消