C : typedef 结构名称 {...}; VS typedef struct{...} 名称;

2023-12-09

正如标题所说,我有这样的代码:

    typedef struct Book{
        int id;
        char title[256];
        char summary[2048];
        int numberOfAuthors;
        struct Author *authors;
    };


    typedef struct Author{
        char firstName[56];
        char lastName[56];
    };


    typedef struct Books{
        struct Book *arr;
        int numberOfBooks;
    };

我从 gcc 收到这些错误:

bookstore.c:8:2: error: unknown type name ‘Author’
bookstore.c:9:1: warning: useless storage class specifier in empty declaration [enabled by default]
bookstore.c:15:1: warning: useless storage class specifier in empty declaration [enabled by default]
bookstore.c:21:2: error: unknown type name ‘Book’
bookstore.c:23:1: warning: useless storage class specifier in empty declaration [enabled by default]

如果我像这样更改 typedef,则不会出现警告,也不会出现错误:

    typedef struct{
        char firstName[56];
        char lastName[56];
    } Author;

经过搜索C 编程语言,第二版并用谷歌搜索了几个小时,我不明白为什么第一个实现不起作用。


这里发生了几件事。首先,正如其他人所说,编译器对未知类型的抱怨可能是因为您需要在使用它们之前声明类型。但更重要的是理解三件事的语法:

  1. 结构体类型的定义,
  2. 结构变量的定义和声明,以及
  3. 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 [变量],”的含义宣言结构[变量]”被理解为创建该结构的实例(变量)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C : typedef 结构名称 {...}; VS typedef struct{...} 名称; 的相关文章

  • 在java程序中使用c++ Dll

    我正在尝试使用System LoadLibrary 使用我用 C 编写的一个简单的 dll UseDllInJava java import com sun jna Library import com sun jna Native imp
  • 如何使用 zlib 制作 .zip 文件

    我正在阅读zlib的文档 它相当详细 但我读到了这一行 输出数据将位于zlib格式 与 gzip 或zip formats http www zlib net zlib how html http www zlib net zlib how
  • 从多线程程序中调用 system()

    我们正在开发一个用 C 编写的多线程内存消耗应用程序 我们必须执行大量的 shellscript linux 命令 并获取返回码 读完之后article http www linuxprogrammingblog com threads a
  • ASP.NET Core 与现有的 IoC 容器和环境?

    我想运行ASP NET 核心网络堆栈以及MVC在已托管现有应用程序的 Windows 服务环境中 以便为其提供前端 该应用程序使用 Autofac 来处理 DI 问题 这很好 因为它已经有一个扩展Microsoft Extensions D
  • SSL/TLS/HTTPS 站点在 C#/.NET WebBrowser 控件中非常慢,但在 Internet Explorer 中则很好

    背景 我正在修改自动维基浏览器 http en wikipedia org wiki Wikipedia AutoWikiBrowser使用托管在安全服务器上的 MediaWiki 站点 我允许用户通过 C 应用程序中的 WebBrowse
  • 如何尝试/捕获所有异常

    我正在完成由其他人启动的 UWP 应用程序 该应用程序经常崩溃 我总是陷入困境应用程序 at if global System Diagnostics Debugger IsAttached global System Diagnostic
  • C# 正则表达式用于查找 中具有特定结尾的链接

    我需要一个正则表达式模式来查找字符串 带有 HTML 代码 中的链接 以获取文件结尾如 gif 或 png 的链接 示例字符串 a href site com folder picture png target blank picture
  • 如何创建用于 QML 的通用对象模型?

    我想知道是否有任何宏或方法如何将 Qt 模型注册为 QObject 的属性 例如 我有AnimalModel http doc qt io qt 5 qtquick modelviewsdata cppmodels html qabstra
  • mprotect 之后 malloc 导致分段错误

    在使用 mprotect 保护内存区域后第一次调用 malloc 时 我遇到分段错误 这是执行内存分配和保护的代码片段 define PAGESIZE 4096 void paalloc int size Allocates and ali
  • 带 If 的嵌套 For 循环的时间复杂度

    void f int n for int i 1 i lt n i if i int sqrt n 0 for int k 0 k lt pow i 3 k do something 我的思考过程 执行if语句的次数 sum i 1 to
  • 如何将带有自定义分配器的 std::vector 传递给需要带有 std::allocator 的函数?

    我正在使用外部库 pcl 因此我需要一个不会更改现有函数原型的解决方案 我正在使用的一个函数生成一个std vector
  • 两种类型的回发事件

    1 我发现了两篇文章 每篇文章对两种类型的回发事件的分类都略有不同 一位资源说两种类型的回发事件是Changed事件 其中控件实现 IPostbackDataHandler 当数据在回发之间更改时触发 然后Raised事件 其中控件实现 I
  • 预处理后解析 C++ 源文件

    我正在尝试分析c 使用我定制的解析器的文件 写在c 在开始解析之前 我想摆脱所有 define 我希望源文件在预处理后可以编译 所以最好的方法是运行C Preprocessor在文件上 cpp myfile cpp temp cpp or
  • 二叉树中的 BFS

    我正在尝试编写二叉树中广度优先搜索的代码 我已将所有数据存储在队列中 但我不知道如何访问所有节点并消耗它们的所有子节点 这是我的 C 代码 void breadthFirstSearch btree bt queue q if bt NUL
  • 从 R 到 C 处理列表并访问它

    我想使用从 R 获得的 C 列表 我意识到这个问题与此非常相似 使用 call 在 R 和 C 之间传递数据帧 https stackoverflow com questions 6658168 passing a data frame f
  • C 中带有指针的结构的内存开销[重复]

    这个问题在这里已经有答案了 我意识到当我的结构包含指针时 它们会产生内存开销 这里有一个例子 typedef struct int num1 int num2 myStruct1 typedef struct int p int num2
  • 如何引用解决方案之外的项目?

    我有一个 Visual Studio C 解决方案 其中包含一些项目 其中一个项目需要引用另一个不属于解决方案的项目 一开始我引用了dll
  • 为什么 Linux 对目录使用 getdents() 而不是 read()?

    我浏览 K R C 时注意到 为了读取目录中的条目 他们使用了 while read dp gt fd char dirbuf sizeof dirbuf sizeof dirbuf code Where dirbuf是系统特定的目录结构
  • 在 Xamarin 中获取 OutOfMemoryException

    java lang OutOfMemoryError 考虑增加 JavaMaximumHeapSize Java 执行时内存不足 java exe 我的 Visualstudio Xamarin 项目出现内存不足异常 请帮助我如何解决此问题
  • C#中为线程指定特殊的cpu

    我有 2 个线程 我想告诉其中一个在第一个 cpu 上运行 第二个在第二个 cpu 上运行 例如在具有两个 cpu 的机器中 我怎样才能做到这一点 这是我的代码 UCI UCIMain new UCI Thread UCIThread ne

随机推荐