简体   繁体   中英

Why can you create typedefs to a struct that doesn't exist?

The following code compiles fine.

header.h:

typedef struct Placeholder_Type* Placeholder;

impl.cpp:

#include "header.h"

void doSomething(Placeholder t) {
  (void) t;
}
    
int main() {
  int *a = new int();
  doSomething((Placeholder)a);
}

compilation command:

clang++ impl.cpp

The type Placeholder_Type does not exist anywhere and it doesn't exist as a symbol in the output binary.

Why is it legal to create a typedef for a type that does not exist?

Why can I create a function using a type that doesn't exist?

Is this equivalent to just using void* but named "Placeholder"?

struct Placeholder_Type declares the struct Placeholder_Type (but doesn't define it), no matter where it appears. Even if it's inside a typedef . So you don't create a typedef to a struct that doesn't exist, but to one you just declared (and thus created, if the compiler didn't already know about it).

As for why, it's because this way you can keep the definition of a struct away from the public interface. For example, this is a typical way to implement an opaque object in C:

// mytype.h
typedef struct MytypeImpl* Mytype;

Mytype create_mytype();
void destroy_mytype(Mytype o);

void do_something_with_mytype(Mytype o, int i);

// mytype.c
struct MytypeImpl {
  int something;
  int otherthing;
};
Mytype create_mytype() {
  Mytype o = malloc(sizeof(*o));
  o->something = 0;
  o->otherthing = 0;
  return o;
}
void destroy_mytype(Mytype o) {
  free(o);
}
// etc.

Individual styles may differ in details, of course. But the key point is that the definition of the struct isn't visible outside mytype.c, so nobody can just access the data members.

Why not? As far as I know "exist" is not an official term. A type can be declared but not defined and it can be declared and defined. In the first case the type is said to be incomplete. There is not much you can do with an incomplete type. For example you cannot create objects of the type. You can however use pointers to an incomplete type. All the compiler needs to know is a declaration of the type. Further note that the typedef does contain a declaration of Placeholder_Type :

typedef struct Placeholder_Type* Placeholder; 
        ^---------------------^ declares the class named Placeholder_Type

As in your code you never create an object or use members of Placeholder_Type it does compile. I am not 100% sure, but I think there is also no UB. Casting the int doesn't look that nice, but as you never actually dereference the pointer there is nothing wrong.

For more inforamation I refer you to this very related questions about forward declarations: What are forward declarations in C++? When can I use a forward declaration?

Not all declarations are at the same time definitions.

For example you can declare a function that is not yet defined

void func( void );

and then refer to the name func .

Or you can declare a class as for example

class A
{
    friend class B;
    //...
};

Declarations introduce names in given scope.

Only when a name is used in an evaluation context it must be defined and has a complete type.

In this declaration

typedef struct Placeholder_Type* Placeholder;

there are introduced two names: Placeholder_Type and Placeholder . Placeholder is an alias for the pointer type struct Placeholder_Type* . Objects of pointer type are always complete objects. You can calculate its size using teh expression

sizeof( Placeholder )

It is the same as to have a pointer to the type void .

void *p;

The type void is always an incomplete type by the pointer type itself is a complete type. If you will not try to dereference the pointer or to apply the pointer arithmetic the referenced type is not required to be complete.

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