2009-11-04 123 views
591

我是C编程的初学者,但我想知道在定义结构时使用typedef与不使用typedef有何区别。在我看来,实际上没有什么区别,他们完成了同样的任务。typedef结构与结构定义

struct myStruct{ 
    int one; 
    int two; 
}; 

typedef struct{ 
    int one; 
    int two; 
}myStruct; 
+0

我刚刚在这里阅读,第二个选项会给编译器错误?! “传递不兼容的指针类型的参数”http://stackoverflow.com/questions/12708897/typedefs-struct-tag-and-alias – hewi 2015-12-18 16:10:08

回答

769

常见成语是同时使用:

typedef struct X { 
    int x; 
} X; 

它们是不同的定义。为了使讨论更加清楚我会分裂了一句:

struct S { 
    int x; 
}; 

typedef struct S S; 

在第一行要定义的结构命名空间中的标识符S(而不是在C++意义上的)。您可以使用它,并通过定义为struct S参数的类型来定义新定义类型的变量或函数参数:

void f(struct S argument); // struct is required here 

第二行增加了一个类型别名S在全局命名空间,从而允许您只写:

void f(S argument); // struct keyword no longer needed 

注意,因为这两个标识符名称空间是不同的,定义S无论是在结构和全球空间是不是一个错误,因为它不是重新定义相同的标识符,而是创造了一个不同的标识符一个不同的地方。

赚取差价更清晰:

typedef struct S { 
    int x; 
} T; 

void S() { } // correct 

//void T() {} // error: symbol T already defined as an alias to 'struct S' 

您可以定义与结构的名称相同的功能的标识都保存在不同的空间,但你不能用相同的名称定义一个函数当这些标识符发生碰撞时。

在C++中,它稍有不同,因为定位符号的规则已经微妙地改变了。 C++仍保持着两个不同的标识符空间,但与C,当你只定义在类标识符空间内的符号,你是不是需要提供结构/ class关键字:

// C++ 
struct S { 
    int x; 
}; // S defined as a class 

void f(S a); // correct: struct is optional 

是什么改变搜索规则,而不是定义标识符的位置。编译器将搜索全局标识符表,并且在S尚未找到之后,它将在类标识符内搜索S

以同样的方式的行为之前给出的代码:

typedef struct S { 
    int x; 
} T; 

void S() {} // correct [*] 

//void T() {} // error: symbol T already defined as an alias to 'struct S' 

在第二行中的S函数的定义之后,结构s不能是由编译器自动解决,并创建一个对象或定义类型的参数必须退回到包括struct关键字:

// previous code here... 
int main() { 
    S(); 
    struct S s; 
} 
+12

伟大的答案显示我也为什么我想要typedef,所以它不能被覆盖作为一个函数,谢谢:)) – 2013-07-20 12:41:42

+7

@AlexanderVarwijk:你想'typedef',以避免需要结构'或'enum' 。如果您使用的命名约定允许使用相同名称的函数和类型,那么您可以做的最好的事情就是检查如何命名程序的元素。 – 2013-07-20 18:45:14

+1

我的命名约定应该确保不会发生,比对不起更安全:) – 2013-07-21 12:56:43

4

typedef,因为它与其它构建体中,用来给一个数据类型的新名称。在这种情况下,它是为了使代码更清洁主要完成:

struct myStruct blah; 

myStruct blah; 
48

在C(不是C++),你必须声明结构变量,例如:

struct myStruct myVariable; 

为了能够使用myStruct myVariable;相反,你可以typedef的结构:

typedef struct myStruct someStruct; 
someStruct myVariable; 

您可以结合struct定义和typedef的IT在其中宣布匿名structtypedef的IT一条语句。

typedef struct { ... } myStruct; 
+1

最后一块代码不等同于前面的代码。在最后一行中,您将一个类型别名'myStruct'定义到一个未命名的结构中。两个版本之间存在(非常)细微的差别。 – 2009-11-04 17:52:40

+4

dribeas:我在句子“......一个声明**匿名结构**和......”的单句中说明了这个微妙的区别 – 2009-11-04 18:10:33

+1

真的,我似乎跳过了那部分:) – 2009-11-05 07:27:36

5

当您使用struct时区别进来。

你必须做的第一种方式:

struct myStruct aName; 

第二种方法允许你删除关键字struct

myStruct aName; 
19

如果使用structtypedef,你总能写

struct mystruct myvar; 

这是非法的,如果您使用typedef你不需要写

mystruct myvar; 

struct前缀了。

+1

给点答案。 – 2017-06-03 14:41:51

1

对于后面的示例,在使用结构时省略了struct关键字。因此,在你的代码随处可见,你可以这样写:

myStruct a; 

,而不是

struct myStruct a; 

这节省一些打字,而可能是更具可读性,但是这是一个品味问题

20

在C的结构,联合类型说明符关键字和所列举的都是强制性的,即你总是有前缀的TY pe的名称(其标签)与struct,unionenum在引用类型时。

您可以通过使用typedef来摆脱关键字,这是一种信息隐藏形式,因为在声明对象时实际类型将不再可见。

因此建议(见例如Linux kernel coding style guide,第5章)只有当 你真正想要隐藏这个信息,不只是为了节省几个按键做到这一点。

的时候应该使用一个typedef将其永远只与相应的存取器函数/宏使用不透明类型的一个例子。

+2

Linux内核编码风格指南供参考:http://www.kernel.org/doc/Documentation/CodingStyle – 2010-07-21 20:45:37

+2

Linux内核编码风格文档已被移至https://www.kernel.org/doc/Documentation/process /coding-style.rst – 2017-05-11 10:48:56

82

另一个不同之处不指出的是,给结构的名称(即结构MYSTRUCT),您还可以提供前进的结构的声明。因此,在其他文件中,您可以编写:

struct myStruct; 
void doit(struct myStruct *ptr); 

而不必访问定义。我的建议是你把你的两个例子:

typedef struct myStruct{ 
    int one; 
    int two; 
} myStruct; 

这给你一个更简洁的类型名字的便利,但仍允许您如果需要使用完整的结构名称。

+1

有一些方法可以用typedef做前向声明:'typedef struct myStruct myStruct;';然后(稍后):'struct myStruct {...};'。你可以在'typedef'之后使用它* * struct myStruct' *或者* myStruct'(但是类型是不完整的,直到定义)。 – 2015-02-10 14:20:28

+0

值得一提的是,尽管(概念上)自动输入了定义标签并在单个“符号空间”中放置了“SomeThing”和“Something”,但C++确实为手动typedef SomeThing重新定义了明确的限制到'struct SomeThing' ... ...否则你可能会认为这会产生一个关于碰撞名称的错误。资料来源:http://stackoverflow.com/a/22386307/2757035我想这是完成的(稍微徒劳!)向后兼容的原因。 – 2016-04-09 16:52:02

+0

@underscore_d:当时,人们认识到代码的价值可以与C和C++一样好,并且希望避免在兼容性方面造成不必要的障碍。可悲的是,这种想法已不再流行。 – supercat 2017-04-15 20:53:15

4

下面的代码与别名myStruct创建一个匿名结构:

typedef struct{ 
    int one; 
    int two; 
} myStruct; 

您不能引用它,而不别名,因为你不指定结构的标识符。

4

不能使用向前声明与typedef struct

struct本身就是一个匿名类型,所以你不要有一个实际的名称转发申报。

typedef struct{ 
    int one; 
    int two; 
} myStruct; 

像这样的向前声明将无法工作:

struct myStruct; //forward declaration fails 

void blah(myStruct* pStruct); 

//error C2371: 'myStruct' : redefinition; different basic types 
70

structtypedef是两个完全不同的事情。

struct关键字用来定义,或指代,结构类型。例如:

struct foo { 
    int n; 
}; 

创建一个名为struct foo的新类型。名称foo标签;这是有意义的,只是当它立即被struct关键字之前,因为标签和其他标识是在不同的的名称空间。 (这与namespace的C++概念相似但限制更多)。

尽管名称存在,但它并没有定义新的类型;它仅为现有类型创建一个新名称。例如,给定:

typedef int my_int; 

my_intint一个新的名字; my_intint都是正好是是同一类型。同样,鉴于上述struct定义,你可以写:

typedef struct foo foo; 

类型已经有一个名字,struct footypedef声明给出了相同类型的新名称foo

的语法允许一个structtypedef合并成一个单一声明:

typedef struct bar { 
    int n; 
} bar; 

这是一个常见的成语。现在你可以参考这个结构类型struct bar或只是bar

请注意,typedef名称在声明结束前不可见。如果结构包含一个指向自身,则必须使用struct版本来参考一下吧:

typedef struct node { 
    int data; 
    struct node *next; /* can't use just "node *next" here */ 
} node; 

一些程序员会使用不同的标识符的结构标记和typedef名。在我看来,这没有什么好的理由。使用相同的名称是完全合法的,并且使得它们更清楚,它们是相同的类型。如果必须使用不同的标识符,至少使用一个一致的约定:

typedef struct node_s { 
    /* ... */ 
} node; 

(就个人而言,我更喜欢省略typedef并参考类型struct bartypedef节省一点打字,但它隐藏了一个事实如果你希望类型是不透明的,这可能是一件好事,如果客户端代码是通过名字引用成员n,那么它不是不透明的;它显然是一个结构,在我的意见认为它是一种结构,但很多聪明的程序员在这一点上不同意我的观点,请准备阅读并理解以任何一种方式编写的代码。)

(C++有不同的规则。给定struct blah的声明,即使没有typedef,也可以将其类型称为blah。使用typedef可能会让你的C代码更像C++ - 如果你认为这是一件好事。)

+2

这个答案帮助我更好地理解了为什么C89库和Linux内核更可能使用'struct mystruct_t {}'而不是'typedef struct {} mystruct_t'。 – 2016-07-25 13:05:51

2

我看到一些澄清是为了这个。 C和C++不会以不同的方式定义类型。 C++本来只不过是C上的一个附加集合。

几乎所有C/C++开发人员今天都有这样的问题:a)大学不再教授基础知识,b)人们不理解定义和声明之间的区别。

此类声明和定义存在的唯一原因是,链接器可以计算结构中字段的地址偏移量。这就是为什么大多数人摆脱了实际写入不正确的代码 - 因为编译器能够确定寻址。当有人试图进行某些事情时会出现问题,例如队列或链接列表,或者支持O/S结构。

声明以'struct'开头,定义以'typedef'开始。

另外,结构体具有前向声明标签和定义的标签。大多数人不知道这一点,并使用前向声明标签作为定义标签。

错误:

struct myStruct 
    { 
    int field_1; 
    ... 
    }; 

他们只需要使用向前声明标注的结构 - 所以现在的编译器知道它 - 但它不是一个实际的定义类型。编译器可以计算寻址 - 但这不是它打算如何使用的原因,因为我将暂时显示。

使用这种形式的声明的人必须在实际上每次引用它时都必须使用“struct” - 因为它不是一种官方的新类型。

相反,不引用本身的任何结构,应该声明和定义只有这样:

typedef struct 
    { 
    field_1; 
    ... 
    }myStruct; 

现在,它是一个真正的类型,使用的时候可以在为“MYSTRUCT”使用,而不必用“结构”这个词加上它。

如果你想有一个指针变量到结构,则包括二次标签:

typedef struct 
    { 
    field_1; 
    ... 
    }myStruct,*myStructP; 

现在你有一个指针变量到结构,习惯它。

FORWARD DECLARATION--

现在,这里的花哨的东西,向前声明是如何工作的。如果你想创建一个引用自身的类型,比如链接列表或者队列元素,你必须使用前向声明。编译器不考虑定义的结构,直到它到达最后的分号,所以它只是在该点之前声明。现在

typedef struct myStructElement 
    { 
    myStructElement* nextSE; 
    field_1; 
    ... 
    }myStruct; 

,编译器知道,虽然它不知道整个类型是什么呢,它仍然可以使用向前参考引用它。

请正确声明和typedef你的结构。其实有一个原因。

+4

我不相信C++曾经是“一系列包含在C之上的东西”。第一个实现是cfront,它是一个将C++源代码翻译为C源代码的预处理器。我不同意你对结构使用typedefs的建议,特别是针对指针。在typedef后面隐藏指针类型可能很危险;它更好的恕我直言,使事实,这是一个指针明确使用'*'。在你最后的例子中,我很好奇使用typedef的一个名字('myStruct')和struct标签的另一个名字('myStructElement')。 – 2016-05-07 21:44:22

+2

这很大程度上是一个文体偏好问题。我对你的大部分建议(这并不是说你错了)相当不满。请参阅[我的答案](http://stackoverflow.com/a/26389105/827263),了解我自己的偏好摘要(许多C程序员都共享这些偏好,但当然不是全部)。 – 2016-05-07 21:45:22