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

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

elem user;

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


当前回答

令人惊讶的是,很多人都错了。请不要在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标记名一致时不存在冲突。

如果我必须维护你的代码,我会删除你的类型定义结构。

其他回答

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

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

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

另一个总是对枚举和结构进行类型定义的原因是:

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类型定义结构和枚举。

不仅是为了节省一些输入(没有双关语;)),而且因为它更安全。

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

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

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