这里发生了几件事。首先,正如其他人所说,编译器对未知类型的抱怨可能是因为您需要在使用它们之前声明类型。但更重要的是理解三件事的语法:
- 结构体类型的定义,
- 结构变量的定义和声明,以及
- typedef
(请注意,在 C 编程语言中,定义和声明通常同时发生,因此本质上是相同的。在许多其他语言中情况并非如此。有关更多详细信息,请参阅下面的脚注。)
定义结构体时,该结构体可以被标记(命名),也可以不带标记(如果不带标记,则必须立即使用该结构体(下面将进一步解释这意味着什么))。
struct Name {
...
};
这定义了一个名为“struct Name”的类型,然后可以使用它来定义结构变量/实例:
struct Name myNameStruct;
这定义了一个名为的变量myNameStruct
这是一个类型的结构体struct Name
.
您还可以定义一个结构体,并同时声明/定义一个结构体变量:
struct Name {
...
} myNameStruct;
和以前一样,这定义了一个名为的变量myNameStruct
这是类型的实例struct Name
... 但它同时定义了类型 struct Name
.
然后可以再次使用该类型来声明和定义另一个变量:
struct Name myOtherNameStruct;
Now typedef
只是用特定名称为类型别名的一种方法:
typedef OldTypeName NewTypeName;
考虑到上面的 typedef,任何时候你使用NewTypeName
它与使用相同OldTypeName
. 在 C 编程语言中,这对于结构体特别有用,因为它使您能够在声明和定义该类型的变量时省略“结构体”一词并将结构体的名称简单地视为其本身的类型(就像我们在 C++ 中所做的那样)。下面是一个首先定义结构体,然后对结构体进行 typedef 的示例:
struct Name {
...
};
typedef struct Name Name_t;
上面的OldTypeName是struct Name
和 NewTypeName 是Name_t
。所以现在,定义一个 struct Name 类型的变量,而不是写:
struct Name myNameStruct;
我可以简单地写:
Name_t myNameStruct;
另请注意,typedef 可以与结构定义组合,这就是您在代码中所做的事情:
typedef struct {
...
} Name_t;
这也可以在标记(命名)结构时完成。这对于自引用结构(例如链表节点)很有用,但在其他方面是多余的。尽管如此,许多人遵循始终标记结构的做法,如下例所示:
typedef struct Name {
...
} Name_t;
请注意:在上面的语法中,由于您是从“typedef”开始的,所以整个语句是一个typedef
语句,其中 OldTypeName 恰好是一个结构体定义。因此编译器会解释这个名字after右大括号 } 作为 NewTypeName ...它是NOT变量名(就像在没有 typedef 的语法中一样,在这种情况下,您将定义结构体并同时声明/定义结构体变量)。
此外,如果您声明 typedef,但在最后省略 Name_t,那么您实际上创建了一个不完整的 typedef 语句,因为编译器会考虑“”中的所有内容struct Name { ... }
“作为 OldTypeName,并且您没有为 typedef 提供 NewTypeName。这就是编译器对您编写的代码不满意的原因(尽管编译器的消息相当神秘,因为它不太确定您做错了什么)。
现在,正如我上面提到的,如果您在定义结构类型时没有标记(命名)它,那么您必须立即使用它,或者定义一个变量:
struct {
...
} myNameStruct; // defines myNameStruct as a variable with this struct
// definition, but the struct definition cannot be re-used.
或者您可以在 typedef 中使用未标记的结构类型:
typedef struct {
...
} Name_t;
最终的语法是您在编写时实际执行的操作:
typedef struct{
char firstName[56];
char lastName[56];
} Author;
编译器很高兴。 HTH。
关于 _t 后缀的评论/问题:
_t 后缀是一种约定,向阅读代码的人表明带有 _t 的符号名称是类型名称(而不是变量名称)。编译器不会解析,也不知道 _t。
C89,特别是 C99,标准库定义了许多类型并选择使用 _t 作为这些类型的名称。例如C89标准定义了wchar_t、off_t、ptrdiff_t。 C99标准定义了很多额外的类型,例如uintptr_t、intmax_t、int8_t、uint_least16_t、uint_fast32_t等。但是_t没有保留,也没有被专门解析,也没有被编译器注意到,它只是一个很好遵循的约定当您在 C 中定义新类型(通过 typedef)时。在 C++ 中,许多人使用约定以大写字母开头类型名称,例如 MyNewType (与 C 约定 my_new_type_t 相对)。华泰
Footnote关于之间的差异宣告 and defining:首先特别感谢 @CJM 提出澄清编辑的建议,特别是与这些术语的使用相关的建议。
以下项目通常是declared and defined: 类型、变量, and 功能.
-
Declaring gives the compiler only a symbolic name and a "type" for that symbolic name.
- 例如,声明一个变量告诉编译器该变量的名称及其类型。
-
Defining gives the complier the full details of an item:
- 对于类型,定义为编译器提供了该类型的名称和详细结构。
- 对于变量,定义告诉编译器分配内存(在哪里以及多少)来创建该变量的实例。
一般来说,在由多个文件组成的程序中,变量、类型和函数可能是declared在许多文件中,但每个文件可能只有一个定义.
在许多编程语言(例如 C++)中,声明和定义很容易分开。这允许类型、变量和函数的“前向声明”,这可以允许文件编译而无需稍后定义这些项目。然而在 C 编程语言中声明和定义变量数是一回事。 (据我所知,在 C 编程语言中,唯一的例外是使用关键字extern
允许在未定义的情况下声明变量。)
正是由于这个原因,在这个答案的先前编辑中我提到“定义结构体”和“宣言struct [变量],”的含义宣言结构[变量]”被理解为创建该结构的实例(变量)。