C和H文件中的Typedef和Struct
我一直在使用下面的代码创建各种结构,但只给C文件之外的人一个指向它的指针。(是的,我知道他们可能会搞乱它,所以它不完全像Java中的private关键字,但我没关系) 不管怎样,我一直在使用下面的代码,今天我看了它,我真的很惊讶它居然能工作,有人能解释为什么会这样吗 在我的C文件中,我创建了我的结构,但没有在typedef名称空间中给它一个标记:C和H文件中的Typedef和Struct,c,struct,include,typedef,C,Struct,Include,Typedef,我一直在使用下面的代码创建各种结构,但只给C文件之外的人一个指向它的指针。(是的,我知道他们可能会搞乱它,所以它不完全像Java中的private关键字,但我没关系) 不管怎样,我一直在使用下面的代码,今天我看了它,我真的很惊讶它居然能工作,有人能解释为什么会这样吗 在我的C文件中,我创建了我的结构,但没有在typedef名称空间中给它一个标记: struct LABall { int x; int y; int radius; Vector velocity;
struct LABall {
int x;
int y;
int radius;
Vector velocity;
};
在H文件中,我写下:
typedef struct LABall* LABall;
很明显,我在c文件中使用了#include“LABall.h”,但在头文件中没有使用#include“LABall.c”,因为这会破坏单独头文件的全部用途。那么,为什么我能够在H文件中创建指向LABall*结构的指针,而实际上我没有包含它呢?即使一个文件与另一个文件没有任何链接,它是否与结构名称空间工作于多个文件有关
谢谢。在
typedef struct A* B;
由于所有指针的接口都是相同的,知道B意味着指向结构a的指针已经包含了足够的信息。A的实际实现是不相关的(这种技术称为“不透明指针”)
(顺便说一句,最好重命名一个
LABall
。对于不兼容的类型使用相同的名称是令人困惑的。)这样的东西的标准模式是使用一个foo.h
文件来定义API,如
typedef struct _Foo Foo;
Foo *foo_new();
void foo_do_something(Foo *foo);
以及一个foo.c
文件,该文件提供了类似API的实现
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++;
}
这隐藏了foo.c
中实现中结构的所有内存布局和大小,并且通过foo.h
公开的接口完全独立于这些内部:只有包含“foo.h”
的调用方.c
只需存储指向某个对象的指针,指针的大小始终相同:
#include "foo.h"
void bleh() {
Foo *f = foo_new();
foo_do_something(f);
}
注意:我将释放内存作为练习留给读者。:-)
当然,这意味着以下文件breaked.c
将无法运行:
#include "foo.h"
void broken() {
Foo f;
foo_do_something(&f);
}
由于实际创建类型为
Foo
的变量所需的内存大小在此文件中未知。既然您要问一个关于“为什么”语言以这种方式工作的确切原因,我假设您需要一些精确的引用。如果你找到了那个书呆子,跳过笔记
它之所以有效是因为两件事:
- 所有指向结构类型的指针都有相同的表示形式(请注意,就标准C而言,并非所有指针类型都是如此)。[1]因此,编译器有足够的信息为指向结构类型的指针的所有用途生成适当的代码
- 标记名称空间(struct、enum、union)确实与所有转换单元兼容。[2]因此,这两个结构(即使其中一个未完全定义,即缺少成员声明)是一个相同的结构
实际上,我认为只有一个存在于struct名称空间中,而其中一个存在于typedef名称空间中就足以区分它们了?哦,我会这么做的,谢谢。@莱夫:对编译器来说很好,但是用户会感到困惑,尤其是那些使用C++的人。这是不必要的——不透明指针在C语言中工作得很好,比如说,看看文件*就知道了——没有特殊的文件指针类型。好的,谢谢。我太习惯于贬低java了,我不喜欢隐藏一切都是指针的事实。”(顺便说一句,导入是非标准的。)“呜呜,我在混合java…我想键入#include.)我会解决的。