【问题标题】:Is a struct {...}; a type or an unnamed variable?是一个结构 {...};类型或未命名的变量?
【发布时间】:2015-10-30 21:27:59
【问题描述】:

在文件范围内,以下是类型声明还是未命名变量?

struct student_s {
    char* name;
    int age;
    double height;
    struct student_s* next;   
};

如果是类型定义,那么与:

typedef struct student_s {
    char* name;
    int age;
    double height;
    struct student_s* next;   
};

?

(背景:请参阅我在Changing a variable from global to local - C 的回答,我相信第一个会引入一个未命名的变量,然后编译器会对其进行优化。)

注意:该问题已被标记为可能与 In what scope is a struct member identifier put? 重复,但我相信我不是在问关于成员范围的问题,而是关于声明实际创建的内容。然而。 Difference between 'struct' and 'typedef struct' in C++? 的答案请解释我的问题。

【问题讨论】:

  • 为什么你认为它会定义一个未命名的变量,而不是没有变量?您是否期待第二个 sn-p 的未命名类型别名?
  • 您也可以查看this thread 了解更多信息
  • 您发布的链接指向一个不属于您的答案。你想要这个:stackoverflow.com/a/31874396/2793118 前者是一个类型定义,后者是无效的,因为typedef 没有名称参数。
  • 区别:第一个是有效的C,第二个不是。

标签: c struct


【解决方案1】:

根据C 标准,结构声明格式为

struct student_s {
    char* name;
    int age;
    double height;
    struct student_s* next;   
};

是一个类型的声明。引用C11,第 §6.7.2.1 章

结构或联合说明符中的结构声明列表的存在声明了翻译单元内的新类型。 struct-declaration-list 是结构或联合成员的一系列声明。 [...]

C 标准不要求为该类型创建变量(命名或未命名)。

在您的第二次 sn-p 中,您实际上(尝试typedefnothing。但是,如果您将 sn-p 更改为

typedef struct {
         //.....members
} student_s ;

您将创建一个类型 student_stypedef 为未命名的结构类型),供以后使用。

FWIW,无论如何,我们从来没有谈论过在这里创建一个变量。这都是关于类型

【讨论】:

  • 在最后一个例子中,next指针可以这样声明,当结构是匿名的并且typedef还没有完成?
  • @Useless 是的,我(我们?)错过了拥有成员变量的情况。不试图为错误辩护(现在更正),但这不在问题的直接范围内,恕我直言。 :-)
  • 同意,直到我第三次阅读它时才发现它:)
【解决方案2】:

第一个声明声明了一个类型。在该声明之后,struct student_s 类型是已知的,您可以声明该类型的变量或指向它的指针:

struct student_s student1;
struct student_s *pStudent;

即使是编译,第二个声明也很奇怪。常见的用法是:

typedef struct {
    char* name;
    int age;
    double height;
    struct student_s* next;   
} studend_t;

向匿名结构声明别名student_t。然后就可以直接使用了:

student_t student1;
student_t *pStudent;

但是第二个示例可以编译(即使带有警告)并且与第一个示例相同!

它真正的作用是为struct student_s 声明一个无效别名。 typedef 被忽略并产生警告:typedef 需要一个名称,但作为副作用,声明了结构,就像在第一个示例中一样。

所以实际问题的真正答案是除了警告之外没有区别

【讨论】:

  • 除非你不能声明 next 指向匿名结构的指针,根据 Sourav 的回答。
  • 虽然第二个声明没有像 OP 认为的那样做,但我认为它的格式不正确。这也许是一个很好的语言律师问题。
  • @DavidHammen :我试过了,很惊讶它编译了。查看我的编辑。
  • 我作为一个单独的问题询问了它的合法性,stackoverflow.com/questions/31879335/… 即使它是非法的,编译器也通过发出诊断来完成它的工作。该标准并没有说编译器在面对格式错误的代码时不应生成目标代码。在我在某个源文件的文件范围内插入无意义的static; 后,多个编译器会生成目标代码。他们确实发布了诊断信息。
【解决方案3】:

两者都是类型定义。第二个是不完整的。它不提供名称作为typedef 的参数。更好的是使用

typedef struct student_s {
    char* name;
    int age;
    double height;
    struct student_s* next;   
} student;


关于合法性

typedef struct student_s {
    char* name;
    int age;
    double height;
    struct student_s* next;   
};

我已将其作为一个单独的问题提出,Legality of `typedef struct foo {int bar};`

【讨论】:

    【解决方案4】:

    它是一个类型,你不能有一个匿名的结构实例。为了比较,这声明了类型struct_type 一个该类型的实例struct_instance

    struct struct_type {
        /* blah */
    } struct_instance;
    

    如果你想单独声明另一个实例(从类型 decl)你会使用

    struct struct_type another_instance;
    

    使用 typedef - 如果正确完成,与您的示例不同 - 只允许您为类型指定另一个名称,不需要 struct 关键字来声明实例:

    typedef struct_type MyStruct;
    MyStruct yet_another_instance;
    

    或等效

    typedef struct struct_type {
        /* blah */
    } MyStruct;
    

    省略名称 (struct_type) 会为您提供一个匿名结构类型,该类型只能通过其 typedef 的名称来引用。


    注 1

    由于您的原始结构包含指向其自身类型的next 指针,因此该类型必须在声明成员时具有名称。所以,你不能声明一个带有自类型指针的匿名结构。如果您为匿名结构类型命名为 typedef,则该名称在成员声明之后才存在,因此不能在那里使用。

    typedef struct /*S*/ {
        struct S *next; /* T doesn't exist yet, so without S, you can't declare this */
    } T;
    

    注2

    可以将匿名联合的匿名实例声明为成员:

    struct S {
        union {
            int i;
            unsigned u;
        };
    };
    
    struct S s;
    s.i = -1;
    printf("%x\n", s.u);
    

    但这是一个非常特殊的情况。我从主要论点中删除了关于这一点的评论,以防它具有误导性。

    【讨论】:

      【解决方案5】:
      struct A { ... }
      

      struct 命名空间中创建可见的struct 'A'(这与C++ 命名空间没有任何共同之处)。因此,要访问struct,您必须使用struct 关键字;

      struct A a;
      

      当使用 typedef struct 定义时

      typedef struct A { ... } B;
      

      变得可见并绑定到B,您可以轻松创建struct,就像B类型的公共变量

      B a;
      

      如果我错了,请纠正我。

      【讨论】:

        【解决方案6】:

        不同之处在于,对于第一个示例,您必须像这样声明一个新变量:struct student_s variable;,而对于第二个示例,您可以简单地使用 student_s variable;

        【讨论】:

        • 本例中没有。没有定义 typedef 名称。
        • 实际上,gcc 确实编译了它,但给出了警告 warning: useless storage class specifier in empty declaration
        • @dbush - 这是因为声明的形式为 declaration-specifiers init-declarator-list opt ;这里typedef 充当storage_class_specifier。该标准并没有说必须提供某些东西作为要进行类型定义的名称。它确实说至少要介绍一个名字,这正在发生。
        • @pelya - 假设您在代码中某处的文件范围内添加了多余的 typedef int;。该标准规定必须发出诊断,因为这是非法代码。该标准对该诊断的后果没有实际意义。该标准没有说明致命诊断与单纯的警告有关。编译器仍然可以生成目标文件。这确实可以使用多个版本的 clang 和 gcc 编译(在生成目标文件的意义上)。
        猜你喜欢
        • 2018-07-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-01-26
        • 2016-11-22
        • 2012-11-02
        相关资源
        最近更新 更多