简体   繁体   English

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

[英]Difference of typedef before or after block in decleration/definition with or without a pointer, with or without a list?

What is the advantage of each style?每种风格的优势是什么? How does including a pointer change things?包含指针如何改变事物?

One is before the tag and the other before the object identifier.一个在标签之前,另一个在 object 标识符之前。 Typedefs are built upon their predecessor derived ultimately from some primitive type arrangement. Typedef 建立在它们的前身之上,这些前辈最终源自一些原始类型安排。

The following are acceptable in clang: 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();

Thank you!谢谢!

Couldn't find any results in the [c][typedef][structs]before and after search and this is all that came up:在搜索前后无法在 [c][typedef][structs] 中找到任何结果,这就是全部结果:

Declaring a struct (that's already been typedef'd) within another struct? 在另一个结构中声明一个结构(已经进行了类型定义)?

The question contains the code:问题包含代码:

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();

It is claimed that that clang accepts it.据称clang接受了它。 Up to a point, that's accurate: both GCC and clang accept the code without complaint if there are no warning options specified.在某种程度上,这是准确的:如果没有指定警告选项,GCC 和clang都会毫无怨言地接受代码。 Add a reasonably comprehensive set of warning options and you get many errors:添加一组相当全面的警告选项,你会得到很多错误:

$ 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
$

In the C grammar, typedef is treated as a storage class — like extern , static , etc. — though there's also a section specifically on typedef .在 C 语法中, typedef被视为存储 class — 就像externstatic等 — 尽管还有一个专门针对typedef的部分。 There's also a future direction §6.11.5 Storage class specifiers which states:还有一个未来方向§6.11.5 存储 class 说明符,其中指出:

The placement of a storage-class specifier other than at the beginning of the declaration specifiers in a declaration is an obsolescent feature.除了在声明中的声明说明符的开头之外,存储类说明符的放置是一个过时的功能。

Therefore, every variant in your question where typedef appears as other than the first word in the declaration is using an obsolescent feature of C and should not be used in new code.因此,在您的问题中, typedef出现在声明中的第一个单词之外的每个变体都使用了 C 的过时功能,不应在新代码中使用。 It is still supported for backwards compatibility, not because it should be used.它仍然支持向后兼容,而不是因为它应该被使用。

There is an Q&A Is it a good idea to typedef pointers?有一个 Q&A Is it a good idea to typedef pointers? for which the majority viewpoint is that the answer is "No — with a possible exception for function pointers".大多数人的观点是答案是“否——function 指针可能是个例外”。

The empty structure definitions typedef struct typeC {} typeC;空结构定义typedef struct typeC {} typeC; violate the rules of §6.7.2.1 Structure and union specifiers and in particular, ¶8 which says:违反§6.7.2.1 结构和联合说明符的规则,特别是 ¶8 其中说:

… If the struct-declaration-list does not contain any named members, either directly or via an anonymous structure or anonymous union, the behavior is undefined. …如果结构声明列表不包含任何命名成员,无论是直接还是通过匿名结构或匿名联合,行为是未定义的。 ……

The empty braces invoke undefined behaviour, therefore.因此,空括号会调用未定义的行为。 However, unless they are constrained by the pedantic options, the compilers decide that they will accept the declaration, even though it is not defined by standard C. The structure types so defined are close to useless.然而,除非它们受到迂腐选项的约束,否则编译器决定他们将接受该声明,即使它没有由标准 C 定义。如此定义的结构类型几乎没有用。 Because of the {} , the type is complete, but empty.由于{} ,类型是完整的,但为空。 Indeed, if you print sizeof(typeC) or an equivalent value (with no warning options), then the result printed is 0 .事实上,如果您打印sizeof(typeC)或等效值(没有警告选项),那么打印的结果是0 You can use pointers to the type, but you can't use the type.您可以使用指向类型的指针,但不能使用类型。

Consider this extension to the initial code:考虑对初始代码的扩展:

#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;
}

On a 64-bit machine, the output is:在 64 位机器上,output 是:

sizeof(typeC) = 0
sizeof(typeF) = 16
offsetof(typeF, C) = 8
offsetof(typeF, left) = 8

Note that theLinux kernel coding standards do not allow the use of typedefs for structures.请注意,Linux kernel 编码标准不允许对结构使用typedef All structures must have a tag, and you must always use struct tag when referencing the type.所有结构都必须有一个标签,并且在引用类型时必须始终使用struct tag

An alternative viewpoint is that you always use a typedef name for a structure type.另一种观点是,您始终对结构类型使用 typedef 名称。 In the simple cases, that means you can use:在简单的情况下,这意味着您可以使用:

typedef struct
{
    int number;
    char *name;
} NumberName;

However, if you need self-referential pointers (for a linked list, for example), then the structure must have a tag.但是,如果您需要自引用指针(例如,对于链表),则该结构必须有一个标记。 You can either use the sequence:您可以使用以下顺序:

typedef struct SomeTag SomeTag;
struct SomeTag
{
    int number;
    char *name;
    SomeTag *next;
    SomeTag *prev;
};

With this form, you can only use SomeTag to reference this type.使用这种形式,您只能使用SomeTag来引用这种类型。 If you write struct SomeTag somewhere, it does not refer to the type shown.如果您在某处编写struct SomeTag ,它不会引用显示的类型。 It may refer to an incomplete type defined somewhere else, but is more likely a mistake.它可能引用在别处定义的不完整类型,但更可能是错误。

Alternatively, you can use:或者,您可以使用:

typedef struct SomeTag
{
    int number;
    char *name;
    struct SomeTag *next;
    struct SomeTag *prev;
} SomeTag;

With this form, after the typedef definition you can use either SomeTag or struct SomeTag to refer to the structure type.使用这种形式,在 typedef 定义之后,您可以使用SomeTagstruct SomeTag来引用结构类型。 However, since the typedef name has not been introduced when the structure body is defined, you have to use the notation struct SomeTag * inside the structure.但是,由于在定义结构体时没有引入typedef名称,所以必须在结构体内部使用符号struct SomeTag *

Although it is not mandatory, it is fairly conventional to use the same name for the tag and the typedef name.虽然这不是强制性的,但为标记和 typedef 名称使用相同的名称是相当常规的。 There is no conflict;没有冲突; they are in different namespaces ( §6.2.3 Name spaces of identifiers . C++ automatically provides a class or type name without the class , struct or union prefix when you define a class, struct or union, so using the same tag for both is consistent with C++. There are other schools of thought that say that the tag name should be different from the type name. One such convention is typedef struct SomeTag_s SomeTag_t; (using different suffixes). Beware that POSIX reserves the _t suffix (see §2.2 The compilation environment and §2.2.2 The Name Space — especially the line that says "Any header" reserves the suffix _t ).它们位于不同的命名空间中(§6.2.3 标识符的名称空间。C++ 自动提供 class 或类型名称,当您定义 class、结构或联合时,不带classstructunion前缀,因此对两者使用相同的标记是一致的与 C++。还有其他学派认为标签名称应该与类型名称不同。一个这样的约定是typedef struct SomeTag_s SomeTag_t; (使用不同的后缀)。注意 POSIX 保留_t后缀(参见§2.2 The编译环境§2.2.2 名称空间——尤其是“任何标头”保留后缀_t的那一行)。

Note that the function pointer declaration typeEptrArrayFunc() is equivalent to:请注意,function 指针声明typeEptrArrayFunc()等效于:

struct typeE typeEptrArrayfunc();

That declares a function type that returns a struct typeE and takes an indeterminate argument list (but it is not a variadic function type with , ... at the end of the prototype).这声明了一个 function 类型,它返回一个struct typeE并采用一个不确定的参数列表(但它不是一个可变参数 function 类型,在原型的末尾带有, ... )。 It is not a strict prototype in C90 through C17/C18.它不是 C90 到 C17/C18 中的严格原型。 In C23, it will become equivalent to:在 C23 中,它将等同于:

struct typeE typeEptrArrayfunc(void);

That's a function type that takes no arguments.这是一个 function 类型,没有 arguments。

The type typeEptrArrayfunc has nothing to do with pointers or arrays, despite the name.类型typeEptrArrayfunc与指针或 arrays 无关,尽管名称如此。 A similar comment applies to typeEArrayptr — that type is "pointer to struct typeE " and has nothing to do with arrays.类似的注释适用于typeEArrayptr — 该类型是“指向struct typeE的指针”,与 arrays 无关。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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