C语言结构的typedef与前向声明 [英] C Structure typedef with Forward Declarations

查看:1683
本文介绍了C语言结构的typedef与前向声明的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的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编译器),它必须得到移植

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 typedefs 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 typedefs, I usually do use typedefs, 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 typedefs, as in the code below. It requires -std=c89 -pedantic or -std=c99 -pedantic to get errors about the repeated typedefs.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆