C/C++中typedef/struct的冗余命名

6
#include <stdio.h>
#include <string.h>

const int NAMELEN=30;

const int MAXCLASSSIZE=10;

typedef struct StudentRec {
    char lastname[NAMELEN];
    char firstname[NAMELEN];
    long int ID;
    int finalmark;
}Student;

我是编程新手,有一个关于为什么括号后面有Student;的问题。这是我们必须遵循的格式吗?


5
C或C++在这个例子中没有区别。在Google或你的书中查找typedef。 - Gregor Brandt
6
@gbrandt:是的,但在C++中没有必要以那种方式声明结构体。 - Billy ONeal
2
@Billly ONeal:没错,但这仍然是有效的C++。我经常看到这样做的人,可能是从C语言中保留下来的习惯。 - Fred Larson
2
你们是不是无法阅读实际问题“这是我们必须遵循的格式吗?” - John Dibling
1
@sbi:实际上,在C++中,结构体标签也处于不同的命名空间中。不同之处在于,当编译器遇到标识符时(与C不同,您必须使用“struct”关键字请求),它还将查找结构体标签命名空间内部。有关更详细的解释,请参见:https://dev59.com/L3I-5IYBdhLWcg3wxruQ#1675446 - David Rodríguez - dribeas
显示剩余5条评论
5个回答

17

你混淆了两件事情。在C语言中,你可以像这样定义一个结构体:

struct foo {
    int a, b;
};

然后要使用其中一个,您需要执行以下操作:

struct foo myFoo;

内容有些啰嗦,他们使用了typedef创造了一种新类型。我可以这样做:

typedef int myInt;

然后使用它:

myInt x;

你所做的是声明一个新类型,名为Student,它等同于一个结构体StudentRec。按照惯例,许多人在typedef和struct中使用相同的名称 - 这是合法的:

typedef struct foo { int a, b; } foo;

13

这里创建了一个名为“Student”的类型名称来代表结构体。在C++中,使用typedef实现此目的是不必要的。可以像这样定义:

struct Student {
    char lastname[NAMELEN];
    char firstname[NAMELEN];
    long int ID;
    int finalmark;
};

1
在大多数情况下,这是不必要的,但结构体的定义和typedef有细微的差异。标识符驻留在不同的命名空间中(不是C ++中namespace关键字的意义)。 - David Rodríguez - dribeas

4
在C中,结构体名称和类型名称(如int)有不同的命名空间{类型命名空间与变量和函数命名空间共享,因此请注意}。当你定义一个新的结构体时,它的名称会自动添加到结构体命名空间中,但是当你声明该类型的变量时,你必须在它的名称前加上struct,这样编译器就知道要查找结构体命名空间以确定您想要的变量。

使用typedef关键字,您可以将变量名称添加到类型命名空间中,以便您在声明中不必使用struct关键字。

您给出的示例结合了typedef声明和结构定义,但在结构命名空间和类型命名空间中使用了不同的结构名称。 您可以这样做的原因有两个。首先,对像变量声明一样的语句进行前缀typedef定义了指定名称的类型名而不是变量。 其次,您可以通过在结构声明和分号之后立即包含它们的名称来声明结构类型的变量。 您的示例将它们组合在一起。

C中还有其他合法形式。例如:

/* This declares a variable foo whose type has no name but is a struct that contains one int named x */
struct {
    int x;
} foo;

/* This adds bar to the type namespace without adding an entry to the struct namespace. */
typedef struct {
    int y;
} bar;

/* This adds the name baz to the struct namespace and declares a variable named b of this type */
struct baz {
    int z;
} b;

/* This adds the name ralf to the type namespace which also refers to this type */
typedef struct baz ralf;

C++有不同的命名空间结构,这一点您肯定已经注意到了,因为它使用了namespace关键字。但在这种情况下,C++比C更简单。定义结构体(或类、联合体或枚举)会自动将您使用的名称(如果有)添加到typedef添加名称的命名空间中。这大大简化了事情,大多数情况下是如此,但也有一些需要注意的地方。这些问题主要与保留与C的向后兼容性有关。主要是要注意当某人typedefs struct foo foo;时,不要将其视为尝试为已使用的名称命名的错误。
另一个问题涉及到这种相当常见的C代码:
struct shoe {
    int size;
} shoe;

这在C语言中很常见,特别是当只需要存在一个结构体时。在C语言中,这很容易实现,因为struct shoe和变量shoe之间不会发生冲突。但在C++中,由于需要向后兼容C语言,此方法仍然有效。


1

这些都不适用于C++。在C++中,请优先考虑:

struct Foo
{
   . . .
};

以下内容仅适用于C

从技术上讲,struct Foo {}; 对于大多数目的来说已经足够了。然而,由于每次使用类型Foo时都必须重复使用struct,因此有点啰嗦。

struct Foo {}

void func( struct Foo* foo );

使用typedef可以使这个过程更简单。

struct Foo { };
typedef struct Foo Foo;
void func( Foo* foo );

这可以进一步缩短:

typedef struct Foo { } Foo;
void func( Foo* foo );

当进行一行代码时,也可以使用以下语法:

typedef struct { } Foo;
void func( Foo* foo );

这是创建一个匿名的struct,然后给它命名为Foo。你最常见到这种情况是在enum中。
typedef enum { } Bar;

通常情况下,初始冗余的Foo被保留是有原因的。这是创建自引用结构(如链表)的唯一方法。

typedef struct Node
{
   Node* next;
} Node;

如果省略了初始的Node,就没有办法声明一个指向这个结构体的指针。技术上讲这也是有效的,但现在你必须想出两个名称,而不是只有一个。
typedef struct node_
{
   node_* next;
} Node;

那么为什么要使用:

typedef struct Foo { } Foo;

为了保持一致性。这种声明风格涵盖了所有的基础,因此在声明结构时您永远不必考虑它。


0

Student是typedef创建的新类型的名称。StudentRec是它定义的结构的名称。


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