简体   繁体   中英

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. Typedefs are built upon their predecessor derived ultimately from some primitive type arrangement.

The following are acceptable in 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:

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. Up to a point, that's accurate: both GCC and clang accept the code without complaint if there are no warning options specified. 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 . There's also a future direction §6.11.5 Storage class specifiers which states:

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. 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? for which the majority viewpoint is that the answer is "No — with a possible exception for function pointers".

The empty structure definitions typedef struct typeC {} typeC; violate the rules of §6.7.2.1 Structure and union specifiers and in particular, ¶8 which says:

… 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. 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 . 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:

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. All structures must have a tag, and you must always use struct tag when referencing the type.

An alternative viewpoint is that you always use a typedef name for a structure type. 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. If you write struct SomeTag somewhere, it does not refer to the type shown. 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. 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.

Although it is not mandatory, it is fairly conventional to use the same name for the tag and the typedef name. 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 ).

Note that the function pointer declaration typeEptrArrayFunc() is equivalent to:

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). It is not a strict prototype in C90 through C17/C18. In C23, it will become equivalent to:

struct typeE typeEptrArrayfunc(void);

That's a function type that takes no arguments.

The type typeEptrArrayfunc has nothing to do with pointers or arrays, despite the name. A similar comment applies to typeEArrayptr — that type is "pointer to struct typeE " and has nothing to do with arrays.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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