[英]Difference of typedef before or after block in decleration/definition with or without a pointer, with or without a list?
每种风格的优势是什么? 包含指针如何改变事物?
一个在标签之前,另一个在 object 标识符之前。 Typedef 建立在它们的前身之上,这些前辈最终源自一些原始类型安排。
clang 可接受以下内容:
typedef struct typeA {int i;} typeA;
struct typeB {int i;} typedef typeB;
typedef struct typeAa {int i;} * typeAa;
struct typeBb {int i;} typedef * typeBb;
typedef struct typeC {} typeC;
struct typeD {} typedef typeD;
typedef struct typeCc {} * typeCc;
struct typeDd {} typedef * typeDd;
typedef struct typeE {} typeE, typeEptr[], * typeEArrayptr, typeEptrArrayfunc();
谢谢!
在搜索前后无法在 [c][typedef][structs] 中找到任何结果,这就是全部结果:
问题包含代码:
typedef struct typeA {int i;} typeA;
struct typeB {int i;} typedef typeB;
typedef struct typeAa {int i;} * typeAa;
struct typeBb {int i;} typedef * typeBb;
typedef struct typeC {} typeC;
struct typeD {} typedef typeD;
typedef struct typeCc {} * typeCc;
struct typeDd {} typedef * typeDd;
typedef struct typeE {} typeE, typeEptr[], * typeEArrayptr, typeEptrArrayfunc();
据称clang
接受了它。 在某种程度上,这是准确的:如果没有指定警告选项,GCC 和clang
都会毫无怨言地接受代码。 添加一组相当全面的警告选项,你会得到很多错误:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -pedantic --pedantic-errors -c td17.c
td17.c:4:12: error: ‘typedef’ is not at beginning of declaration [-Werror=old-style-declaration]
4 | struct typeB {int i;} typedef typeB;
| ^~~~~
td17.c:7:12: error: ‘typedef’ is not at beginning of declaration [-Werror=old-style-declaration]
7 | struct typeBb {int i;} typedef * typeBb;
| ^~~~~~
td17.c:9:20: error: struct has no members [-Wpedantic]
9 | typedef struct typeC {} typeC;
| ^~~~~
td17.c:10:12: error: struct has no members [-Wpedantic]
10 | struct typeD {} typedef typeD;
| ^~~~~
td17.c:10:12: error: ‘typedef’ is not at beginning of declaration [-Werror=old-style-declaration]
td17.c:11:20: error: struct has no members [-Wpedantic]
11 | typedef struct typeCc {} * typeCc;
| ^~~~~~
td17.c:12:12: error: struct has no members [-Wpedantic]
12 | struct typeDd {} typedef * typeDd;
| ^~~~~~
td17.c:12:12: error: ‘typedef’ is not at beginning of declaration [-Werror=old-style-declaration]
td17.c:14:20: error: struct has no members [-Wpedantic]
14 | typedef struct typeE {} typeE, typeEptr[], * typeEArrayptr, typeEptrArrayfunc();
| ^~~~~
td17.c:14:20: error: function declaration isn’t a prototype [-Werror=strict-prototypes]
cc1: all warnings being treated as errors
$
在 C 语法中, typedef
被视为存储 class — 就像extern
、 static
等 — 尽管还有一个专门针对typedef
的部分。 还有一个未来方向§6.11.5 存储 class 说明符,其中指出:
除了在声明中的声明说明符的开头之外,存储类说明符的放置是一个过时的功能。
因此,在您的问题中, typedef
出现在声明中的第一个单词之外的每个变体都使用了 C 的过时功能,不应在新代码中使用。 它仍然支持向后兼容,而不是因为它应该被使用。
有一个 Q&A Is it a good idea to typedef pointers? 大多数人的观点是答案是“否——function 指针可能是个例外”。
空结构定义typedef struct typeC {} typeC;
违反§6.7.2.1 结构和联合说明符的规则,特别是 ¶8 其中说:
…如果结构声明列表不包含任何命名成员,无论是直接还是通过匿名结构或匿名联合,行为是未定义的。 ……
因此,空括号会调用未定义的行为。 然而,除非它们受到迂腐选项的约束,否则编译器决定他们将接受该声明,即使它没有由标准 C 定义。如此定义的结构类型几乎没有用。 由于{}
,类型是完整的,但为空。 事实上,如果您打印sizeof(typeC)
或等效值(没有警告选项),那么打印的结果是0
。 您可以使用指向类型的指针,但不能使用类型。
考虑对初始代码的扩展:
#include <stdio.h>
typedef struct typeF
{
size_t size;
struct typeC C;
size_t left;
} typeF;
int main(void)
{
printf("sizeof(typeC) = %zu\n", sizeof(typeC));
printf("sizeof(typeF) = %zu\n", sizeof(typeF));
printf("offsetof(typeF, C) = %zu\n", offsetof(typeF, C));
printf("offsetof(typeF, left) = %zu\n", offsetof(typeF, left));
return 0;
}
在 64 位机器上,output 是:
sizeof(typeC) = 0
sizeof(typeF) = 16
offsetof(typeF, C) = 8
offsetof(typeF, left) = 8
请注意,Linux kernel 编码标准不允许对结构使用typedef 。 所有结构都必须有一个标签,并且在引用类型时必须始终使用struct tag
。
另一种观点是,您始终对结构类型使用 typedef 名称。 在简单的情况下,这意味着您可以使用:
typedef struct
{
int number;
char *name;
} NumberName;
但是,如果您需要自引用指针(例如,对于链表),则该结构必须有一个标记。 您可以使用以下顺序:
typedef struct SomeTag SomeTag;
struct SomeTag
{
int number;
char *name;
SomeTag *next;
SomeTag *prev;
};
使用这种形式,您只能使用SomeTag
来引用这种类型。 如果您在某处编写struct SomeTag
,它不会引用显示的类型。 它可能引用在别处定义的不完整类型,但更可能是错误。
或者,您可以使用:
typedef struct SomeTag
{
int number;
char *name;
struct SomeTag *next;
struct SomeTag *prev;
} SomeTag;
使用这种形式,在 typedef 定义之后,您可以使用SomeTag
或struct SomeTag
来引用结构类型。 但是,由于在定义结构体时没有引入typedef名称,所以必须在结构体内部使用符号struct SomeTag *
。
虽然这不是强制性的,但为标记和 typedef 名称使用相同的名称是相当常规的。 没有冲突; 它们位于不同的命名空间中(§6.2.3 标识符的名称空间。C++ 自动提供 class 或类型名称,当您定义 class、结构或联合时,不带class
、 struct
或union
前缀,因此对两者使用相同的标记是一致的与 C++。还有其他学派认为标签名称应该与类型名称不同。一个这样的约定是typedef struct SomeTag_s SomeTag_t;
(使用不同的后缀)。注意 POSIX 保留_t
后缀(参见§2.2 The编译环境和§2.2.2 名称空间——尤其是“任何标头”保留后缀_t
的那一行)。
请注意,function 指针声明typeEptrArrayFunc()
等效于:
struct typeE typeEptrArrayfunc();
这声明了一个 function 类型,它返回一个struct typeE
并采用一个不确定的参数列表(但它不是一个可变参数 function 类型,在原型的末尾带有, ...
)。 它不是 C90 到 C17/C18 中的严格原型。 在 C23 中,它将等同于:
struct typeE typeEptrArrayfunc(void);
这是一个 function 类型,没有 arguments。
类型typeEptrArrayfunc
与指针或 arrays 无关,尽管名称如此。 类似的注释适用于typeEArrayptr
— 该类型是“指向struct typeE
的指针”,与 arrays 无关。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.