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

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标记名一致时不存在冲突。

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

其他回答

在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++类。

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

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

摘自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++中,有了类型定义,隐藏的名称就变成了编译器告诉你的错误,而不是潜在问题的隐藏来源。

您(可选地)给结构的名称称为标记名称,正如前面所指出的,它本身并不是一个类型。要获得该类型,需要使用结构体前缀。

除了GTK+,我不确定tagname是否像struct类型的typedef一样常用,所以在c++中,这是可以识别的,你可以省略struct关键字,并使用tagname作为类型名:

struct MyStruct
{
  int i;
};

// The following is legal in C++:
MyStruct obj;
obj.i = 7;

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