什么时候使用没有 typedef 的结构有意义?
Posted
技术标签:
【中文标题】什么时候使用没有 typedef 的结构有意义?【英文标题】:When does it make sense to use a struct without a typedef? 【发布时间】:2021-09-09 01:04:00 【问题描述】:C 中的结构声明了一个数据结构,它将不同的数据类型关联到一块连续的内存中。
Typedef 是一种创建用户定义数据类型名称的方法。这对许多应用程序都很有用,包括<stdint.h>
结构似乎只与 typedef 一起使用。似乎定义struct
的默认行为也应该定义typedef
。
我为什么要定义 struct
而不使用 typedef
?
【问题讨论】:
C++ 的设计者也有同样的感受——在 C++ 中,当你声明一个结构或一个类时,它实际上会自动声明 typedef。但是 C 仍然以旧的方式做事。如果你想要typedef,你必须自己声明它,如果你喜欢每次引用结构类型时都输入struct
,你可以。
我为什么要在不使用 typedef 的情况下定义 struct? 相反,你为什么要隐藏你正在处理 struct
的事实?
@AndrewHenle 我猜少打字
@EugeneSh。那不是优势。来自kernel.org/doc/html/v4.10/process/coding-style.html#typedefs 当您在源代码中看到vps_t a;
时,这是什么意思?相反,如果它显示struct virtual_container *a;
,您实际上可以分辨出a
是什么。
在 C 语言中有一种重要的情况,您需要使用 struct 标签,那就是您想在定义结构之前声明一个结构指针。你可以在定义struct foo
之前做struct foo *p;
,单独使用typedef是无法达到同样效果的。当您有一个包含指向自身的指针的结构或两个包含彼此指针的结构时,就会出现这种情况。我通常在前面声明 typedef:typedef struct foo foo_t;
然后你可以在结构定义中使用foo_t *p;
。
【参考方案1】:
typedef
不仅仅是节省一些击键 - 它是关于抽象出底层类型的实现细节。 IOW,如果您为 struct
类型提供 typedef 名称,您应该还提供完整的 API 用于设置和访问成员、格式化输出、分配、解除分配,等等。您正在向使用它的人隐藏该类型的“结构”特性。想想标准库中的 FILE
类型 - 这是一个 typedef 名称,通常用于某些特定于实现的结构。但是,您永远不会直接访问 FILE
类型的任何元素 - 您只需将 FILE *
对象传递给隐藏实现的各种函数(fprintf
、fread
、feof
、ferror
等)你。
如果您希望该类型的用户使用 .
或 ->
运算符显式访问成员,则不要创建 typedef 名称对于它 - 只需将其保留为 struct <em>whatever</em>
。否则你会创建一个“泄漏”的抽象,这会导致胃灼热。
指针类型的类似规则 - 不要将类型的“指针”隐藏在 typedef
后面,除非您也愿意创建一个完整的 API 来抽象出指针操作。
【讨论】:
【参考方案2】:你问:我为什么要定义结构而不使用 typedef?
这取决于“定义结构”是什么意思。并非 struct 的所有用途都是定义命名类型。
例如,您可以通过以下方式定义变量
struct
double a, b;
dbls = -1.0, 1.0;
那么 dbls.a 等就有意义了,但是没有命名类型。
类似地在你可能拥有的匿名工会中
struct ructT
union
struct int a; int b; ints;
struct float a; float b; floats;
;
;
这里定义了一个类型 struct ructT 但内部结构没有命名。
在每一种情况下,如果坚持使用 typedef,就会有更多的名称需要构思,也需要输入更多的代码。
【讨论】:
【参考方案3】:我从不对我的结构进行 typedef,以便可以更轻松地在其他标头中“extern”声明它们。
【讨论】:
【参考方案4】:这是个人喜好问题,可能会强加给从事同一项目的其他人作为会议。例如,毫不含糊的 Linux 内核编码风格指南discourages the introduction of new typedefs。
虽然我不一定同意该指南中的所有内容,其中一些似乎很愚蠢(例如 vps_t a;
示例可能是 virtual_container_t a;
:问题取决于为 typedef
选择的神秘名称多于typedef 的存在),在我的 TXR 语言项目中,这里有一些原始统计数据:
txr$ git grep '^typedef struct' '*/*.[ch]' '*.[ch]' | wc
25 91 839
txr$ git grep '^struct' '*/*.[ch]' '*.[ch]' | wc
135 528 4710
以struct
开头的代码行数是typedef struct
行数的5.4 倍!
C 的 union/tag 命名空间特性意味着您可以在与 struct foo
相同的范围内拥有一个名为 foo
的变量而不会发生冲突,这很有用。这个额外的命名空间使我们有机会不使用用户定义的类型名称污染常规标识符命名空间,从而提高卫生。
代价是我们必须在声明中输入struct foo
而不仅仅是foo
。
如果您有使用类/类型名称不会侵入词法变量命名空间的语言(如 Common Lisp)的经验,这将特别有意义。
不过,上面的代码库编译为 C++,因此有点麻烦。在 C++ 中,struct foo
将foo
定义为一个类型,可以在普通命名空间中引用。
另一个原因是有一些明确性。当我们看到这样的声明时:
struct foo x;
我们知道 x 是一个结构,而
foo x;
可以是任何东西;可能是typedef double foo
。有时我们会追求那种抽象。如果您想隐藏某事是如何实现的,请联系typedef
。但是,typedef
并没有提供完美的抽象。
另外,如果我们看到:
struct foo f;
struct bar b;
我们知道这些必然是不同的、不兼容的类型。我们不知道:
foo f;
bar b;
就我们所知,它们可能都是typedef
s,也可能是int
。
如果你 typedef
指针类型,但代码取消引用它们,它看起来很傻:
foo x = get_foo();
char *fname = x->name; /* what? */
该内核编码风格文档对上述内容有这样的说法:一般来说,指针或具有可以合理直接访问的元素的结构不应该是 typedef。
不将typedef
用于结构与使用标签命名空间的一些卫生有关,因为它与避免typedef
有关:保持代码明确,并保留typedef
用于我们实际上需要适当抽象的情况。
【讨论】:
【参考方案5】:结构似乎只与 typedef 一起使用。
这是一个错误的印象。结构经常在没有 typedef 的情况下使用,我个人更喜欢这样。例如,C 标准库和 POSIX 标准库扩展函数声明和使用了许多结构类型,但没有内置 typedef。
好像 定义结构的默认行为也应该定义 typedef。
在 C++ 中,它确实有效。
我为什么要定义结构而不使用 typedef?
你为什么要定义一个struct
和一个typedef
?通过struct
关键字及其标签使用(标记的)结构类型可以明确它是哪种类型,并使您能够通过肉眼快速确定两种类型是否相同。另一方面,typedef
ed 别名可以表示任何类型,并且同一类型可以有多个这样的别名。
typedef
有一些好的和适当的用途,但还有很多其他用途的适当性是代码风格的考虑。我自己,我非常喜欢尽量减少使用 typedef
的样式。
【讨论】:
您的回答是迄今为止最好的。 Typedef 本质上隐藏了类型是自定义结构还是标准结构,这可能并不理想。如果其他人正在写答案,我会稍等片刻接受这个答案。【参考方案6】:typedef
只是为了方便您引用您的结构而无需在每次引用时都明确声明 struct MyStruct
。
有些人实际上更喜欢这种明确性,清楚地表明您正在使用用户定义的类型。
【讨论】:
以上是关于什么时候使用没有 typedef 的结构有意义?的主要内容,如果未能解决你的问题,请参考以下文章
在 Spring MVC 之上使用 Spring WebFlow 啥时候有意义?
在没有 UITableViewController 的情况下使用 NSFetchedResultsController 有意义吗?它们有啥关系?