为什么C需要使用“struct”关键字而C++不需要?

61

我一直有点困惑这里发生了什么:

#include <stdio.h>

int main() {  
    timeval tv;
    tv.tv_sec = 1;

    for (;;) {
        select(0, 0, 0, 0, &tv);
        printf("%s\n", "Hello World!");
    }
}

抱歉如果代码无法编译,我只是写了一个快速的示例。

像这样的代码在gcc下不会编译,除非我在使用“struct timeval”之前添加关键字struct。另一方面,g++可以正常处理。

这是C和C++处理结构的差异还是编译器的差异?(我非常专注于C ++,而在这样的行中使用C中的struct始终让我感到困惑)。


12
我喜欢想象C语言有一个“typedef命名空间”和一个“结构体命名空间”。如果结构体没有被typedef,你需要在它前面显式地加上“struct”来“解析”它的命名空间。你可以想象C++自动将所有结构体都放入“typedef命名空间”中。当然,这并不是实际发生的事情,但我觉得这个思维模型很有用。 - Rooke
C需要关键字,因为这是它的语法。我不确定这里正在询问什么。您是否想知道设计理念? - Maxpm
@Rooke,为了更加混淆,"typedef" 命名空间实际上是对象命名空间;typedef 是一种存储类,就像 staticexternauto 一样,因此 typedef 声明在语法上与对象声明相同。 - Simon Richter
3
@Rooke,这基本上就是发生的事情了,C结构体标签在它们自己的命名空间中,而且..就像Simon所说的typedef命名空间一样。在原始的K+R C中,所有结构成员的名称都在一个公共命名空间中 - 这就是为什么古老的系统结构体(如“struct stat”)都有前缀(st_dev、st_ino、st_mode...),以避免与其他结构体的成员发生冲突。每个这样的名称都与类型和偏移量相关联,而不是它所在的结构体。因此,->运算符不关心左侧的指针类型。不过这个问题很早就被解决了。 - greggo
6个回答

99

从语法上讲,两者都几乎相同地对待struct。只有C++添加了一条额外的规则,允许在没有歧义的情况下省略struct(和class)关键字。

如果存在歧义,也需要在某些地方使用struct关键字。一个臭名昭著的例子是在POSIX系统上的stat,它有一个struct stat 和一个函数stat


2
在Linux中,还有一个处理信号的例子或struct sigactionint sigaction(...)方法(来自signal.h),其中如果要使用struct关键字,则需要对其进行结构化。 - Patryk
我想说,结构体基本上和类相同,只是结构体默认成员的导出属性为“public”,而类的默认成员导出属性为“private”。 - matiu
为什么不能省略它?只是隐式的typedef。 - undefined
这不仅仅是隐式的typedef,而是“除非有其他人意外地已经使用该名称来表示其他内容的隐式typedef”。这在很大程度上取决于上下文,并且可能会导致您的头文件在大多数情况下正常工作,但在某些用户(或第三方头文件)使用相同名称的情况下会失败,原因是不明确。 - undefined

13
考虑 C++ 的原始想法(或者说,当它只是一个想法时,“带类的 C”),即面向对象的语言,可以与 C 兼容,以至于大多数有效的 C 程序也是有效的 C++ 程序。
C++ 通过从 C 的 struct 开始添加一些更多功能来构建其类模型:
1. 继承(尽管你可以通过将 struct 的第一个成员设为你要“继承”的 struct 来接近此目标)。 2. 信息隐藏(通过 public、private 等) 3. 成员方法(最初通过宏转换为 struct 外的 C 代码,并添加了一个 this 参数 - 许多实现仍然很相似)。
在这一点上有两个问题。首先,默认访问权限必须为 public,因为 C 没有信息隐藏,因此从 C++ 的角度来看,所有内容都是 public。为了良好的面向对象,应该默认为 private。通过添加 class 来解决这个问题,它与 struct 几乎完全相同,除了默认值为 private 而不是 public。
另一个问题是,这种面向对象的思想应该将 timeval 或任何其他类/struct 与 int 或 char 等放在同一“地位”,而不是在代码中经常被注释为特殊的。通过放宽一个规则,即在声明该类型的变量时,必须将 struct(或class)放在类型名称之前,解决了这个问题。因此,struct timeval tv 可以变成 timeval tv。
这随后影响了后来的 C 语法 OO 语言,例如 Java 和 C#,以至于例如只有较短的形式(timeval tv)才是 C# 中有效的语法。

11

我认为这是两种语言的设计决策。

C 中的结构体只是有不同用途的结构记录,与内置类型不同。

C++ 具有构造函数和运算符重载,因此它们像类型一样使用。

struct foo x;         // create a structure of pattern foo
typedef foo foo_type; // "define" a type
foo_type x;           // create an instance of type foo_type

C++:

foo x; // create an instance of type foo

顺便提一下,在C++中仍然允许使用struct foo。相对于使用typedef定义的foostruct foo更易于解析,因为名称查找更简单。


9

这就是C语言的样子。因此,在C中,以下模式非常常见:

typedef struct YourStructure
{
   int x;
   // more fields
} YourStructure;

然后您可以像在C++中一样引用它。

7

这只是两种语言的不同之处。C++在其结构体语法上更加宽容。


4
"宽容" 这个词并不适合。 - Šimon Tóth
3
因为这是关于命名空间而不是关于宽容度的问题。 - Šimon Tóth
2
从程序员的角度来看,C++结构体语法更加宽容。这是最重要的一点。我怀疑变化的动机与命名空间无关。动机肯定是为了让编码人员的生活更加轻松和简单。 - David Heffernan
2
@BasileStarynkevitch,C++从C中继承了相同的“命名空间”类型。它只有一条额外规则,将标记命名空间中的标识符解释为类型。请参阅我的答案以获取示例。 - Jens Gustedt
1
@BasileStarynkevitch 我知道你谈论命名空间时的意思。我只是认为这是一个实现细节,从提问者的角度来看并不特别有启发性。 - David Heffernan
显示剩余3条评论

4
当然,C语言是最早采用这种方式的。在C++中,结构体和类几乎是相同的,如果每个类变量都需要使用class关键字来定义,那将非常不方便。因此,为了简化操作,两者被统一起来了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接