我是C编程的初学者,但我想知道在定义结构时使用typedef与不使用typedef之间有什么区别。在我看来,这两者并没有什么区别,它们实现的目标是一样的。

struct myStruct{
    int one;
    int two;
};

vs.

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

当前回答

在C语言中,结构、联合和枚举的类型说明关键字是强制性的,也就是说,当引用类型时,你总是必须在类型名(它的标记)前面加上struct、联合或enum。

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

因此建议(参见Linux内核编码风格指南,第5章)只在以下情况下这样做 实际上,您希望隐藏这些信息,而不仅仅是为了节省一些按键。

你应该使用typedef的一个例子是不透明类型,它只与相应的访问器函数/宏一起使用。

其他回答

我看需要澄清一下。C和c++没有不同的类型定义。c++最初不过是C之上的一组附加的包含。

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

这样的声明和定义存在的唯一原因是链接器可以计算到结构中字段的地址偏移量。这就是为什么大多数人使用实际上写得不正确的代码——因为编译器能够确定寻址。当有人试图提前做一些事情,比如队列、链表或承载O/S结构时,问题就出现了。

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

此外,结构体具有前向声明标签和已定义标签。大多数人不知道这一点,而是将forward声明标签用作定义标签。

错误的:

struct myStruct
   {
   int field_1;
   ...
   };

它们只是使用forward声明来标记结构——所以现在编译器知道它了——但它不是实际定义的类型。编译器可以计算寻址——但这不是它打算使用的方式,原因我马上会说明。

使用这种形式的声明的人,必须在几乎所有的引用中都加上“struct”——因为它不是一个正式的新类型。

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

typedef struct
   {
   field_1;
   ...
   }myStruct;

现在它是一个实际的类型,当使用时,你可以使用at作为'myStruct',而不必在它前面加上'struct'这个词。

如果你想要一个指向该结构的指针变量,那么包括一个二级标签:

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

现在你有了一个指向该结构的指针变量,自定义的。

前置声明——

现在,这里有一些有趣的东西,forward声明是如何工作的。如果希望创建引用自身的类型,如链表或队列元素,则必须使用前向声明。编译器不会考虑定义的结构,直到它到达最后的分号,所以它只是在这一点之前声明。

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

现在,编译器知道,尽管它还不知道整个类型是什么,但它仍然可以使用前向引用引用它。

请正确地声明和定义结构类型。这其实是有原因的。

另一个没有指出的区别是,给struct一个名称(即struct myStruct)也允许你提供结构的前向声明。所以在其他文件中,你可以这样写:

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

而不需要知道定义。我建议你把这两个例子结合起来:

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

这为您提供了更简洁的类型定义名称的便利,但如果需要,仍然允许您使用完整的结构名称。

在C语言中,结构、联合和枚举的类型说明关键字是强制性的,也就是说,当引用类型时,你总是必须在类型名(它的标记)前面加上struct、联合或enum。

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

因此建议(参见Linux内核编码风格指南,第5章)只在以下情况下这样做 实际上,您希望隐藏这些信息,而不仅仅是为了节省一些按键。

你应该使用typedef的一个例子是不透明类型,它只与相应的访问器函数/宏一起使用。

常用的习语是两者都用:

typedef struct S { 
    int x; 
} S;

它们是不同的定义。为了使讨论更清楚,我将把句子分开:

struct S { 
    int x; 
};

typedef struct S S;

在第一行中,您在结构名称空间(不是c++意义上的)中定义标识符S。你可以使用它并通过将参数的类型定义为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'

当标识符保存在不同的空格中时,可以定义与结构同名的函数,但当标识符冲突时,不能定义与typedef同名的函数。

在c++中,它略有不同,因为定位符号的规则发生了微妙的变化。c++仍然保留了两个不同的标识符空间,但与C不同的是,当你只在类标识符空间中定义符号时,你不需要提供struct/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;
}

对于后一个示例,在使用结构时省略了struct关键字。所以在你的代码中,你可以这样写:

myStruct a;

而不是

struct myStruct a;

这节省了一些输入,并且可能更易于阅读,但这是一个品味问题