I suggest you read more about opaque data types, and consider e.g. the FILE
structure.
In short, don't split your structure into "public" and "private" variants (that way lies madness and possible undefined behavior). Instead just declare a structure in a public header file, and have your functions return pointers to that structure or accept arguments that are pointers to that structure.
Then internally in the library you have a private header file which have the definition of the structure, and use that header file for your implementation.
Simple example
Public header file
#ifndef PUBLIC_HEADER_FILE_H
#define PUBLIC_HEADER_FILE_H
typedef my_private_structure MY_PUBLIC_TYPE;
MY_PUBLIC_TYPE *mylib_create(void);
void mylib_destroy(MY_PUBLIC_TYPE *ptr);
#endif
Private header file
#ifndef PRIVATE_HEADER_FILE_H
#define PRIVATE_HEADER_FILE_H
#include "public_header_file.h"
struct my_private_structure
{
// Some private fields here
};
#endif
Private library source file
#include "private_header_file.h"
#include <stdlib.h>
MY_PUBLIC_TYPE *mylib_create(void)
{
MY_PUBLIC_TYPE *ptr = malloc(sizeof *ptr);
return ptr;
}
void mylib_destroy(MY_PUBLIC_TYPE *ptr)
{
free(ptr);
}
You distribute public_header_file.h
together with your library. It's the header file that the users of the library will use.
The source of your library, and especially the private_header_file.h
file should not be distributed, or at least not installed if you make an open-source library.
Note that this scheme make all of the structure "private", which is usually a good idea since then you can modify it as you like without the users of the library needing to rebuild their applications using your library. To access members of the private structure you can use functions which simply returns the value of whatever member needs to be accessed.