【问题标题】:C structs don't define types?C结构不定义类型?
【发布时间】:2009-11-15 14:49:46
【问题描述】:

我刚刚开始学习 C,具有专业的 Java 背景和一些(如果不是太多)C++ 知识,我很惊讶这在 C 中不起作用:

struct Point {
    int x;
    int y;
};

Point p;

p.x = 0;
p.y = 0;

看来我必须使用struct Point 作为类型或使用typedef 来声明p。此代码在 C99 中有效吗?还是这是“C++ 的东西”?

【问题讨论】:

  • 将 p 声明为 struct Point p;。您可以使用 typedef,但我建议您练习将您的类型称为 struct Point
  • @PP 为什么?你眼中的typedef有什么问题?
  • @Grumdrig:我从不提倡通过 typedef 重新定义语言。它使代码维护和审查更加困难。如果某物是一个结构,就这么说。当然,在某些情况下,不同的 int 类型更适合用作 typedef。但我不相信对结构做同样的事情通常是一个很好的例子。同样,这是反 Google-Go 哲学,其中关键字被视为邪恶,而是使用大写来区分公共/私人功能;但我更喜欢明确。
  • @Grumdrig - 将 const 或 const 指针“修改器”添加到已经有 const 甚至只是一个指针的 typedef 是很痛苦的。

标签: c


【解决方案1】:

据我所知,它不应该在 C99 中没有 typedef 的情况下工作(因为这只是 C 的工作方式),但它确实在 C++ 中工作,因为 C++ 中的 struct 只是一个 @ 987654323@,所有成员默认公开。

【讨论】:

  • 投了反对票,因为自 K&R C 以来这绝对有效。typedefstructenum 之间存在很大差异。
  • Mikael 是对的,C++ 不要求您在声明结构类型时使用 struct。为什么答案被否决了?
  • 啊,重新投票为误读问题/答案;从技术上讲,Mikael 是正确的,如果需要事先没有 struct 关键字的结构类型名,则需要 typedef。作为 C 语言的长期用户,我坚持我的建议,即明确声明类型(结构)。
【解决方案2】:

不,结构不定义新类型。你需要的是:

typedef struct {
   int x; int y;
} Point;

现在 Point 是您可以使用的新类型:

Point p;
p.x = 0; p.y = 0;

【讨论】:

  • 一个结构确实定义了一个类型。这就是为什么在定义struct Foo之后,我可以声明一个变量struct Foo a
  • 对;混淆点是结构 tag 本身不能用作类型名称;它必须以struct 关键字开头。
  • 你说得对,我应该更准确。像“struct tag { ... };”这样的结构不将 'tag' 设置为新类型。当然,您可以使用该标记来指定变量具有“struct tag var;”中的结构。在我看来,OP 并不清楚 struct 标签所扮演的角色与 typedef 定义的类型相比。
【解决方案3】:

struct Point 是类型,就像union Foo 是类型一样。您可以使用 typedef 将其别名为另一个名称 - typedef struct Point Point;

【讨论】:

    【解决方案4】:

    在 C 中没有混淆

    struct Point {
        int x;
        int y;
    };
    

    union Point {
        int x;
        int y;
    };
    

    这是两种不同的类型,分别称为struct Pointunion Point

    C99 标准第 6.7.2.1 节规定:

    6 结构和联合说明符有 相同的形式。关键字structunion 表示类型为 分别指定为 结构类型或联合类型。

    7 结构声明列表的存在 在结构或联合说明符中 在 a 中声明一个新类型 翻译单元。

    所以它最明确地声明了一个类型。 C 中类型名称的语法在 6.7.6 节中给出,包括 6.7.2 中的 specifier-qualifier-list,其形式为 struct-or-union identifier

    此代码在 C99 中有效吗?还是这是“C++ 的东西”?

    不,C99 不决定将结构类型提升到具有相同名称的枚举类型和联合类型之上。这是一个“C++ 的东西”,因为结构和类在 C++ 中大部分是相同的,而类对 C++ 很重要。

    【讨论】:

    • 实际上,我相信存在混淆,或者至少曾经存在过 - 标签命名空间在 structunionenum 之间共享,我什至听说过一些人们建议,当 foo 被定义为 union 但绝不是 struct 时,声明 struct foo bar; 可能是有效的。不确定目前的情况如何,但这是一个混乱的主题。
    【解决方案5】:

    所以...

    Point           a tag
    struct Point    a type
    
    typedef struct {
        . . .
    } Point_t;
    
    Point_t         a type
    

    我经常在字里行间看到为什么?。毕竟,只说Point x; 似乎完全合理,那你为什么不能呢?

    碰巧的是,C 的早期实现为标签和其他标识符建立了一个单独的 命名空间。实际上有 4 个命名空间1 .一旦以这种方式定义语言,就不可能允许将 struct 标签用作类型,因为所有现有代码在 普通标识符标签之间存在名称冲突> 会突然出错。


    1. 4 个命名空间是:
    -- 标签名称(通过标签声明和使用的语法消除歧义);
    -- 结构体、联合体和枚举的标签(通过任何关键字来消除歧义);
    -- 结构或工会的成员;每个结构或联合都有一个单独的名称 其成员的空间(通过用于访问的表达式的类型来消除歧义 会员通过 .或 -> 运算符);
    -- 所有其他标识符,称为普通标识符(在普通声明符中声明或作为 枚举常量)

    【讨论】:

      【解决方案6】:

      它在 C99 中不起作用,它是 C++ 的东西。您必须说 struct Point 或在 C 中使用 typedef。

      【讨论】:

        【解决方案7】:

        在 C 中,结构可以有两种名称:标记名称和类型名称。标记名称只能在以结构为前缀的情况下使用,例如:

        struct mytag { int a; }
        struct mytag mystruct;
        

        typedef 允许您定义标签名称和类型名称。类型名称可以不带结构前缀使用:

        typedef struct mytag { int a; } mytype;
        mytype mystruct; // No struct required
        

        【讨论】:

          【解决方案8】:

          结构不是类型。您可以从结构中创建一个点,如下所示:

          struct Point p;
          p.x = 0;
          p.y = 0;
          

          如果你想使用一个结构体作为一个类型,你必须 typedef 它。这适用于 C 和 C++:

          typedef struct _point {
              int x;
              int y;
          } Point;
          

          如果您必须转发声明它以能够拥有指向相同类型或循环依赖的结构的指针,请始终为您的结构命名。

          【讨论】:

          • 结构是类型。 struct Point {} 定义的类型名称是“struct Point”
          【解决方案9】:

          这就是 C 的工作方式。在 C 中你必须说:

          typedef struct Point {
              int x;
              int y;
          } Point;
          

          然后您将拥有一个名为 Point 的类型,它可以满足您的需求。

          在 C++ 中,按照您的方式定义它就足够了。

          【讨论】:

            【解决方案10】:

            您首先需要将 Point 设为如下类型:

            typedef struct Point {
                int x;
                int y;
            } Point;
            

            这允许您使用Point 作为类型,或struct Point

            即:

            Point *p;
            
            sizeof(struct Point);
            

            有些人喜欢使结构的名称与正在创建的类型不同,例如:

            typedef struct _point {
                   ...
            } Point;
            

            你会遇到两者。最后一个例子告诉我,我不应该使用struct _point,除了Point 类型之外,它并不意味着公开。

            【讨论】:

            • 最后一个例子告诉你行为没有被 C 标准定义。保留以下划线和大写字母开头的标记名称(实际上是所有标识符)。当然,通常不可移植的代码是好的——如果它今天通过了测试,那么谁在乎它明天是否还能工作,对吧?但是,当您不需要时,为什么要依赖 _Point 未被当前编译器的当前版本实际使用?
            • @Steve - 绝对正确。切勿使用前导下划线后跟大写字母(这在 C99 的 6.10.8 节的第 4 段中)
            • 我是根据 OP 的例子给出的例子,使用他的名字、变量和类型。不过,我会编辑答案。
            • 现在,如果 typedef 具有文件范围(在这种情况下,下划线后跟小写字母也被保留),您将遇到同样的问题。不过,在函数体中没关系。最好避免使用初始下划线,除非您正在编写一些实现(例如,如果您正在编写标准头文件以与编译器一起使用)。我见过的常见可移植形式是typedef struct Point { ... } Point;typedef struct Point_tag { ... } Point;。 “_tag”的原因是struct后面的标识符在所谓的“标签命名空间”中。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-04-15
            • 2014-05-28
            • 1970-01-01
            • 2013-03-21
            • 2020-11-14
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多