何时不需要使用typedef?

6
我遇到一些代码阅读。
typedef enum eEnum { c1, c2 } tagEnum;

typedef struct { int i; double d; } tagMyStruct;

我听说这些结构体起源于C语言。在C++中,你可以轻松地编写:
enum eEnum { c1, c2 };
struct MyStruct { int i; double d; };

这是真的吗?第一个版本什么时候需要?

5个回答

11

首先,在C和C++中,这两种声明都是合法的。然而,在C中,它们具有略微不同的语义(特别是之后引用结构的方式不同)。

理解的关键概念是,在C中,结构体存在于单独的命名空间中。 所有内置类型以及typedef存在于“默认”命名空间中。也就是说,当我输入int时,编译器只检查该“默认”命名空间。如果我像您的示例中一样输入“tagMyStruct”,编译器也仅检查此命名空间。但根据使用的声明类型,结构体可能不存在于那个命名空间中。

结构体不同,存在于单独的命名空间中。因此,如果我进行以下声明:

struct mystruct {};

我不能简单地将其称为mystruct。相反,我必须指定我想要的存在于struct命名空间中的mystruct:

void foo(struct mystruct bar); // Declare a function which takes a mystruct as its parameter

长期来看,这样会变得有些啰嗦和笨拙。相反,您可以将其 typedef 到默认命名空间中:

typedef struct mystruct mystruct; // From now on, 'mystruct' in the normal namespace is an alias for 'mystruct' in the struct namespace

现在,我的函数可以用简单直接的方式声明:

void foo(mystruct bar);

所以你的第一个示例只是将这两个步骤合并在一起:声明一个结构体,然后将一个别名放入常规命名空间中。当然,既然我们已经使用typedef,我们不需要“原始”名称,因此我们可以使结构体成为匿名的。所以在你的声明之后。

typedef struct { int i; double d; } tagMyStruct;

我们有一个没有名称的结构体,在默认命名空间中,它已被typedef为'tagMyStruct'。

这是C的处理方法。这两种声明方式都是有效的,但其中一种不会在“默认”命名空间中创建别名,因此每次引用该类型时都必须使用struct关键字。

在C ++中,独立的结构体命名空间不存在,所以它们意思相同。(但较短的版本更受欢迎)。

编辑:为了清楚起见,C并没有命名空间。不是通常意义下的。C只是将标识符放入两个预定义的命名空间之一。结构体(和枚举,据我所知)的名称被放置在其中一个命名空间中,而所有其他标识符则放置在另一个命名空间中。从技术上讲,这些是命名空间,因为它们是放置名称以避免冲突的单独的“容器”,但它们当然不是C ++ / C#中的命名空间。


所以 C 有命名空间! :) 谢谢,这解决了很多问题。 - xtofl
xtofl:不,实际上并没有。在C++的意义上是不存在的。结构体和内置类型存在于不同的命名空间中,但除此之外,没有其他的。你不能声明新的命名空间或者其他你期望的东西。所以这是一个特殊情况。把它看作“C有命名空间”可能是个坏主意 ;) - jalf
1
重点是,“命名空间”在C++将其用于指代用户定义的命名空间概念之前,它本身就是一个完全合适的词。同样地,你可以说C预处理器添加了另一个命名空间(用于宏),与C保留其名称的空间不同。 - Steve Jessop
我不会因此责怪C++。但是,命名空间确实比C++的实现具有更基本的含义。最终,它只是通过将它们存储在单独的“空间”中来区分相同名称的能力,在大多数语言中以某种形式存在这个概念。 - jalf
不要忘记联合标签。它们与结构体标签共享相同的命名空间 - 在同一作用域中不能同时有'union u {...}'和'struct u {...}'。 - Jonathan Leffler

10

在C语言中,如果你要写下如下代码:

struct MyStruct { int i; double d; };
无论何时你想引用该类型,你必须指定你正在谈论一个结构体:
struct MyStruct instanceOfMyStruct;
struct MyStruct *ptrToMyStruct;

使用typedef版本:

typedef struct { int i; double d; } tagMyStruct;

你只需要写:

tagMyStruct instanceOfMyStruct;
tagMyStruct *ptrToMyStruct;

使用typedef的版本的另一个重要优点是,您可以在C和C++中以相同的方式引用结构体,而在第二个版本中,在C中和C++中引用它们的方式不同。


“只需编写”版本是否应为“tagMyStruct…”而不是“MyStruct”?还是这个正确的?(这有些违反直觉…) - xtofl

1

这只是速记。

在第二个实例中,要声明结构体,您可以使用以下代码:

struct MyStruct a;

在第一种变体中,您可以这样做:
tagMyStruct a;

这也是C语言的速记法吗?还是你只指C++? - xtofl

0
第二种变体也在C中存在。
当你想要给你的类型命名时,你使用第一种变体,比如:
tagEnum myFunc(tagMyStruct * myArg);

0

第一种方法在C++中可能不太方便,因为您无法在头文件中进行前向声明。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接