Why should we typedef a struct so often in C?
我看过许多由如下结构组成的程序
1 2 3 4 5 6 7 | typedef struct { int i; char k; } elem; elem user; |
为什么需要这么频繁?具体原因或适用范围?
正如GregHewgill所说,typedef意味着您不再需要在整个地方编写
类似的东西
1 2 3 4 5 6 7 8 9 10 11 | 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"的类型。我想,在江户记1〔0〕之后,情况是这样的。
另外请注意,虽然您的示例(和我的示例)省略了命名
1 2 3 | typedef struct Point Point; Point * point_new(int x, int y); |
然后在实现文件中提供
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 C,它needlessly pollutes命名空间,这是全球typically污染已经在非常大的C程序。
也,类型定义的结构没有一个tag name是一个主要的原因needless大学学院imposition序关系在头文件。
考虑:
1 2 3 4 5 6 7 8 9 10 11 12 | #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得到的
也,自是不同的命名空间的名称和标签之间的成员的名称,它是可能的两个读写的代码,如:
1 2 3 |
由于《命名空间是分开的,有没有冲突的coincident命名变量和他们的struct tag name。
如果想要保持你的代码,将消除你定义的结构。
来自丹·萨克斯的一篇老文章(http://www.ddj.com/cpp/184403396?PGNO=3):
The C language rules for naming
structs are a little eccentric, but
they're pretty harmless. However, when
extended to classes in C++, those same
rules open little cracks for bugs to
crawl through.In C, the name s appearing in
1
2
3
4 struct s
{
...
};is a tag. A tag name is not a type
name. Given the definition above,
declarations such as
1
2 s x; /* error in C */
s *p; /* error in C */are errors in C. You must write them
as
1
2 struct s x; /* OK */
struct s *p; /* OK */The names of unions and enumerations
are also tags rather than types.In C, tags are distinct from all other
names (for functions, types,
variables, and enumeration constants).
C compilers maintain tags in a symbol
table that's conceptually if not
physically separate from the table
that holds all other names. Thus, it
is possible for a C program to have
both a tag and an another name with
the same spelling in the same scope.
For example,
1 struct s s;is a valid declaration which declares
variable s of type struct s. It may
not be good practice, but C compilers
must accept it. I have never seen a
rationale for why C was designed this
way. I have always thought it was a
mistake, but there it is.Many programmers (including yours
truly) prefer to think of struct names
as type names, so they define an alias
for the tag using a typedef. For
example, defining
1
2
3
4
5 struct s
{
...
};
typedef struct s S;lets you use S in place of struct s,
as in
1
2 S x;
S *p;A program cannot use S as the name of
both a type and a variable (or
function or enumeration constant):
1 S S; // errorThis is good.
The tag name in a struct, union, or
enum definition is optional. Many
programmers fold the struct definition
into the typedef and dispense with the
tag altogether, as in:
1
2
3
4 typedef struct
{
...
} S;
链接文章还讨论了不要求EDCOX1×0的C++行为如何引起微妙的名称隐藏问题。为了防止这些问题,在C++中使用EDCOX1 0类和结构是一个好主意,尽管乍一看它似乎是不必要的。在C++中,使用EDCOX1(0),名称隐藏成为编译器告诉您的错误,而不是潜在问题的隐藏来源。
每次声明该类型的变量时,使用
1 2 3 4 5 6 7 | struct elem { int i; char k; }; elem user; // compile error! struct elem user; // this is correct |
另一个始终使用typedef枚举和结构的好原因是:
1 2 3 4 5 6 7 8 9 10 11 | enum EnumDef { FIRST_ITEM, SECOND_ITEM }; struct StructDef { enum EnuumDef MyEnum; unsigned int MyVar; } MyStruct; |
注意到结构(enuumdef)中enumdef的拼写错误吗?编译时没有错误(或警告),并且(取决于C标准的文字解释)是正确的。问题是我刚刚在结构中创建了一个新的(空)枚举定义。我没有(按预期)使用前面的定义enumdef。
如果使用typdef,类似类型的拼写错误将导致使用未知类型的编译器错误:
1 2 3 4 5 6 7 8 9 10 11 12 | 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的结构和枚举。
不仅要保存一些类型(不打算使用双关语;),而且因为它更安全。
Linux内核提供的编码风格的第五大缺点是鸭(mostly缺点)
Please don't use things like"vps_t".
It's a mistake to use typedef for structures and pointers. When you see a
1 vps_t a;in the source, what does it mean?
In contrast, if it says
1 struct virtual_container *a;you can actually tell what"a" is.
Lots of people think that typedefs"help readability". Not so. They are useful only for:
(a) totally opaque objects (where the typedef is actively used to hide what the object is).
Example:"pte_t" etc. opaque objects that you can only access using the proper accessor functions.
NOTE! Opaqueness and"accessor functions" are not good in themselves. The reason we have them for things like pte_t etc. is that there really is absolutely zero portably accessible information there.
(b) Clear integer types, where the abstraction helps avoid confusion whether it is"int" or"long".
u8/u16/u32 are perfectly fine typedefs, although they fit into category (d) better than here.
NOTE! Again - there needs to be a reason for this. If something is"unsigned long", then there's no reason to do
1 typedef unsigned long myflags_t;but if there is a clear reason for why it under certain circumstances might be an"unsigned int" and under other configurations might be"unsigned long", then by all means go ahead and use a typedef.
(c) when you use sparse to literally create a new type for type-checking.
(d) New types which are identical to standard C99 types, in certain exceptional circumstances.
Although it would only take a short amount of time for the eyes and brain to become accustomed to the standard types like 'uint32_t', some people object to their use anyway.
Therefore, the Linux-specific 'u8/u16/u32/u64' types and their signed equivalents which are identical to standard types are permitted -- although they are not mandatory in new code of your own.
When editing existing code which already uses one or the other set of types, you should conform to the existing choices in that code.
(e) Types safe for use in userspace.
In certain structures which are visible to userspace, we cannot require C99 types and cannot use the 'u32' form above. Thus, we use __u32 and similar types in all structures which are shared with userspace.
Maybe there are other cases too, but the rule should basically be to NEVER EVER use a typedef unless you can clearly match one of those rules.
In general, a pointer, or a struct that has elements that can reasonably be directly accessed should never be a typedef.
我认为可能是平衡式的声明和定义。使用struct,枚举,和联合代理声明为allow(知道什么时候dependencies)是双向的。
风格: 使用typedef在C + +,使很长一点道理的。它可以几乎是必要的,当处理与模板和/或需要多变量的参数。《保持helps定义命名的直。
没有,所以在C编程语言。务必使用typedef罐头或没有用途,但两obfuscate《数据结构的使用。由于只读(struct枚举(6),(4),(5)}的联盟keystrokes号码是使用DECLARE A的数据类型有几乎没有使用的混叠的struct。数据类型是A或A struct联盟?以《straightforward非typdefed声明让你知道了什么是它的类型。
冰方书面通知Linux与严格避免这种类型的brings走样的废话。结果是一个一个极简主义者,干净的风格。
事实证明,这其中有利弊。一个有用的信息来源是开创性的书"专家C编程"(第3章)。简而言之,在C中有多个名称空间:标记、类型、成员名称和标识符。
1 2 3 | typedef struct Tag{ ...members... }Type; |
定义两件事。标记命名空间中的一个标记和类型命名空间中的一个类型。所以你可以同时做
1 | typedef Type *Type_ptr; |
我们定义了指向类型的指针。因此,如果我们声明:
1 2 | Type_ptr var1, var2; struct Tag *myTagType1, myTagType2; |
那么,
在上面提到的书中,它提到typedefing结构并不是很有用,因为它只会避免程序员编写单词struct。但是,我有一个反对意见,就像其他许多C程序员一样。虽然有时它会使一些名称变得模糊(这就是为什么在像内核这样的大型代码库中不可取的原因),但当您希望在C中实现多态性时,它有助于在这里查找详细信息。例子:
1 2 3 4 5 6 | typedef struct MyWriter_t{ MyPipe super; MyQueue relative; uint32_t flags; ... }MyWriter; |
你可以做到:
1 2 3 4 5 6 | void my_writer_func(MyPipe *s) { MyWriter *self = (MyWriter *) s; uint32_t myFlags = self->flags; ... } |
因此,您可以通过内部结构(
最后,与宏相比,
1 2 | #define X char[10] or typedef char Y[10] |
然后你可以申报
1 2 | unsigned X x; but not unsigned Y y; |
对于结构,我们并不真正关心这一点,因为它不适用于存储说明符(
让我们从最基本的开始,一路往上走。
以下是结构定义的示例:
1 2 3 4 | struct point { int x, y; }; |
这里,名称
结构可以在定义期间或之后声明。
在定义期间声明
1 2 3 4 | struct point { int x, y; } first_point, second_point; |
定义后声明
1 2 3 4 5 | struct point { int x, y; }; struct point first_point, second_point; |
现在,请仔细注意上面的最后一个案例;如果您决定稍后在代码中创建该类型,则需要编写
输入
1 2 3 4 5 6 | typedef struct point { int x, y; } Points; Points first_point, second_point; |
命名自定义类型时要注意一点
没有什么可以阻止您在自定义类型名称的末尾使用后缀,但是POSIX标准保留使用后缀来表示标准库类型名称。
您(可选)为结构指定的名称称为标记名,正如前面提到的,它本身不是一个类型。要获取类型,需要结构前缀。
除了GTK+之外,我不确定TAGNORE使用的任何东西都是与Type类型的Type一样,所以在C++中被识别,并且可以省略Strut关键字并使用TAGNOTY作为类型名称:
1 2 3 4 5 6 7 8 9 10 | <wyn> struct MyStruct { int i; }; // The following is legal in C++: MyStruct obj; obj.i = 7; </wyn> |
A>typedef通过允许为数据类型创建更有意义的同义词来帮助程序的意义和文档编制。此外,它们还帮助参数化程序,防止出现可移植性问题(K&R、PG147、C Prog Lang)。
B>结构定义类型。结构允许对var集合进行方便的分组,以便将(k&r、pg127、c prog lang)作为单个单元进行处理。
C>结构的typedef在上面的a中进行了解释。
对我来说,结构是自定义类型、容器、集合、名称空间或复杂类型,而typdef只是创建更多昵称的一种方法。
typedef将不提供一组相互依赖的数据结构。您不能使用typedef:
1 2 3 4 5 6 7 8 9 10 | struct bar; struct foo; struct foo { struct bar *b; }; struct bar { struct foo *f; }; |
当然,您可以随时添加:
1 2 | typedef struct foo foo_t; typedef struct bar bar_t; |
这到底有什么意义?
在c99中,typedef是必需的。它已经过时了,但是很多工具(ala-hackrank)使用c99作为其纯C实现。这里需要typedef。
我不是说他们应该改变(可能有两个C选项)如果要求改变,我们那些在网站上为面试而学习的人将是索尔。
在"c"编程语言中,关键字"typedef"用于为某些对象(结构、数组、函数..枚举类型)声明新名称。例如,我将使用"struct-s"。在"c"中,我们经常在"main"函数之外声明"struct"。例如:
1 2 3 4 5 6 7 8 9 10 11 | 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 ",number.real_part, number.img_part); } |
每次我决定使用一个结构类型时,我都需要这个关键字"struct"something""name""。"typedef"只需重命名该类型,我就可以每次在程序中使用这个新名称。所以我们的代码是:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 ", number.real_part, number.img_part); } |
如果您有一些将在整个程序中使用的本地对象(结构、数组、有价值的对象),您只需使用"typedef"为其命名即可。
全,在C语言中,struct /联合/枚举是宏指令处理的预处理程序(C语言没有错误处理,与"治疗"# include"和其他)
SO:
1 2 3 4 5 6 7 8 9 10 11 | struct a { int i; }; struct b { struct a; int i; int j; }; |
struct B冰expended AS这样的东西:
1 2 3 4 5 6 7 8 9 | struct b { struct a { int i; }; int i; int j; } |
所以,在编译时计算堆栈作为进化之类的东西。 B: 国际人工智能 在国际 国际杂志
这是为什么它的也有两个dificult selfreferent C预处理器结构,在一个déclaration方位回路,不能terminate。
类型是类型说明符,均值,只有C编译器处理它,它可以为他做的那样optimise汇编代码的执行。它也不expend成员类型对大学做的一préprocessor stupidly样结构,但使用更复杂的算法参考建设,社会建设类:
1 2 3 4 5 6 | typedef struct a A; //anticipated declaration for member declaration typedef struct a //Implemented declaration { A* b; // member declaration }A; |
冰可以和全功能。这也给执行存取的两种类型的转换compilator and remove bugging一些特效的执行线程离开的时候initialisation函数的应用领域。
均在C,这是更近的typedef为C++类比孤独的结构体。