admin管理员组

文章数量:1122832

#ifndef HANDLE_H
#define HANDLE_H

struct List;
typedef struct List* HANDLE;

struct Person;
typedef struct Person Person_t;

HANDLE list_create(void);
void list_destroy(HANDLE);
void list_push_front(HANDLE, const Person_t*);
void list_pop_front(HANDLE);

#endif  // HANDLE_H

Question: Are the forward declarations (List and Person) needed, and is it a good practice to forward declare them?

#ifndef HANDLE_H
#define HANDLE_H

struct List;
typedef struct List* HANDLE;

struct Person;
typedef struct Person Person_t;

HANDLE list_create(void);
void list_destroy(HANDLE);
void list_push_front(HANDLE, const Person_t*);
void list_pop_front(HANDLE);

#endif  // HANDLE_H

Question: Are the forward declarations (List and Person) needed, and is it a good practice to forward declare them?

Share Improve this question edited Nov 21, 2024 at 15:47 Jonathan Leffler 753k145 gold badges946 silver badges1.3k bronze badges asked Nov 21, 2024 at 13:49 rand_programrand_program 1,1701 gold badge4 silver badges19 bronze badges 5
  • Why not just put the complete structure definition in the header? – OldBoy Commented Nov 21, 2024 at 14:00
  • 1 @OldBoy: What is an opaque pointer in C? What defines an opaque type in C, and when are they necessary and/or useful? – Eric Postpischil Commented Nov 21, 2024 at 14:05
  • 2 There is no need for struct List; or struct Person; in the code shown because these structure tags are introduced by typedef struct List *HANDLE; and typedef struct Person Person_t;, respectively. – Eric Postpischil Commented Nov 21, 2024 at 14:07
  • See Is it a good idea to typedef pointers? TL;DR — the answer is generally "No", with an exception for function pointers. Using 'opaque types' is a powerful information hiding technique. You could simply write typedef struct Person Person_t; (one line, not two). Similarly with the list structure. Normally, the structure tag and the typedef name should be related, as with Person and Person_t. Emulating C++, you can use the tag name as the typedef name (typedef struct List List;) — C++ achieves the same effect automnatically. – Jonathan Leffler Commented Nov 21, 2024 at 15:51
  • There are cases where the forward declaration is either required (to resolve cyclic dependencies) or highly recommended (to avoid defining useless parameter types), but this is not one of them. – Ian Abbott Commented Nov 21, 2024 at 16:44
Add a comment  | 

3 Answers 3

Reset to default 2

Forward declarations are only needed when the struct definition isn't already visible earlier inside the same translation unit or otherwise you wouldn't be able to use the struct. Forward declarations can be done with or without typedef. Meaning that the struct Person; etc parts are redundant here.

Regarding best practices:

  • It is often good practice to hide implementation details of a struct to a caller which should not access it directly. That is known as "opaque type" and is one way to achieve private encapsulation. See How to do private encapsulation in C?

  • It is widely considered as very bad practice to hide pointers behind typedef. Even in the case of opaque types.

    An example of one of the most infamous code bases out there is the Windows API, which uses something called HANDLE very similar to what you have here. One of many problems with this is that the programmer doesn't realize that they are dealing with a pointer and therefore add one more level of needless indirection, leading to slower code which is harder to read. Another problem is that they might think they are performing a hard copy of the whole object when assigning one HANDLE to another. Just don't do this.

The separate line declaring each struct type isn't strictly required. This:

typedef struct List* HANDLE;

Besides defining the type HANDLE, also declares struct List, and the same for the other typedef.

So your header can safely contain the following:

#ifndef HANDLE_H
#define HANDLE_H

typedef struct List* HANDLE;
typedef struct Person Person_t;

HANDLE list_create(void);
void list_destroy(HANDLE);
void list_push_front(HANDLE, const Person_t*);
void list_pop_front(HANDLE);

#endif  // HANDLE_H

A forward declaration for a structure is not required before a typedef declaration for that structure. So instead of that

struct List;
typedef struct List* HANDLE;

struct Person;
typedef struct Person Person_t;

you may just write

typedef struct List* HANDLE;

typedef struct Person Person_t;

Pay attention to that if you are going to use a typedef declaration for an enumeration then the enumeration shall be defined either before the typedef or inside the typedef.

本文标签: cAre forward declarations needed when the typedef declaration is doneStack Overflow