我看过很多程序,其中包括以下结构
typedef struct
{
int i;
char k;
} elem;
elem user;
为什么经常需要它?有任何特定原因或适用范围?
struct * ptr
结构内的行会导致错误
我看过很多程序,其中包括以下结构
typedef struct
{
int i;
char k;
} elem;
elem user;
为什么经常需要它?有任何特定原因或适用范围?
struct * ptr
结构内的行会导致错误
Answers:
正如Greg Hewgill所说的,typedef意味着您不再需要在struct
所有地方写东西。这不仅可以节省击键次数,而且还可以使代码更整洁,因为它提供了更多的smidgen抽象。
像东西
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;
}
在后一种情况下,您无法返回“按值”,因为头文件的用户不知道其定义。例如,这是GTK +中广泛使用的技术。
UPDATE请注意,也有一些备受关注的C项目,这种使用typedef
隐藏的struct
想法被认为是一个坏主意,Linux内核可能是最著名的此类项目。有关Linus的愤怒话,请参阅Linux Kernel CodingStyle文档的第5章。:)我的意思是,毕竟问题中的“应该”可能不是一成不变的。
令人惊讶的是有很多人弄错了。请不要在C中进行typedef结构,它会不必要地污染全局名称空间,而该名称空间通常在大型C程序中已经非常污染。
同样,没有标记名的类型定义的结构也是在头文件之间不必要地施加排序关系的主要原因。
考虑:
#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,combiland单元可能会包含foo.h来获得该FOO_DEF
定义。如果它不尝试取消引用结构的'bar'成员,foo
则无需包含“ bar.h”文件。
另外,由于标记名和成员名之间的名称空间不同,因此可以编写非常易读的代码,例如:
struct foo *foo;
printf("foo->bar = %p", foo->bar);
由于名称空间是独立的,因此在命名与它们的struct标记名称一致的变量时不会发生冲突。
如果必须维护您的代码,则将删除您的typedef结构。
typedef struct X { ... } X
。这样,您可以使用简短形式X
在定义可用的任何地方寻址该结构,但仍可以struct X
在需要时进行前向声明和使用。
摘自Dan Saks的旧文章(http://www.ddj.com/cpp/184403396?pgno=3):
用于命名结构的C语言规则有些古怪,但它们几乎没有害处。但是,当扩展到C ++中的类时,这些相同的规则几乎不会为爬虫提供任何裂痕。
在C中,名称出现在
struct s { ... };
是一个标签。标记名称不是类型名称。根据上面的定义,声明如下
s x; /* error in C */ s *p; /* error in C */
是C中的错误。您必须将它们写为
struct s x; /* OK */ struct s *p; /* OK */
联合和枚举的名称也是标签而不是类型。
在C语言中,标记与所有其他名称(对于函数,类型,变量和枚举常量)不同。C编译器将符号保存在符号表中,该符号表在概念上与包含所有其他名称的表在物理上并不分离。因此,C程序可能在相同范围内同时具有标签和另一个具有相同拼写的名称。例如,
struct s s;
是一个有效的声明,它声明类型为struct的变量s。这可能不是一个好习惯,但是C编译器必须接受它。对于C为什么如此设计,我从未见过任何理由。我一直认为这是一个错误,但是确实存在。
许多程序员(包括您的程序员在内)实际上都喜欢将结构名视为类型名,因此他们使用typedef为标签定义别名。例如,定义
struct s { ... }; typedef struct s S;
允许您使用S代替struct,如
S x; S *p;
程序不能同时使用S作为类型和变量(或函数或枚举常量)的名称:
S S; // error
很好
结构,联合或枚举定义中的标记名称是可选的。许多程序员将struct定义折叠为typedef并完全放弃了标签,如下所示:
typedef struct { ... } S;
链接的文章还讨论了不要求a的C ++行为如何typedef
引起细微的名称隐藏问题。为了防止这些问题,typedef
对于C ++中的类和结构来说,这也是一个好主意,即使乍一看似乎没有必要。在C ++中,typedef
隐藏名称成为编译器告诉您的错误,而不是潜在问题的隐藏源。
int stat(const char *restrict path, struct stat *restrict buf)
功能的(POSIX或Unix)程序中,标记名与非标记名相同的一个示例。在那里,您stat
在普通名称空间和struct stat
标签名称空间中都有一个功能。
使用a 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;
注意结构(Enu u mDef)中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) */
我会提倡始终使用typedef的结构和枚举。
不仅节省了一些键入操作(无双关语;)),而且因为它更安全。
enum
用作类型,因为许多编译器在“错误”使用它们时会发出奇怪的警告。例如,将an初始化enum
为0可能会给出“整数常量不在枚举中”警告。enum
也不允许向前声明an 。应该使用int
(或unsigned int
)代替。
Linux内核编码风格第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是完美的typedef,尽管它们比这里更适合(d)类。
注意!再次-需要有一个原因。如果某事是“无符号的长”,则没有理由这样做
typedef unsigned long myflags_t;
但是,如果有明确的原因,为什么在某些情况下它可能是“ unsigned int”而在其他配置下可能是“ unsigned long”,则一定要继续使用typedef。
(c)当您使用稀疏从字面上创建用于类型检查的新类型时。
(d)在某些特殊情况下,与标准C99类型相同的新类型。
尽管眼睛和大脑只需要很短的时间就习惯了'uint32_t'这样的标准类型,但是仍然有人反对使用它们。
因此,允许使用特定于Linux的'u8 / u16 / u32 / u64'类型及其与标准类型相同的带符号等效项-尽管在您自己的新代码中不是必需的。
编辑已使用一种或另一组类型的现有代码时,应遵循该代码中的现有选择。
(e)在用户空间中可以安全使用的类型。
在用户空间可见的某些结构中,我们不能要求C99类型,也不能使用上面的“ u32”形式。因此,我们在与用户空间共享的所有结构中使用__u32和类似类型。
也许还有其他情况,但是该规则基本上应该是从不使用typedef,除非您可以清楚地匹配其中一个规则。
通常,指针或具有可以合理地直接访问的元素的结构永远都不应该是typedef。
事实证明,这是有利有弊的。有用的信息来源是开创性的书“ Expert C Programming”(第3章)。简而言之,在C中,您具有多个名称空间:标签,类型,成员名称和标识符。typedef
为类型引入别名,并将其定位在标记名称空间中。即
typedef struct Tag{
...members...
}Type;
定义了两件事。标签名称空间中的一个标签,类型名称空间中的一个类型。因此,您可以同时进行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
不是。
在上述书籍中,它提到了类型定义结构不是很有用,因为它仅使程序员不必编写struct这个词。但是,与许多其他C程序员一样,我也有反对意见。当您想要在C中实现多态时,尽管有时会混淆一些名称(这就是为什么不建议在诸如内核之类的大型代码库中使用它的原因),但在这里可以找到很多细节。例:
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;
...
}
因此,您可以flags
通过内部结构(MyPipe
)访问外部成员()。对我来说,强制转换整个类型要比(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定义了标签,则此处的“ Type”也应该是标签。事实是该定义定义了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)}个键来声明数据类型,因此几乎没有使用struct的别名。该数据类型是联合还是结构?使用简单的非典型定义声明,您可以立即知道它是什么类型。
请注意,Linux是如何严格避免这种废话的typedef带来的。结果是极简风格和简洁风格。
struct
所有地方重复出现……Typedef创造了新的类型。你用什么?类型。我们不在乎它是结构,联合还是枚举,这就是我们对它进行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
。如果您打算稍后在程序中使用相同的蓝图创建新的Structure(Structure是自定义数据类型),则typedef
在其定义期间使用可能是一个好主意,因为您可以保存一些向前输入。
typedef struct point
{
int x, y;
} Points;
Points first_point, second_point;
没有什么可以阻止您在自定义类型名称的末尾使用_t后缀,但是POSIX standard保留使用后缀_t表示标准库类型名称。
在“ 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类型时,都需要使用关键字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编译器对其进行处理,并且可以像他想要的那样优化汇编程序代码实现。它还不会像préprocessor那样笨拙地使用par类型的成员来处理结构,而是使用更复杂的引用构造算法,因此构造如下:
typedef struct a A; //anticipated declaration for member declaration
typedef struct a //Implemented declaration
{
A* b; // member declaration
}A;
被允许并且功能齐全。当执行线程离开初始化函数的应用程序域时,此实现还可以访问编译器类型转换并消除一些错误影响。
这意味着在C中,typedef更像C ++类,而不是孤独的结构。
structs
and typedef
的分辨率已经足够糟糕了,但是您的其余部分却是如此混乱,以至于我很难从中获取任何消息。有一件事我可以说,不过,是你的想法,非typedef
d struct
不能前置声明或用作不透明(指针)成员是完全错误的。在您的第一个示例中,struct b
可以简单地包含struct a *
,而不是typedef
必需的。断言struct
s只是宏观扩张的愚蠢片段,并typedef
赋予它们革命性的新力量,这些