为什么我们应该在C中经常input一个结构?
我看过很多程序,包括下面的结构
typedef struct { int i; char k; } elem; elem user;
为什么需要这么频繁? 任何特定原因或适用区域?
正如Greg Hewgill所说,typedef意味着您不必再遍布整个地方写struct
。 这不仅节省了击键次数,还可以使代码更清洁,因为它提供了更多的抽象。
东西像
typedef struct { int x, y; } Point; Point point_new(int x, int y) { Point a; ax = x; ay = y; return a; }
当你不需要在整个地方看到“struct”关键字就变得更清洁了,看起来更像是你的语言里实际上有一个叫做“Point”的types。 在typedef
,我猜是这样的。
还要注意,虽然你的例子(和我的)省略了命名struct
本身,但实际上命名它也是有用的,当你想提供一个不透明的types。 然后你会在头部有这样的代码,例如:
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 +中广泛使用的技术。
更新请注意,也有高度重视的C项目,这种使用typedef
隐藏struct
被认为是一个坏主意,Linux内核可能是最知名的这样的项目。 有关Linus生气的话,请参阅Linux Kernel CodingStyle文档的第5章。 :)我的观点是,问题中的“应该”毕竟可能不是一成不变的。
有多less人弄错了,真是太神奇了。 请不要在C中使用typedef结构,它会不必要地污染已经在大型C程序中受到严重污染的全局名称空间。
此外,没有标签名称的typedef'd结构是造成头文件之间不必要的sorting关系的主要原因。
考虑:
#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,compiland单元可以包含foo.h来获得FOO_DEF
定义。 如果它不试图取消引用foo
结构的“bar”成员,那么将不需要包含“bar.h”文件。
而且,由于标签名称和成员名称之间的名称空间不同,因此可以编写非常易读的代码,例如:
struct foo *foo; printf("foo->bar = %p", foo->bar);
由于命名空间是独立的,命名与其结构标签名称一致的variables没有冲突。
如果我必须维护你的代码,我将删除你的typedef结构。
来自Dan Saks的一篇旧文章( http://www.ddj.com/cpp/184403396?pgno=3 ):
命名结构的C语言规则有点古怪,但是它们非常无害。 然而,当扩展到C ++的类时,这些相同的规则对于错误的爬行没有什么裂缝。
在C中,名字出现在
struct s { ... };
是一个标签。 标签名称不是types名称。 鉴于上面的定义,声明如
sx; /* error in C */ s *p; /* error in C */
在C中是错误的。你必须把它们写成
struct sx; /* OK */ struct s *p; /* OK */
联合和枚举的名称也是标签而不是types。
在C中,标签不同于所有其他名称(对于函数,types,variables和枚举常量)。 C编译器在符号表中维护标记,这在概念上与物理上不包含所有其他名称的表分开。 因此,C程序有可能在相同的范围内同时使用同一个拼写的标签和另一个名称。 例如,
struct ss;
是一个声明struct stypes的variables的有效声明。 这可能不是很好的做法,但是C编译器必须接受它。 我从来没有看到为什么C这样devise的理由。 我一直认为这是一个错误,但它是。
许多程序员(包括你的真正的)更喜欢把结构名称看作types名称,所以他们使用typedef为标签定义一个别名。 例如,定义
struct s { ... }; typedef struct s S;
可以让你用S代替struct s,如
S x; S *p;
程序不能使用S作为types和variables(或函数或枚举常量)的名称:
SS; // error
这很好。
结构体,联合体或枚举定义中的标记名是可选的。 许多程序员将结构定义折叠到typedef中,并完全省去标签,如下所示:
typedef struct { ... } S;
链接的文章还讨论了如何不要求typedef
的C ++行为可以导致微妙的名称隐藏问题。 为了防止出现这些问题,使用C ++types化类和结构也是一个好主意,即使乍一看似乎没有必要。 在C ++中,使用typedef
,名称隐藏成为编译器告诉您的错误,而不是潜在问题的隐藏源。
使用typedef
可以避免每次声明该types的variables时都必须写入struct
:
struct elem { int i; char k; }; elem user; // compile error! struct elem user; // this is correct
总是键入枚举和结构的另一个好的理由来自这个问题:
enum EnumDef { FIRST_ITEM, SECOND_ITEM }; struct StructDef { enum EnuumDef MyEnum; unsigned int MyVar; } MyStruct;
注意结构体(Enu u mDef)中的EnumDef错字? 这个编译没有错误(或警告),并且(取决于C标准的字面解释)是正确的。 问题是我只是在我的结构中创build了一个新的(空的)枚举定义。 我不是(如预期)使用以前的定义EnumDef。
使用typdef类似的错别字会导致使用未知types的编译器错误:
typedef { FIRST_ITEM, SECOND_ITEM } EnumDef; typedef struct { EnuumDef MyEnum; /* compiler error (unknown type) */ unsigned int MyVar; } StructDef; StrructDef MyStruct; /* compiler error (unknown type) */
我会主张总是键入结构和枚举。
不仅要节省一些打字(没有双关意图)),而是因为它更安全。
Linux内核编码风格第5章给出了使用typedef
优点和缺点(主要是缺点)。
请不要使用“vps_t”之类的东西。
对结构和指针使用typedef是错误的 。 当你看到一个
vps_t a;
在来源中,这是什么意思?
相反,如果它说
struct virtual_container *a;
你实际上可以说出“a”是什么。
很多人认为typedefs“有助于可读性”。 不是这样。 它们仅适用于:
(a)完全不透明的对象(typedef被主动用来隐藏对象的地方)。
例如:“pte_t”等不透明的对象,只能使用正确的存取函数访问。
注意! 不透明和“访问者function”本身并不好。 我们拥有像pte_t之类的东西的原因是,那里确实存在绝对零可移植的信息。
(b)清除整数types,抽象有助于避免混淆,无论是“int”还是“long”。
u8 / u16 / u32是非常好的types定义,尽pipe它们比这里更适合类(d)。
注意! 再次 – 这需要有一个原因 。 如果什么是“无符号长”,那么没有理由去做
typedef unsigned long myflags_t;
但是如果在某些情况下为什么它可能是一个“unsigned int”,而在其他configuration下可能是“unsigned long”,那么明确的原因是,那么请务必使用typedef。
(三)当你使用稀疏字面上创build一个新的types检查types。
(d)在某些特殊情况下,与标准C99types相同的新types。
尽pipe眼睛和大脑只需要很短的时间来适应像“uint32_t”这样的标准types,但有些人反对使用它们。
因此,特定于Linux的“u8 / u16 / u32 / u64”types及其与标准types相同的已签名等价物是允许的 – 尽pipe它们在您自己的新代码中不是必需的。
当编辑已经使用一个或另一个types的现有代码时,应该符合该代码中的现有select。
(e)在用户空间中安全使用的types。
在用户空间可见的某些结构中,我们不能使用C99types,也不能使用上面的“u32”forms。 因此,我们在与用户空间共享的所有结构中使用__u32和类似的types。
也许还有其他的一些情况,但是规则基本上是永远不要使用typedef,除非你能明确地匹配其中的一个规则。
一般来说,一个指针或者一个具有可以被合理地直接访问的元素的结构不应该是一个typedef。
我不认为前面的声明甚至可以用typedef。 当依赖性(知道)是双向的时,使用struct,enum和union允许进行前向声明。
风格:在C ++中使用typedef有相当的意义。 在处理需要多个和/或可变参数的模板时,几乎是必要的。 typedef有助于保持命名正确。
在C编程语言中不是这样的。 使用typedef通常没有任何用处,只是为了模糊数据结构的使用。 由于只有{struct(6),enum(4),union(5)}键击次数被用来声明一个数据types,所以几乎没有用于结构的别名。 数据types是联合还是结构? 使用直接的非typdefed声明可以让你立即知道它是什么types。
注意Linux是如何严格避免这种别名废话typedef带来的。 其结果是极简主义和干净的风格。
事实certificate,有prons和缺点。 有用的信息来源是“Expert C Programming”( 第3章 )。 简而言之,在C中,您有多个名称空间: 标记,types,成员名称和标识符 。 typedef
为types引入一个别名,并将其定位在标记名称空间中。 也就是说,
typedef struct Tag{ ...members... }Type;
定义了两件事。 标签命名空间中有一个标签,types命名空间中有一个Type。 所以你可以同时struct Tag myTagType
和struct Tag myTagType
。 声明像struct Type myType
Tag myTagType
或Tag myTagType
是非法的。 另外,在这样的声明中:
typedef Type *Type_ptr;
我们定义一个指向我们types的指针。 所以如果我们声明:
Type_ptr var1, var2; struct Tag *myTagType1, myTagType2;
那么var1
, var2
和myTagType1
是指向Type而不是myTagType1
的指针。
在上面提到的书中,它提到typedefing结构体并不是非常有用,因为它只会节省程序员写struct这个词。 不过,像其他许多C程序员一样,我有一个反对意见。 虽然它有时会变成混淆某些名称(这就是为什么在内核这样的大代码基础上不build议这样做的原因),但是如果要在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; ... }
所以你可以通过内部结构( MyPipe
)通过转换来访问外部成员( flags
)。 对于我来说,把整个types转换成比(struct MyWriter_ *) s;
更容易混淆(struct MyWriter_ *) s;
每次你想要执行这样的function。 在这些情况下,简短的引用是一个大问题,特别是如果你在代码中大量使用这个技术。
最后, typedef
types的最后一个方面是无法扩展它们,而不是macros。 例如,如果你有:
#define X char[10] or typedef char Y[10]
你可以再申报
unsigned X x; but not unsigned Y y;
我们并不真正关心结构,因为它不适用于存储说明符( volatile
和const
)。
您(可选)给结构的名称称为标记名称 ,正如已经指出的那样,它本身不是一种types。 要获得该types需要结构体前缀。
除了GTK +之外,我不确定标记名是否像通常那样被用作types定义的结构types,所以在被识别的C ++中,您可以省略struct关键字并使用标记名作为types名称:
struct MyStruct { int i; }; // The following is legal in C++: MyStruct obj; obj.i = 7;
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;
那究竟是什么意思呢?
在'C'编程语言中,关键字'typedef'用于为某个对象(结构,数组,函数声明types)声明一个新的名字。例如,我将使用'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.%di \n",number.real_part, number.img_part); }
每次我决定使用一个structtypes的时候,我会需要这个关键字'struct'这个东西''name'''typedef'会简单的重命名这个types,我可以在我的程序中每次使用这个新名字。是:
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'。
在C99中发现typedef是必需的。 它已经过时了,但是很多工具(ala HackRank)使用c99作为其纯C实现。 在那里需要typedef。
我不是说他们应该改变(也许有两个C选项),如果需求改变了,我们在网站上search的那些人将是SOL。
A> a typdef通过允许为数据types创build更有意义的同义词来帮助程序的含义和文档。 另外,他们帮助参数化一个程序来防止可移植性问题(K&R,pg147,C prog lang)。
B> 一个结构定义一个types 。 结构允许方便地处理一组variables(K&R,pg127,C prog lang。)作为一个单元
C> typedef'ing一个结构在上面的A中解释。
D>对我来说,结构是自定义types或容器或集合或名称空间或复杂types,而typdef只是创build更多昵称的手段。
在C语言中,struct / union / enum是由C语言预处理程序处理的macros指令(不要误认为处理“#include”等的预处理程序)
所以:
struct a { int i; }; struct b { struct a; int i; int j; };
结构b被扩展为这样的东西:
struct b { struct a { int i; }; int i; int j; }
所以,在编译时,它会像堆栈一样在栈上演变:b:int ai int i int j
这也是为什么有一个自反的结构是困难的,C预处理器在一个不能终止的解释循环中轮回。
typedef是types说明符,这意味着只有C编译器才能进行处理,并且可以像优化汇编代码一样执行。 它也不像前处理程序那样笨拙地使用结构体而是使用更复杂的参考构造algorithm,所以构造如下:
typedef struct a A; //anticipated declaration for member declaration typedef struct a //Implemented declaration { A* b; // member declaration }A;
是permited和function齐全。 当执行线程离开初始化函数的应用程序字段时,这个实现还可以访问编译器types转换并消除一些错误的影响。
这意味着在C中,typedef比C ++类更接近孤独的结构。