在C和C++中typedef和结构体名称空间中,其中一个评论似乎暗示暴露某些struct foo
比沿用typedef更可取...
typedef struct foo foo;
...然后在整个API中使用foo
而不是struct foo
。
这种后一种方式有什么缺点吗?
在C和C++中typedef和结构体名称空间中,其中一个评论似乎暗示暴露某些struct foo
比沿用typedef更可取...
typedef struct foo foo;
...然后在整个API中使用foo
而不是struct foo
。
这种后一种方式有什么缺点吗?
foo
是一个结构体,而不是某种内置类型的别名。typedef struct foo foo;
可以用于不透明指针,而且也是合法的C++语法;虽然如果你不使用typedef,你可以声明没有显式前向声明struct foo;
的指针变量,但你仍应该加一个:否则,如果结构体的第一次出现是在函数声明中,那么类型将具有原型作用域,这很少是想要的--虽然它实际上并不会有害,因为类型兼容规则,但编译器可能会发出警告... - Christophvoid foo(struct bar *);void foo(struct bar *);
将在没有前向声明的情况下无法编译,因为两个 struct bar
类型将不兼容... - Christophtypedef name name
的方式声明不透明类型,并使用指向该不透明类型的指针。也许他们假设不透明类型必须始终是 typedef name* name
或类似的形式。 - Lundin若要了解另一种但完全可行的观点,请阅读Linux内核编码风格指南。
typedef
,只要它与相同类型别名即可:
ISO/IEC 9899:2011 §6.7 Declarations
语义
¶5 声明指定一组标识符的解释和属性。标识符的定义是该标识符的声明:
— 对于对象,导致为该对象保留存储空间;
— 对于函数,包括函数体;119)
— 对于枚举常量,是标识符的(唯一)声明;
— 对于typedef名称,是标识符的第一个(或唯一)声明。
与不可能在C99中相比:
语义
¶5 声明指定了一组标识符的解释和属性。对于一个标识符的定义是指对该标识符的声明:
— 对于一个对象,会导致为该对象保留存储空间;
— 对于一个函数,包括函数体;98)
— 对于一个枚举常量或typedef名称,是该标识符的(唯一)声明。
只要您保持一致(但仅当您在每个相关平台上都有足够兼容的C2011编译器时),这将简化类型定义的创建。
这更多或少是一种口味问题。
仅通过声明一个struct
标签,该名称也可用于在同一作用域中的变量、函数或枚举器(甚至另一种类型,如果您喜欢混淆)中使用;用户必须编写单词struct
,这使得他们的代码更加明确。
通过还声明类型名称,您允许人们不想键入struct
时可以不用键入。
我在其他问题中的评论是指声明与结构标记相同名称的指针类型:
typedef struct foo * foo;
从风格上讲,这样做有点不愉快,因为它隐藏了它是一个指针的事实。在那个问题的背景下,这是由API定义的不透明类型,这可能还好,但在我看来,对于非不透明类型而言,这样做可能会相当无礼。它也破坏了与C++的兼容性。在那种语言中,这样的声明是无效的,因为struct foo
引入了foo
到当前命名空间中,而不是一个单独的标签空间,并防止在该命名空间中声明任何其他具有相同名称的类型。
typedef struct foo foo;
然后强制调用者使用指针符号来声明他们的对象 foo* obj;
。这本质上与在使用动态分配时强制调用者使用指针是一样的。你从来没有看到过这样的东西 typedef void* malloc_t; ... malloc_t obj = malloc(...);
,那么为什么不透明类型会有所不同呢? - Lundinmalloc
的调用者不可避免地最终会将其返回值用作指针,而并非所有函数都是如此。如果您的 API 返回某些内容,从调用者的角度来看它纯粹是一个不透明句柄,但出于实现原因,它实际上是一个地址更方便,那么在我看来,您不应该仅仅因为感觉指针是隐藏无关实现细节的通常准则的例外而使句柄类型成为指针类型。 - Steve Jessopmalloc
的返回值并不是不透明的;它是您请求的内存的指针。 - Mike SeymourFILE*
是一个指针类型,而文件描述符是整数类型,这符合您的规则,Lundin。但对于用户来说,fopen
返回的类型是被称为FILE*
还是只是FILE
并没有任何区别。如果我想要实现文件系统API,使得FILE*
的值实际上只是转换为FILE*
的fd(或其他特定于操作系统的句柄),其中FILE
是一个空结构体,那么我几乎可以这样做。调用者错误地认为它是一个地址的事实只是一个小障碍,但我不会以这种方式设计API。 - Steve Jessop只要遵循命名规范,你就没问题了。
typedef struct
{
//...
} t_mytype;
//...
t_mytype thing;
这样你就知道它是一个自定义类型了。至于它是否为结构体,只需使用显式名称而不是真正的t_mytype
typedef struct { .. } mytype
。 - GrahamS另一个反对typedef的论点是它很难编写干净的头文件。让我们看一个头文件的例子:
#ifndef HEADER_H
#define HEADER_H
#include "s.h"
void f(struct s *);
#endif
这个头文件可以改进。实际上,struct s
不需要被定义。因此,它可以写成:
#ifndef HEADER_H
#define HEADER_H
struct s;
void f(struct s *);
#endif
struct s
用作不透明结构,我们可以将"s.h"
保密。typedef
做同样的事情:#ifndef HEADER_H
#define HEADER_H
typedef struct s s_t;
void f(s_t *);
#endif
每个想要使用 s_t *
的头文件中都会出现 typedef struct s s_t
这一行代码,我不喜欢冗余的代码。有多种方法可以避免这种情况,但最简单的方法可能是摆脱 typedef。
foo
而不需要显式地使用typedef来代替struct foo
)。 - jtbandes