在C语言中,是否需要使用typedef?

13

Typedef在可移植的名称、标签名(typedef struct foo Foo;)和使复杂(函数)声明可读性更高(typedef int(*cmpfunc)(const void *, const void *);)等方面非常有用。

但是,在C语言中是否真正需要typedef的情况?使用简单地编写派生类型可以实现相同的效果。

稍微解释一下:我指的是对于语言用户,而不是实现者。整个stdint.h就是第二类的很好的例子。

结论

感谢您提供的所有信息。我想我可以总结如下:

  • C99库需要typedef来实现各种(u)intN_t类型。
  • 在C89中,您确实希望自己使用typedef来创建类似的可移植类型。
  • 在使用va_arg宏时可能需要typedef,但我怀疑您实际上不会遇到这些派生类型。

如果不是通过typedef,你还能如何定义可移植类型呢? - Michael Foukarakis
@Michael:如果实现已经给我 int32_t 等类型,那我就不需要这样做了。 - schot
15个回答

15

感谢各位的答案。我自己又查了一些资料,在C99标准中找到了J.2未定义行为:

在以下情况下,行为是未定义的: [...]

  • va_arg 宏的类型参数不是这样一个类型,即这个类型的对象指针可以通过在其后面加上* 来简单地获得(参见7.15.1.1)。

因此,如果您想要传递/提取复杂的派生类型(例如int (*)[])给va_arg,您需要将该类型typedef为可行的类型(已更新/更正):

typedef int (*intarrptr)[4];
intarrptr x = va_arg(ap, intarrptr);

因为很难找到实际有用的案例,所以人们可能得出结论,这不是证明需要使用typedef的有力论据。


@Oli Charlesworth:是的,我在编写这个棘手的派生类型时犯了一个错误。请查看更新后的示例。 - schot
@schot:还是不合法。你是说typedef int (*intarrptr)[4];吗? - Oliver Charlesworth
3
我认为这是第一个实际示例,表明在用户代码中需要使用 typedef (而不是在编译器头文件内部等)。很好的发现 :) - bdonlan
还不太确定,因为我还没有看到一个可行的例子!你能不能把类型用括号括起来? - Oliver Charlesworth
4
为了获得指向int (*)[4]的指针,仅在末尾添加一个*是不够的-必须将其插入到中间,例如int (**)[4]。然而,va_args宏无法更改传入参数的内部内容,它们只能在末尾添加标记。这就是为什么有这个限制的原因。 - bdonlan
显示剩余2条评论

14

typedef 是一种别名。因此,你总是可以用实际类型替换别名。否则它将不再是别名。

这并不意味着避免使用 typedef 是一个好主意。


不,我不会避免使用 typedef :) 但是显然邪恶的宏 va_arg 可能需要一个 typedef。(请参见我的答案。) - schot
3
您可以使用实际类型语义上替换typedef名称。但您并不总是能够在文本上替换它。所以,在关键依赖于文本替换的代码中(例如像va_arg这样的宏),您别无选择,只能使用typedef名称。 - AnT stands with Russia
尽管这可能不是100%好的选择,但我选择它而不是我的答案,因为回想起来,我的答案似乎有点过于理论化了。 - schot

5

来自维基百科:

typedef是C和C++编程语言中的关键字。typedef的目的是为现有类型分配替代名称,通常是那些标准声明繁琐、可能令人困惑或可能因一种实现而异的类型。1

根据该定义,不,它从未是必需的,因为您始终可以编写扩展形式。但是,可能会有宏定义根据平台或其他内容选择要使用的typedef。因此,始终使用扩展形式可能不太可移植。


在offsetof()宏中,您不能使用完全展开的形式。 - Sjoerd
1
如果名称包含逗号,则需要使用typedef在宏中使用它。 - tstenner
@tstenner:一个包含逗号的名字的例子是什么? - Oliver Charlesworth
1
@tstenner:回答自己的问题:void(*)(int,int)。是的,这似乎是标准反例! - Oliver Charlesworth
1
我仍在努力决定typedef是天才还是意外。 - Ken

2

这绝对是必需的,一个很好的例子是size_t,在各种平台上都进行了typedef。


你可以使用unsigned intunsigned long代替size_t,所以它不是一个很好的例子。像int32_t这样的类型可能需要在不同平台上进行不同的typedef,因此更好。 - JeremyP
2
这是指“能够编写独立于平台的代码”是必需的。但在特定平台上,理论上你可以随时替换typedef类型。 - Oliver Charlesworth
在我看来,size_t可以很好地成为一个独立的类型,与所有其他无符号整数类型不同。intN_T需要被定义为typedefs,但请参阅我对问题的附加说明。 - schot
问题是是否需要 typedef - 编译器肯定可以使用 #define 或原始类型来实现 size_t,因此这并不意味着需要 typedef - bdonlan
1
@schot:intN_t需要被定义为typedef,但它们不需要是标准整数类型(即signed charshortint,...),因为C标准允许实现定义扩展整数类型。 - Christoph

2

在检查C编译器是否符合ISO-C标准的测试套件中,关键字typedef绝对是必需的。

在不明确需要使用typedef的代码中(如上文所述的测试套件),它通常非常有帮助,但从不是必需的,因为它只建立对另一种类型的别名。它不会创建类型。

最后,我认为不应该将避免使用typedefs缩写而避免编译器限制预处理源文件大小之类的事情计算在内。


1

offsetof()宏需要一个结构体名称和一个成员名称,因此不能与内联结构定义一起使用。也许在您的实现中可以,但这并不被C标准保证。

更新: 正如评论中指出的那样,这是可能的:

struct blah { int a; int b; };
x = offsetof(struct blah, a); // legal

但是内联结构体定义不是:

x = offsetof(struct {int a; int b;}, a); // illegal

前者不包含关键字typedef,但是也无法内联结构定义。哪个版本被“简单写出派生类型”所指示是不清楚的。


你肯定可以这样做 struct blah { int a; int b; }; ... offsetof(struct blah, a);,对吧? - Oliver Charlesworth
我理解这个问题是在问是否可能使用offsetof( struct { int a; int b; } , a)。 - Sjoerd

1

是的。整数类型必须具有固定大小,例如int32_t(并且您希望您的代码具有可移植性)。


如果您希望您的代码可移植,请勿编写依赖于变量类型字节长度的代码! - user82238
2
@Blank Xavier:有很多跨平台应用程序需要已知大小的整数类型,比如处理二进制文件或网络协议等。 - JeremyP

1

不。

typedef 并不像 C++ 中的类一样创建一个真正的新类型,它只是创建了一个类型别名——为已经存在的某个东西创建的单个标识符。在 typedef 中,你并没有定义任何新的行为、语义、转换或操作符。


0

个人程序员不需要创建自己的typedef。没有普遍规定说我不能写这样的东西

int *(*(*x())[5])();

如果我想这样做的话(尽管我的公司编码标准可能不赞成)。

然而,它们都存在于标准库中(例如FILEsize_t等),所以你真的无法避免使用typedef名称。


0

不,我认为我可以安全地说!


一个直觉和事实的组合,我想不出反例。证明否定命题非常困难!真的不值得打负分... - Oliver Charlesworth

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