繁体   English   中英

在有或没有指针,有或没有列表的情况下,decleration/definition 块之前或之后的 typedef 的差异?

[英]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 — 就像externstatic等 — 尽管还有一个专门针对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 定义之后,您可以使用SomeTagstruct SomeTag来引用结构类型。 但是,由于在定义结构体时没有引入typedef名称,所以必须在结构体内部使用符号struct SomeTag *

虽然这不是强制性的,但为标记和 typedef 名称使用相同的名称是相当常规的。 没有冲突; 它们位于不同的命名空间中(§6.2.3 标识符的名称空间。C++ 自动提供 class 或类型名称,当您定义 class、结构或联合时,不带classstructunion前缀,因此对两者使用相同的标记是一致的与 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM