我看过很多程序,其中包括以下结构
typedef struct
{
int i;
char k;
} elem;
elem user;
为什么经常需要它? 有任何特定原因或适用范围?
#1楼
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。
#2楼
事实证明,这是有利有弊的。 有用的信息来源是开创性的书“ Expert C Programming”( 第3章 )。 简而言之,在C中,您具有多个名称空间: 标签,类型,成员名称和标识符 。 typedef为类型引入别名,并将其定位在标记名称空间中。 即
typedef struct Tag{
...members...
}Type;
定义了两件事。 标签名称空间中的一个标签,类型名称空间中的一个类型。 所以,你都可以做Type myType和struct Tag myTagType 。 诸如struct Type myType Tag myTagType或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;
...
}
因此,您可以通过强制转换通过内部结构( MyPipe )访问外部成员( flags )。 对我来说, (struct MyWriter_ *) s;整个类型比做(struct MyWriter_ *) s;容易混淆(struct MyWriter_ *) s; 每次您想要执行此类功能时。 在这些情况下,简短引用是一件很重要的事情,特别是如果您在代码中大量使用该技术的话。
最后,随着最后一个方面typedef ED类型是无法扩展它们,而相比之下,宏。 例如,如果您有:
#define X char[10] or
typedef char Y[10]
然后您可以声明
unsigned X x; but not
unsigned Y y;
我们并不真正在意此结构,因为它不适用于存储说明符( volatile和const )。
#3楼
完全,在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 ++类,而不是孤独的结构。
#4楼
使用typedef可以避免每次声明该类型的变量时都必须编写struct :
struct elem
{
int i;
char k;
};
elem user; // compile error!
struct elem user; // this is correct
#5楼
正如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章。 :)我的意思是,毕竟问题中的“应该”可能不是一成不变的。