C结构体不定义类型?

4

我具备专业的Java背景和一些(不算太多)的C++知识,最近刚开始学习C语言。但是我惊讶地发现,在C语言中,下面这段代码是无法正常工作的:

struct Point {
    int x;
    int y;
};

Point p;

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

看起来我必须使用struct Point作为类型或使用typedef来声明p。这段代码在C99中有效吗?还是这只是"C++的事情"?


1
声明p为struct Point p;。你可以使用typedef,但我建议你养成将类型称为struct Point的习惯。 - PP.
@PP: 我同意:https://dev59.com/L3I-5IYBdhLWcg3wxruQ#1675507 - Christoph
@PP 为什么?在你看来,typedef有什么问题吗? - Grumdrig
@Grumdrig:我从未主张通过typedef重新定义语言。这会使代码维护和审查更加困难。如果某物是一个结构体,请明确说明。当然,有时不同的int类型最好作为typedef。但我不认为对结构体做同样的事情通常有充分的理由。再次强调,这是反Google Go哲学,其中关键字被视为邪恶,而使用大写来区分公共/私有函数;但我更喜欢明确。 - PP.
@Grumdrig - 给已经带有const或者指针的typedef添加const或者const指针"修饰符"是一件痛苦的事情。 - Pod
10个回答

8
据我所知,在C99中没有typedef是行不通的(因为这是C语言的工作方式),但在C++中,struct就是一个默认所有成员都是公共成员的class,所以可以这样使用。

因为自K&R C以来,这绝对有效。typedefstructenum之间有很大的区别,所以被踩了。 - PP.
1
Mikael是正确的,C++在声明结构类型时不需要使用struct关键字。为什么这个答案被踩了呢? - ardsrk
啊,因为误读了问题/答案而重新点赞;从技术上讲,如果希望在结构体类型名称之前不使用“struct”关键字,则需要使用typedef。作为一名长期使用C语言的用户,我坚持认为类型(结构体)应该明确声明。 - PP.

5
不,结构体并不定义新类型。您需要的是:

不,结构体并没有定义新类型。您需要的是:

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

现在Point是一种新类型,您可以使用:

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

3
结构体定义了一种类型。这就是为什么在定义了 struct Foo 之后,我可以声明一个变量 struct Foo a - Chuck
2
没错,混淆的点在于结构体 tag 本身并不作为类型名称使用;它必须被关键词 struct 所引导。 - John Bode
你说得对,我应该更加精确。像“struct tag { ... };”这样的结构并没有将'tag'设置为一个新类型。当然,你可以使用那个标签来指定一个变量拥有那个结构,如“struct tag var;”。 我认为OP似乎不清楚结构体标签与使用typedef定义的类型之间的区别。 - Remo.D

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 在结构体说明符或联合体说明符中存在struct-declaration-list声明一个新类型,在翻译单元内。”
因此,它最明确地声明了一种类型。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;可能是有效的。不确定当前情况如何,但这是一个混乱的主题。 - R.. GitHub STOP HELPING ICE

4

Point结构体就像union Foo一样,是一种类型。你可以用typedef给它取别名 - typedef struct Point Point;


2

因此...

Point           a tag
struct Point    a type

typedef struct {
    . . .
} Point_t;

Point_t         a type

我经常看到在代码中写下为什么?。毕竟,只是写Point x;似乎完全合理,那么为什么不能这样做呢?

事实上,早期的C语言实现为标签和其他标识符建立了单独的名称空间。实际上有4个名称空间1。一旦以这种方式定义了语言,就不可能允许结构体标签被用作类型,因为这样所有现有代码中的普通标识符标签之间的名称冲突将突然出错。


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


1

结构体不是类型。您可以按以下方式从结构体创建一个点:

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

如果你想使用结构体作为一种类型,你必须对它进行typedef。这在C和C++中都适用:
typedef struct _point {
    int x;
    int y;
} Point;

无论如何,请始终为您的结构体命名,如果您必须前向声明它以便能够具有指向相同类型的结构体或循环依赖关系的指针。


4
结构体是一种类型。由struct Point {}定义的类型名称为“struct Point”。 - Pete Kirkham

1
在C语言中,结构体可以有两种名称:标签名和类型名。只有在前缀为struct的情况下才能使用标签名,例如:
struct mytag { int a; }
struct mytag mystruct;

typedef允许你同时定义一个标签名和一个类型名。类型名可以在不使用struct前缀的情况下使用:

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

1

在C99中它不起作用,这是一个C++的事情。你必须要么说struct Point,要么在C中使用typedef。


0

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

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

然后你会有一个叫做Point的类型,它会按照你的要求进行操作。
在C++中,只需要按照你所做的方式进行定义就足够了。

0

你首先需要将Point定义为一种类型,例如:

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

这使您可以将Point用作类型,或者struct Point

I.e:

Point *p;

sizeof(struct Point);

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

typedef struct _point {
       ...
} Point;

你会遇到这两种情况。最后一个示例告诉我不应该使用 struct _point,它的本意是作为类型 Point而非被公开。

4
最后一个例子告诉你,行为在C标准中没有定义。以下划线和大写字母开头的标记名称(实际上所有标识符)都是保留的。当然,通常来说,非可移植的代码是可以接受的 - 如果它今天通过了测试,那么谁会在乎它明天是否仍然能够工作呢?但是,既然有选择,为什么要依赖于_Point没有实际被当前版本的编译器使用呢? - Steve Jessop
@Steve - 完全正确。永远不要使用下划线后跟大写字母的前导下划线(这在C99的第6.10.8节的第4段中有说明)。 - D.Shawley
我是基于原帖提供的示例,使用了原帖中的名称、变量和类型。不过我会编辑回答内容。 - Tim Post
现在,如果typedef具有文件作用域(在这种情况下,下划线后跟小写字母也是保留的),您将面临相同的问题。不过,在函数体中是可以的。除非您正在编写实现的部分(例如,如果您正在编写与编译器配套的标准头文件),最好避免使用初始下划线。我见过的常见可移植形式是typedef struct Point { ... } Point;typedef struct Point_tag { ... } Point;。"_tag"的原因是struct之后的标识符位于所谓的“标签命名空间”。 - Steve Jessop

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