C语言结构的typedef与前向声明 [英] C Structure typedef with Forward Declarations
问题描述
我使用的typedef结构到处都在我的应用程序。然后我开始重构为几个
头文件时,它开始变得沉闷。我发现我需要转发申报对象,克拉斯。
好了,让我吃惊,我不能向前申报对象,或克拉斯。这是因为,你可以在看
对象和克拉斯结构,我使用的对象,克拉斯的一个typedef。
// Klass.htypedef结构克拉斯Klass_t;结构克拉斯
{
无效(* _initialize)(Object_t *的对象,Klass_t *克拉斯);
};//Object.htypedef结构对象Object_t;Struct对象
{
Klass_t * _klass;
};
首先,使用typedef的是伟大的。但是,试图转发申报对象:
结构Object_t;
不工作,我需要通过函数声明为重写:
无效(* _initialize)(结构Object_t *的对象,Klass_t *克拉斯);
所以我决定只是Klass.h头文件中的typedef对象:
typedef结构对象Object_t;
那么当所有头文件纳入我的main.c文件,它complians:
Object.h:5:错误的:typedef重新定义Object_t
所以,我于是决定刚落所有结构typedef和明确地宣布我的结构。
有没有办法来的typedef结构和前瞻性声明在另一个文件中没有明确使用结构对象?
我要保持头文件,其中内结构的typedef
结构声明。如果我要组一个头文件中的所有类型定义那我宁可不使用typedef的。不管怎么说,感谢您的时间。
请记住,的typedef
仅仅是一个类型的替代名称。还有一个原因,Linux内核不使用的typedef
S代表结构类型,和你展示它。
有一对夫妇在你的问题的方式。我注意到,C11确实允许同一的typedef
多次出现,但我假设你坚持使用旧的编译器不支持这一点。
TL; DR
尽管Linux内核不使用的typedef
S,我通常的做的使用的typedef
S,但我主要是避免相互引用的结构类型(在这里我用了这样一个类型,我想不出任何code的)。而且,像延Gustedt 音符在他的回答,我几乎总是使用符号:
typedef结构SomeTag SomeTag;
这样的类型名称和结构变量是相同的(他们在不同的命名空间)。此操作是不是在C ++必要;当你定义结构SomeTag
或类SomeTag
,名称 SomeTag
成为一个类型名称,而不需要一个明确的的typedef
(比其显露虽然的typedef
做其他任何伤害笔者更是在经历温度比C ++或code起源于C code)。
我也观察到,以下划线开头的名字最好为保留用于执行处理。规则是比稍微复杂一些,但在运行盗用您的名字实施的风险 - 和是其权利范围内抢尽你的名字 - 当您使用以下划线开头的名字,所以不要。同样,POSIX保留键入结尾的名称 _t
的实施,如果您有任何POSIX头文件(如< stdio.h中>
当你不严格的标准只有C模式下编译)。避免创建这样的名字;他们迟早会伤害你。 (我没有固定低于code应对任何这些问题:买者自负的)。
在下面的code片段,我主要是忽略了code是prevents多个包含(但你应该在你的code)。
附加标题
typedefs.h:
typedef结构对象Object_t;
typedef结构克拉斯Klass_t;
klass.h
的#includetypedefs.h结构克拉斯
{
无效(* _initialize)(Object_t *的对象,Klass_t *克拉斯);
};
object.h
的#includetypedefs.hStruct对象
{
Klass_t * _klass;
};
这工作,因为这两个类型的名称 Klass_t
和 Object_t
正在使用前声明。
使用结构对象
原型
klass.h
typedef结构克拉斯Klass_t;
Struct对象;结构克拉斯
{
无效(* _initialize)(Struct对象*的对象,Klass_t *克拉斯);
};
或者,一致性,它甚至可能是:
无效(* _initialize)(Struct对象*的对象,结构克拉斯*克拉斯);
object.h
的#includeklass.hStruct对象
{
Klass_t * _klass;
};
这工作,因为(广泛的范围内 - 基本上,如果类型在文件范围内定义的,而不是一个函数内部)结构对象
总是指同一类型,无论是否所有的细节都完全没有定义。
GCC 4.8.2
在所有 -std = C89
, -std = C99
和 -std = C11
,GCC 4.8.2接受复制的typedef
S,如下面的code。它要求 -std = C89 -pedantic
或 -std = C99 -pedantic
来获取有关重复<$ C错误$ C>的typedef 秒。
即使没有 -pedantic
选项,GCC 4.5.2拒绝该code;然而,GCC 4.6.0及以后的版本接受它,而不 -pedantic
选项。
klass.h
的#ifndef KLASS_H_INCLUDED
#定义KLASS_H_INCLUDEDtypedef结构克拉斯Klass_t;
typedef结构对象Object_t;结构克拉斯
{
无效(* _initialize)(Object_t *的对象,Klass_t *克拉斯);
};#ENDIF / * * KLASS_H_INCLUDED /
object.h
的#ifndef OBJECT_H_INCLUDED
#定义OBJECT_H_INCLUDEDtypedef结构克拉斯Klass_t;
typedef结构对象Object_t;Struct对象
{
Klass_t *克拉斯;
};#ENDIF / * * OBJECT_H_INCLUDED /
consumer.c
的#includeklass.h
#包括object.hKlass_t K表;
反对;
您将必须决定是否这就是你愿意把你的code危险 - 是多么重要的便携性,以及它的C版本(和C编译器),它必须得到移植。 p>
I was using typedef for structures everywhere in my application. I then started to refactor into several header files when it started to get clunky. I noticed I needed to forward declare Object, and Klass. Well, to my surprise, I couldn't forward declare Object, or Klass. This is because, as you can see in the Object and Klass structure, I'm using a typedef of Object and Klass.
//Klass.h
typedef struct Klass Klass_t;
struct Klass
{
void (*_initialize)(Object_t* object, Klass_t* klass);
};
//Object.h
typedef struct Object Object_t;
struct Object
{
Klass_t* _klass;
};
At first, using typedef was great. But trying to forward declare Object:
struct Object_t;
Doesn't work as I would need to rewrite by function declarations as:
void (*_initialize)(struct Object_t* object, Klass_t* klass);
So I decided to just typedef Object inside the Klass.h header file:
typedef struct Object Object_t;
Well when all header files are included into my Main.c file, it complians:
Object.h:5: error: redefinition of typedef 'Object_t'
So, I then decided to just drop all struct typedefs and explicity declare my structures.
Is there a way to typedef a structure and forward declare in another file without explicitly using struct Object?
I want to keep structure typedefs inside the header file where the structure is declared. If I have to group all typedefs inside one header file then I would rather not use typedef at all. Anyways, thanks for your time.
Remember that a typedef
is just an alternative name for a type. There's a reason the Linux kernel doesn't use typedef
s for structure types, and you're demonstrating it.
There are a couple of ways around your problem. I note that C11 does allow multiple occurrences of the same typedef
, but I'm assuming you're stuck with an older compiler that does not support that.
TL;DR
Even though the Linux kernel doesn't use typedef
s, I usually do use typedef
s, but I mostly avoid mutually referential structure types (I can't think of any code where I used such a type). And, like Jens Gustedt notes in his answer, I almost invariably use the notations:
typedef struct SomeTag SomeTag;
so that the type name and the structure tag are the same (they're in different namespaces). This operation is not necessary in C++; when you define struct SomeTag
or class SomeTag
, the name SomeTag
becomes a type name without the need for an explicit typedef
(though a typedef
does no harm other than revealing that the author is more experienced in C than C++, or the code originated as C code).
I also observe that names starting with an underscore are best treated as 'reserved for the implementation'. The rules are a little more complex than that, but you run a risk of the implementation usurping your names — and being within its rights to usurp your names — when you use names starting with an underscore, so don't. Likewise, POSIX reserves type names ending _t
for the implementation if you include any POSIX headers (such as <stdio.h>
when you aren't compiling in strict Standard C only mode). Avoid creating such names; they'll hurt you sooner or later. (I've not fixed the code below to deal with either of these issues: caveat emptor!).
In the code fragments below, I'm mostly ignoring the code that prevents multiple inclusions (but you should have it in your code).
Extra header
typedefs.h:
typedef struct Object Object_t;
typedef struct Klass Klass_t;
klass.h
#include "typedefs.h"
struct Klass
{
void (*_initialize)(Object_t *object, Klass_t *klass);
};
object.h
#include "typedefs.h"
struct Object
{
Klass_t *_klass;
};
This works because the two type names Klass_t
and Object_t
are declared before they're used.
Use struct Object
in prototype
klass.h
typedef struct Klass Klass_t;
struct Object;
struct Klass
{
void (*_initialize)(struct Object *object, Klass_t *klass);
};
Or, for consistency, it might even use:
void (*_initialize)(struct Object *object, struct Klass *klass);
object.h
#include "klass.h"
struct Object
{
Klass_t *_klass;
};
This works because (within broad limits — basically, if the types are defined at file scope, not inside a function) struct Object
always refers to the same type, regardless of whether all the details are fully defined yet.
GCC 4.8.2
Under all of -std=c89
, -std=c99
and -std=c11
, GCC 4.8.2 accepts replicated typedef
s, as in the code below. It requires -std=c89 -pedantic
or -std=c99 -pedantic
to get errors about the repeated typedef
s.
Even without the -pedantic
option, GCC 4.5.2 rejects this code; however, GCC 4.6.0 and later versions accept it without the -pedantic
option.
klass.h
#ifndef KLASS_H_INCLUDED
#define KLASS_H_INCLUDED
typedef struct Klass Klass_t;
typedef struct Object Object_t;
struct Klass
{
void (*_initialize)(Object_t *object, Klass_t *klass);
};
#endif /* KLASS_H_INCLUDED */
object.h
#ifndef OBJECT_H_INCLUDED
#define OBJECT_H_INCLUDED
typedef struct Klass Klass_t;
typedef struct Object Object_t;
struct Object
{
Klass_t *klass;
};
#endif /* OBJECT_H_INCLUDED */
consumer.c
#include "klass.h"
#include "object.h"
Klass_t k;
Object_t o;
You'll have to decide whether that's a risk you're willing to take for your code — how important is portability, and to which versions of C (and which C compilers) must it be portable.
这篇关于C语言结构的typedef与前向声明的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!