简体   繁体   English

具有前向声明的C结构typedef

[英]C Structure typedef with Forward Declarations

I was using typedef for structures everywhere in my application. 我在应用程序中的所有地方都在使用typedef进行结构化。 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. 我注意到我需要转发声明Object和Klass。 Well, to my surprise, I couldn't forward declare Object, or Klass. 好吧,令我惊讶的是,我无法转发声明Object或Klass。 This is because, as you can see in the Object and Klass structure, I'm using a typedef of Object and Klass. 这是因为,正如您在Object and Klass结构中所看到的,我使用的是Object and Klass的typedef。

//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. 首先,使用typedef很棒。 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: 所以我决定只在Klass.h头文件中输入typedef Object:

typedef struct Object Object_t;

Well when all header files are included into my Main.c file, it complians: 好吧,当所有头文件都包含在我的Main.c文件中时,它就会符合以下条件:

Object.h:5: error: redefinition of typedef 'Object_t'

So, I then decided to just drop all struct typedefs and explicity declare my structures. 因此,然后我决定只删除所有struct typedef,并显式声明我的结构。

Is there a way to typedef a structure and forward declare in another file without explicitly using struct Object? 有没有一种方法可以在没有显式使用struct Object的情况下对结构进行typedef定义并在另一个文件中转发声明?

I want to keep structure typedefs inside the header file where the structure is declared. 我想将结构typedefs保留在声明结构的头文件中。 If I have to group all typedefs inside one header file then I would rather not use typedef at all. 如果我必须将所有typedef分组到一个头文件中,那么我宁愿完全不使用typedef。 Anyways, thanks for your time. 无论如何,谢谢您的时间。

You are missing the struct keyword. 您缺少struct关键字。 It should be 它应该是

typedef struct Object Object_t;

Forward declaration in that way (but see below) should always be possible. 以这种方式进行前向声明(但请参见下文)应该总是可能的。 This forward declares the typedef identifier and the struct tag at the same time. 此转发同时声明typedef标识符和struct标记。

Just put such forward declarations of all your struct before the real declarations. 只需将所有 struct此类前向声明​​放在实际声明之前即可。 As long as you only use pointers to these struct inside the declarations everything should be fine, then. 只要您在声明中仅使用指向这些struct指针,那么一切就可以了。

nitpick: names with a _t are reserved by POSIX. nitpick:带_t名称由POSIX保留。 This means that you should avoid it, because some day on some platform there could be a Object_t that is predefined and will conflict with your type. 这意味着您应该避免使用它,因为某天在某个平台上可能会有一个预定义的Object_t ,并且将与您的类型冲突。

I personally prefer the following convention 我个人更喜欢以下约定

typedef struct Object Object;

so the word Object, with or without struct , always refers to the same. 因此,带有或不带有struct Object一词总是指相同的东西。

Remember that a typedef is just an alternative name for a type. 请记住, typedef只是类型的替代名称。 There's a reason the Linux kernel doesn't use typedef s for structure types, and you're demonstrating it. Linux内核不对结构类型使用typedef是有原因的,您正在演示它。

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. 我注意到C11确实允许多次出现相同的typedef ,但是我假设您陷入了一个不支持该类型的旧编译器。

TL;DR 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). 即使Linux内核不使用typedef ,我通常使用typedef ,但是我大多避免使用相互引用的结构类型(我无法想到使用这种类型的任何代码)。 And, like Jens Gustedt notes in his answer , I almost invariably use the notations: 而且,就像詹斯·古斯特Jens Gustedt )在其回答中指出的那样,我几乎总是使用这些表示法:

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++; 在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). 当您定义struct SomeTagclass SomeTag ,名称SomeTag成为类型名称,而无需显式的typedef (尽管typedef除了显示出作者比C ++更具有C经验外,或透露源于C的代码没有其他危害。码)。

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). 同样,如果您包含任何POSIX标头(例如,当您不使用严格的标准C模式编译时,例如<stdio.h> ,则POSIX会为实现保留类型名_t 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: typedefs.h:

typedef struct Object Object_t;
typedef struct Klass  Klass_t;

klass.h 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. 之所以有效,是因为在使用两个类型名称Klass_tObject_t之前就已经声明了它们。

Use struct Object in prototype 在原型中使用struct Object

klass.h 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. 之所以可行,是因为(在广泛的限制内-基本上,如果类型是在文件作用域内定义的,而不是在函数内部定义的) struct Object始终引用相同的类型,而不管所有细节是否都已完全定义。

GCC 4.8.2 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. 在所有-std=c89-std=c99-std=c11 ,GCC 4.8.2接受复制的typedef ,如下面的代码所示。 It requires -std=c89 -pedantic or -std=c99 -pedantic to get errors about the repeated typedef s. 它需要-std=c89 -pedantic-std=c99 -pedantic才能获得有关重复的typedef的错误。

Even without the -pedantic option, GCC 4.5.2 rejects this code; 即使没有-pedantic选项,GCC 4.5.2 -pedantic拒绝此代码; however, GCC 4.6.0 and later versions accept it without the -pedantic option. 但是,GCC 4.6.0和更高版本接受不带-pedantic选项的功能。

klass.h 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(以及哪个C编译器)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM