我见过许多由如下结构组成的程序
typedef struct
{
int i;
char k;
} elem;
elem user;
为什么如此频繁地需要它?有什么具体原因或适用领域吗?
我见过许多由如下结构组成的程序
typedef struct
{
int i;
char k;
} elem;
elem user;
为什么如此频繁地需要它?有什么具体原因或适用领域吗?
使用typedef可以避免每次声明该类型的变量时都要写struct:
struct elem
{
int i;
char k;
};
elem user; // compile error!
struct elem user; // this is correct
正如Greg Hewgill所说,类型定义意味着你不再需要到处写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之后,我猜就是这个情况。
还要注意,虽然您的示例(和我的示例)省略了对结构体本身的命名,但当您希望提供不透明类型时,实际上对它进行命名也是有用的。然后你会在头文件中有这样的代码,例如:
typedef struct Point Point;
Point * point_new(int x, int y);
然后在实现文件中提供结构定义:
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内核可能是最著名的这样的项目。关于Linus的愤怒言论,请参阅Linux内核编码风格文档的第5章。:)我的观点是,这个问题中的“应该”也许并不是一成不变的。
您(可选地)给结构的名称称为标记名称,正如前面所指出的,它本身并不是一个类型。要获得该类型,需要使用结构体前缀。
除了GTK+,我不确定tagname是否像struct类型的typedef一样常用,所以在c++中,这是可以识别的,你可以省略struct关键字,并使用tagname作为类型名:
struct MyStruct
{
int i;
};
// The following is legal in C++:
MyStruct obj;
obj.i = 7;
摘自Dan Saks的一篇旧文章(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 struct s { ... }; is a tag. A tag name is not a type name. Given the definition above, declarations such as s x; /* error in C */ s *p; /* error in C */ are errors in C. You must write them as 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, 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 struct s { ... }; typedef struct s S; lets you use S in place of struct s, as in S x; S *p; A program cannot use S as the name of both a type and a variable (or function or enumeration constant): S S; // error This 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: typedef struct { ... } S;
这篇链接的文章还讨论了不需要类型定义的c++行为是如何导致微妙的名称隐藏问题的。为了防止这些问题,在c++中对类和结构进行类型定义也是一个好主意,尽管乍一看似乎没有必要。在c++中,有了类型定义,隐藏的名称就变成了编译器告诉你的错误,而不是潜在问题的隐藏来源。
另一个总是对枚举和结构进行类型定义的原因是:
enum EnumDef
{
FIRST_ITEM,
SECOND_ITEM
};
struct StructDef
{
enum EnuumDef MyEnum;
unsigned int MyVar;
} MyStruct;
注意到结构(EnumDef)中的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类型定义结构和枚举。
不仅是为了节省一些输入(没有双关语;)),而且因为它更安全。
在C语言中,struct/union/enum是由C语言预处理器处理的宏指令(不要与处理“#include”和other的预处理器混淆)
so :
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人工智能 int我 int j
这也是为什么在不能终止的déclaration循环中很难有自不同的结构,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;
是允许的,功能齐全。当执行线程离开初始化函数的应用程序字段时,这个实现还允许访问编译器类型转换,并删除一些bug影响。
这意味着在C中,typedef比单独的结构体更接近c++类。
我认为typedef甚至不可能实现前向声明。使用struct, enum和union允许在依赖关系(知道)是双向的情况下转发声明。
风格: 在c++中使用typedef是很有意义的。在处理需要多个和/或变量参数的模板时,它几乎是必要的。typedef有助于保持命名的直直性。
在C编程语言中并非如此。typedef的使用通常没有任何目的,只会混淆数据结构的使用。由于只有{struct (6), enum (4), union(5)}个按键被用于声明一个数据类型,因此结构的别名几乎没有任何用处。数据类型是联合还是结构?使用简单的非类型定义声明可以让您立即知道它是什么类型。
请注意Linux是如何在编写时严格避免typedef带来的这种无意义的别名的。结果是一个极简主义和干净的风格。
令人惊讶的是,很多人都错了。请不要在C中定义结构类型,它不必要地污染全局命名空间,在大型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,编译单元可以包含foo.h来获得FOO_DEF定义。如果它不试图解引用foo结构体的'bar'成员,那么就不需要包含"bar.h"文件。
此外,由于标签名和成员名之间的名称空间是不同的,因此可以编写非常可读的代码,例如:
struct foo *foo;
printf("foo->bar = %p", foo->bar);
由于名称空间是分开的,因此在命名变量时与其struct标记名一致时不存在冲突。
如果我必须维护你的代码,我会删除你的类型定义结构。
Linux内核编码风格第5章给出了使用typedef的优点和缺点(主要是缺点)。
Please don't use things like "vps_t". It's a mistake to use typedef for structures and pointers. When you see a vps_t a; in the source, what does it mean? In contrast, if it says 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 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.
一个有用的信息来源是《Expert C Programming》(第3章)。简单地说,在C语言中你有多个命名空间:标记、类型、成员名和标识符。Typedef为类型引入别名,并将其定位在标记命名空间中。也就是说,
typedef struct Tag{
...members...
}Type;
定义了两个东西。1)在标签命名空间中标记,2)在类型命名空间中键入。你可以同时使用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;
...
}
因此,您可以通过强制转换通过内部结构(mpipe)访问外部成员(flags)。对我来说,强制转换整个类型比执行(struct MyWriter_ *) s;每次你想执行这样的功能。在这种情况下,简要引用是很重要的,特别是当你在代码中大量使用这种技术时。
最后,与宏相比,类型定义类型的最后一个方面是无法扩展它们。例如,你有:
#define X char[10] or
typedef char Y[10]
然后你可以声明
unsigned X x; but not
unsigned Y y;
对于结构体,我们并不关心这一点,因为它不适用于存储说明符(volatile和const)。
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'用于为某些对象(struct, array, function..enum类型)声明一个新名称。例如,我将使用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'。
在C99中,typedef是必需的。它已经过时了,但是很多工具(比如HackRank)使用c99作为它的纯C实现。这里需要typedef。
我不是说他们应该改变(也许有两个C选项),如果要求改变,我们这些在网站上学习面试的人将是SOL。
一个> typdef允许为数据类型创建更有意义的同义词,从而帮助定义程序的含义和文档化。此外,它们还有助于参数化程序以解决可移植性问题(K&R, pg147, C prog lang)。
B > 结构定义了一种类型。Structs允许方便地将变量集合分组,以便将(K&R, pg127, C prog lang.)作为单个单元处理
C > 结构的类型定义在上面的a中有解释。
对我来说,struct是自定义类型、容器、集合、命名空间或复杂类型,而typdef只是一种创建更多昵称的方法。
让我们从最基本的开始,慢慢来。
下面是一个结构定义的例子:
struct point
{
int x, y;
};
这里的名称点是可选的。
结构可以在定义期间声明,也可以在定义之后声明。
在定义期间声明
struct point
{
int x, y;
} first_point, second_point;
在定义之后声明
struct point
{
int x, y;
};
struct point first_point, second_point;
现在,仔细注意上面的最后一种情况;如果您决定稍后在代码中创建该类型,则需要编写struct point来声明该类型的结构。
输入类型。如果您打算在稍后使用相同的蓝图在程序中创建新的Structure (Structure是自定义数据类型),那么在定义过程中使用typedef可能是一个好主意,因为您可以节省一些输入。
typedef struct point
{
int x, y;
} Points;
Points first_point, second_point;
在命名自定义类型时要注意一点
没有什么可以阻止您在自定义类型名称的末尾使用_t后缀,但POSIX标准保留使用后缀_t来表示标准库类型名称。