我见过许多由如下结构组成的程序
typedef struct
{
int i;
char k;
} elem;
elem user;
为什么经常需要它?有什么具体原因或适用范围吗?
struct * ptr
会导致错误
正如 Greg Hewgill 所说,typedef 意味着您不再需要到处写 struct
。这不仅节省了击键,还可以使代码更清晰,因为它提供了更多的抽象。
像这样的东西
typedef struct {
int x, y;
} Point;
Point point_new(int x, int y)
{
Point a;
a.x = x;
a.y = y;
return a;
}
当您不需要到处看到“struct”关键字时,它会变得更清晰,它看起来更像是在您的语言中确实存在一种称为“Point”的类型。我猜在 typedef
之后就是这种情况。
另请注意,虽然您的示例(和我的示例)省略了 struct
本身的命名,但实际上命名它对于您想要提供不透明类型时也很有用。然后你会在标题中有这样的代码,例如:
typedef struct Point Point;
Point * point_new(int x, int y);
然后在实现文件中提供 struct
定义:
struct Point
{
int x, y;
};
Point * point_new(int x, int y)
{
Point *p;
if((p = malloc(sizeof *p)) != NULL)
{
p->x = x;
p->y = y;
}
return p;
}
在后一种情况下,您不能按值返回 Point,因为它的定义对头文件的用户是隐藏的。例如,这是一种在 GTK+ 中广泛使用的技术。
更新请注意,还有一些备受推崇的 C 项目,其中使用 typedef
隐藏 struct
被认为是一个坏主意,Linux 内核可能是最著名的此类项目。参见The Linux Kernel CodingStyle document的第 5 章,了解 Linus 的愤怒言论。 :) 我的观点是,问题中的“应该”毕竟可能不是一成不变的。
令人惊讶的是有多少人犯了这个错误。请不要在 C 中 typedef 结构,它会不必要地污染全局命名空间,这通常在大型 C 程序中已经被严重污染。
此外,没有标签名称的 typedef 结构是不必要地在头文件之间强加排序关系的主要原因。
考虑:
#ifndef FOO_H
#define FOO_H 1
#define FOO_DEF (0xDEADBABE)
struct bar; /* forward declaration, defined in bar.h*/
struct foo {
struct bar *bar;
};
#endif
使用这样的定义,而不使用 typedef,编译单元可以包含 foo.h 以获取 FOO_DEF
定义。如果它不尝试取消引用 foo
结构的“bar”成员,则不需要包含“bar.h”文件。
此外,由于标签名称和成员名称之间的名称空间不同,因此可以编写非常易读的代码,例如:
struct foo *foo;
printf("foo->bar = %p", foo->bar);
由于命名空间是独立的,因此在命名变量与其结构标记名称一致时不会发生冲突。
如果我必须维护您的代码,我将删除您的 typedef 结构。
typedef struct X { ... } X
。这样,您可以在定义可用的任何地方使用缩写形式 X
来寻址结构,但仍然可以在需要时前向声明和使用 struct X
。
摘自 Dan Saks (http://www.ddj.com/cpp/184403396?pgno=3) 的一篇旧文章:
命名结构的 C 语言规则有点古怪,但它们是无害的。然而,当扩展到 C++ 中的类时,这些相同的规则打开了一些小裂缝,让 bug 爬过。在 C 中,名称 s 出现在 struct s { ... };是一个标签。标签名称不是类型名称。给定上面的定义,声明如 sx; /* C 语言错误 */ s *p; /* C 中的错误 */ 是 C 中的错误。您必须将它们写为 struct sx; /* OK */ struct s *p; /* OK */ 联合和枚举的名称也是标签而不是类型。在 C 中,标签不同于所有其他名称(对于函数、类型、变量和枚举常量)。 C 编译器在符号表中维护标记,该符号表在概念上与包含所有其他名称的表在物理上是分开的。因此,C 程序有可能在同一范围内同时拥有一个标记和另一个拼写相同的名称。例如,结构 ss;是一个有效的声明,它声明了 struct s 类型的变量 s。这可能不是好的做法,但 C 编译器必须接受它。我从来没有看到为什么 C 是这样设计的理由。我一直认为这是一个错误,但它就在那里。许多程序员(包括你的程序员)更喜欢将结构名称视为类型名称,因此他们使用 typedef 为标签定义别名。例如,定义 struct s { ... }; typedef struct s S;允许您使用 S 代替 struct s,如在 S x 中; S *p;程序不能同时使用 S 作为类型和变量(或函数或枚举常量)的名称:SS; // 错误 这很好。结构、联合或枚举定义中的标记名称是可选的。许多程序员将结构定义折叠到 typedef 中并完全省去标记,例如: typedef struct { ... } S;
链接的文章还讨论了不需要 typedef
的 C++ 行为如何导致微妙的名称隐藏问题。为防止这些问题,最好也typedef
用 C++ 编写类和结构,尽管乍一看似乎没有必要。在 C++ 中,使用 typedef
隐藏名称成为编译器告诉您的错误,而不是潜在问题的隐藏来源。
int stat(const char *restrict path, struct stat *restrict buf)
函数。在普通名称空间中有一个函数 stat
,在标签名称空间中有一个函数 struct stat
。
使用 typedef
可以避免每次声明该类型的变量时都必须编写 struct
:
struct elem
{
int i;
char k;
};
elem user; // compile error!
struct elem user; // this is correct
typedef
结构的理由。你也可以做typedef struct foo foo;
。当然,struct
关键字不是必需的,这可能是一个有用的提示,我们看到的类型别名是结构的别名,但总的来说它还不错。还要考虑这样一种情况,即生成的类型别名 typedef
的标识符表明它是结构的别名,fe: typedef struct foo foo_struct;
。
总是 typedef 枚举和结构的另一个很好的理由是由这个问题引起的:
enum EnumDef
{
FIRST_ITEM,
SECOND_ITEM
};
struct StructDef
{
enum EnuumDef MyEnum;
unsigned int MyVar;
} MyStruct;
注意到 struct (EnuumDef) 中 EnumDef 的错字了吗?这编译没有错误(或警告)并且(取决于 C 标准的字面解释)是正确的。问题是我刚刚在我的结构中创建了一个新的(空)枚举定义。我没有(按预期)使用前面的定义 EnumDef。
使用 typdef 类似的拼写错误会导致使用未知类型的编译器错误:
typedef
{
FIRST_ITEM,
SECOND_ITEM
} EnumDef;
typedef struct
{
EnuumDef MyEnum; /* compiler error (unknown type) */
unsigned int MyVar;
} StructDef;
StrructDef MyStruct; /* compiler error (unknown type) */
我会提倡 ALWAYS typedef'ing 结构和枚举。
不仅是为了节省一些打字(不是双关语;)),而且因为它更安全。
Linux kernel coding style 第 5 章给出了使用 typedef
的优点和缺点(主要是缺点)。
请不要使用“vps_t”之类的东西。将 typedef 用于结构和指针是错误的。当你看到一个 vps_t a;在源代码中,它是什么意思?相反,如果它说 struct virtual_container *a;您实际上可以说出“a”是什么。很多人认为 typedef“有助于提高可读性”。不是这样。它们仅对以下情况有用: (a) 完全不透明的对象(其中 typedef 被积极用于隐藏对象是什么)。示例:“pte_t”等不透明对象,您只能使用正确的访问器函数访问。笔记!不透明性和“访问器功能”本身并不好。我们将它们用于 pte_t 等的原因是那里确实有绝对为零的可移植访问信息。 (b) 清晰的整数类型,其中抽象有助于避免混淆它是“int”还是“long”。 u8/u16/u32 是非常好的类型定义,尽管它们比这里更适合 (d) 类。笔记!再次 - 需要有一个理由。如果某些东西是“unsigned long”,那么就没有理由做 typedef unsigned long myflags_t;但是,如果有明确的理由说明它在某些情况下可能是“unsigned int”,而在其他配置下可能是“unsigned long”,那么一定要继续使用 typedef。 (c) 当您使用 sparse 从字面上创建一个新类型进行类型检查时。 (d) 在某些特殊情况下与标准 C99 类型相同的新类型。尽管眼睛和大脑只需要很短的时间就可以习惯像“uint32_t”这样的标准类型,但还是有人反对使用它们。因此,允许使用特定于 Linux 的“u8/u16/u32/u64”类型及其与标准类型相同的签名等价物——尽管它们在您自己的新代码中不是强制性的。在编辑已经使用一个或另一组类型的现有代码时,您应该遵守该代码中的现有选择。 (e) 在用户空间中安全使用的类型。在某些对用户空间可见的结构中,我们不能要求 C99 类型,也不能使用上面的 'u32' 形式。因此,我们在与用户空间共享的所有结构中使用 __u32 和类似类型。也许还有其他情况,但是规则基本上应该是永远不要使用 typedef,除非您可以清楚地匹配其中一个规则。通常,具有可以合理直接访问的元素的指针或结构不应该是 typedef。
float
坐标表示一个 3d 点。可以要求想要读取点的 x
值的代码使用访问器函数来执行此操作,并且有时真正抽象的“可读 3d 点”类型可能很有用,但还有很多其他时候所需要的是一种可以完成 float x,y,z
三元组可以做的所有事情的类型,并且具有相同的语义。在后一种情况下,试图使类型不透明会导致混乱而不是清晰。
事实证明,有利有弊。一个有用的信息来源是开创性书籍“Expert C Programming” (Chapter 3)。简而言之,在 C 中,您有多个命名空间:标签、类型、成员名称和标识符。 typedef
为类型引入别名并将其定位在标记命名空间中。即,
typedef struct Tag{
...members...
}Type;
定义了两件事。 1)标签命名空间中的标签和2)类型命名空间中的类型。因此,您可以同时执行 Type myType
和 struct Tag myTagType
。 struct Type myType
或 Tag myTagType
之类的声明是非法的。此外,在这样的声明中:
typedef Type *Type_ptr;
我们定义了一个指向我们的类型的指针。所以如果我们声明:
Type_ptr var1, var2;
struct Tag *myTagType1, myTagType2;
那么 var1
、var2
和 myTagType1
是指向 Type 但 myTagType2
不是的指针。
在上面提到的书中,它提到 typedefing structs 不是很有用,因为它只是使程序员免于编写单词 struct 。但是,我有一个反对意见,就像许多其他 C 程序员一样。尽管当您想在 C 中实现多态性时,它有时会混淆某些名称(这就是为什么不建议在像内核这样的大型代码库中使用它)它有很大帮助look here for details。例子:
typedef struct MyWriter_t{
MyPipe super;
MyQueue relative;
uint32_t flags;
...
}MyWriter;
你可以做:
void my_writer_func(MyPipe *s)
{
MyWriter *self = (MyWriter *) s;
uint32_t myFlags = self->flags;
...
}
因此,您可以通过转换通过内部结构 (MyPipe
) 访问外部成员 (flags
)。对我来说,每次想要执行此类功能时,强制转换整个类型比执行 (struct MyWriter_ *) s;
更容易混淆。在这些情况下,简短的引用很重要,尤其是当您在代码中大量使用该技术时。
最后,与宏相比,typedef
ed 类型的最后一个方面是无法扩展它们。例如,如果您有:
#define X char[10] or
typedef char Y[10]
然后你可以声明
unsigned X x; but not
unsigned Y y;
对于结构,我们并不真正关心这一点,因为它不适用于存储说明符(volatile
和 const
)。
MyPipe *s; MyWriter *self = (MyWriter *) s;
而您刚刚打破了严格的别名。
typedef struct Tag{ ...members... }Type;
“定义了两件事”不太有意义。如果 typedef 定义了标签,那么这里的“类型”也应该是一个标签。事实是定义定义了 2 个标签和 1 个类型(或 2 个类型和 1 个标签。不确定):struct Tag
、Tag
和 Type
。 struct Tag
绝对是一种类型。 Tag
是一个标签。但令人困惑的是 Type
是标签还是类型
我认为 typedef 甚至不可能进行前向声明。当依赖项(知道)是双向的时,使用 struct、enum 和 union 允许转发声明。
风格:在 C++ 中使用 typedef 很有意义。在处理需要多个和/或可变参数的模板时,它几乎是必要的。 typedef 有助于保持命名的正确性。
在 C 编程语言中并非如此。 typedef 的使用通常没有任何目的,只是混淆了数据结构的使用。由于只有 { struct (6), enum (4), union (5) } 次击键用于声明数据类型,因此几乎没有使用结构的别名。该数据类型是联合还是结构?使用简单的非类型定义声明可以让您立即知道它是什么类型。
请注意,Linux 是如何在严格避免 typedef 带来的这种别名胡说八道的情况下编写的。结果是简约和干净的风格。
struct
... Typedef 创建新类型。你用什么?类型。我们不关心它是结构体、联合体还是枚举,这就是我们对它进行类型定义的原因。
FILE
?
让我们从基础开始,逐步向上。
这是结构定义的示例:
struct point
{
int x, y;
};
这里的名称 point
是可选的。
结构可以在其定义期间或之后声明。
在定义期间声明
struct point
{
int x, y;
} first_point, second_point;
定义后声明
struct point
{
int x, y;
};
struct point first_point, second_point;
现在,请仔细注意上面的最后一种情况;如果您决定稍后在代码中创建该类型,则需要编写 struct point
来声明该类型的结构。
输入 typedef
。如果您打算稍后在您的程序中使用相同的蓝图创建新的结构(结构是一种自定义数据类型),那么在其定义期间使用 typedef
可能是一个好主意,因为您可以节省一些未来的输入。
typedef struct point
{
int x, y;
} Points;
Points first_point, second_point;
命名自定义类型时要注意
没有什么可以阻止您在自定义类型名称的末尾使用 _t 后缀,但 POSIX 标准保留使用后缀 _t 来表示标准库类型名称。
typedef point { ... } Points
并不是一个好主意。由于名称 point
和 Points
有太多区别。当有人必须为指针转发声明 struct
时,不同的名称是一个问题,尤其是当它位于可能更改的库中时。如果您使用 typedef
,请使用相同的名称或有明确的规则如何将名称从 struct
名称转换为 typedef
名称。
您(可选地)为结构命名的名称称为标记名称,并且如前所述,它本身不是类型。要获取类型需要结构前缀。
除了 GTK+,我不确定标记名是否像结构类型的 typedef 一样普遍使用,因此在 C++ 中可以识别,您可以省略 struct 关键字并将标记名也用作类型名称:
struct MyStruct
{
int i;
};
// The following is legal in C++:
MyStruct obj;
obj.i = 7;
typedef 不会提供一组相互依赖的数据结构。这是你不能用 typdef 做的:
struct bar;
struct foo;
struct foo {
struct bar *b;
};
struct bar {
struct foo *f;
};
当然,您可以随时添加:
typedef struct foo foo_t;
typedef struct bar bar_t;
那到底有什么意义呢?
typedef struct name name;
作为一种常见做法,但是已经证明自己可靠的旧编译器不允许这种重复定义,并且解决该限制会产生比简单地使用 struct tagName
作为类型名称更多的问题。
A> typdef 通过允许为数据类型创建更有意义的同义词来帮助解释程序的含义和文档。此外,它们有助于针对可移植性问题(K&R、pg147、C prog lang)对程序进行参数化。
B> 一个结构定义了一个类型。 Structs 允许对一组 var 进行方便的分组,以便于将(K&R、pg127、C prog lang.)作为一个单元进行处理
C> typedef'ing a struct 在上面的 A 中解释。
D> 对我来说,结构是自定义类型或容器或集合或命名空间或复杂类型,而 typdef 只是创建更多昵称的一种手段。
结果在 C99 中 typedef 是必需的。它已经过时了,但是很多工具(ala HackRank)使用 c99 作为其纯 C 实现。那里需要 typedef 。
我并不是说如果要求发生变化,他们应该改变(可能有两个 C 选项),我们这些在网站上学习面试的人将是 SOL。
typedef
中是必需的。”你是什么意思?
C
,而不是 C++
。在 C
中,typedef 是“必需的”(并且很可能总是如此)。 '必需',您将无法声明变量 Point varName;
并且类型与没有 typedef struct Point Point;
的 struct Point;
同义。
在“C”编程语言中,关键字“typedef”用于为某些对象(结构、数组、函数..枚举类型)声明一个新名称。例如,我将使用“struct-s”。在“C”中,我们经常在“main”函数之外声明一个“struct”。例如:
struct complex{ int real_part, img_part }COMPLEX;
main(){
struct KOMPLEKS number; // number type is now a struct type
number.real_part = 3;
number.img_part = -1;
printf("Number: %d.%d i \n",number.real_part, number.img_part);
}
每次我决定使用结构类型时,我都需要这个关键字 'struct 'something' 'name'.'typedef' 将简单地重命名该类型,并且我可以在每次需要时在我的程序中使用这个新名称。所以我们的代码将是:
typedef struct complex{int real_part, img_part; }COMPLEX;
//now COMPLEX is the new name for this structure and if I want to use it without
// a keyword like in the first example 'struct complex number'.
main(){
COMPLEX number; // number is now the same type as in the first example
number.real_part = 1;
number.img)part = 5;
printf("%d %d \n", number.real_part, number.img_part);
}
如果您有一些将在整个程序中使用的本地对象(结构、数组、有价值的),您可以简单地使用“typedef”给它一个名称。
根本上,在C语言中,struct/union/enum都是C语言预处理器处理的宏指令(不要误认为预处理器处理“#include”等)
所以 :
struct a
{
int i;
};
struct b
{
struct a;
int i;
int j;
};
struct b 被用作这样的东西:
struct b
{
struct a
{
int i;
};
int i;
int j;
}
因此,在编译时它在堆栈上演变为: b: int ai int i int j
这也是为什么很难拥有自引用结构,C 预处理器在无法终止的声明循环中循环。
typedef 是类型说明符,这意味着只有 C 编译器处理它,它可以像他想要的那样优化汇编代码实现。它也不会像预处理器对结构一样愚蠢地消耗 par 类型的成员,而是使用更复杂的引用构造算法,因此构造如下:
typedef struct a A; //anticipated declaration for member declaration
typedef struct a //Implemented declaration
{
A* b; // member declaration
}A;
被允许并且功能齐全。当执行线程离开初始化函数的应用程序字段时,此实现还可以访问编译器类型转换并消除一些错误影响。
这意味着在 C 中 typedef 更接近于 C++ 类而不是单独的结构。
structs
和 typedef
的分辨率已经够糟糕的了,但是你的其余部分内容非常混乱,我发现很难从中得到任何信息。不过,我可以说的一件事是,您认为非 typedef
d struct
不能被向前声明或用作不透明(指针)成员的想法是完全错误的。在您的第一个示例中,struct b
可以简单地包含一个 struct a *
,不需要 typedef
。 struct
s 只是宏观扩张的愚蠢片段,typedef
s 赋予它们革命性的新力量的断言是非常错误的