我见过许多由如下结构组成的程序

typedef struct 
{
    int i;
    char k;
} elem;

elem user;

为什么如此频繁地需要它?有什么具体原因或适用领域吗?


当前回答

一个有用的信息来源是《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)。

其他回答

正如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章。:)我的观点是,这个问题中的“应该”也许并不是一成不变的。

让我们从最基本的开始,慢慢来。

下面是一个结构定义的例子:

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来表示标准库类型名称。

令人惊讶的是,很多人都错了。请不要在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.

在'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'。