Your global header file should define a type name without its implementation, along with prototypes of functions to create, destroy and handle data of that type.
header.h
#ifndef HEADER_H_
#define HEADER_H_
struct XyzData;
typedef struct XyzData XYZDATA;
typedef struct XyzData *P_XYZDATA;
P_XYZDATA CreateEmptyXyzData();
P_XYZDATA CreateFilledXyzData(int param1, int param2);
void DestroyXyzData(P_XYZDATA pData);
int ModifyXyzData(P_XYZDATA pData, int action, int param);
#endif
Then you need an internal header, which provides an implementation for the structure.
xyzintheader.h
#ifndef XYZINTHEADER_H_
#define XYZINTHEADER_H_
#include "header.h" // include the XyzData declaration
struct XyzData { // and add its definition
int par1;
int par2;
};
#endif
Then files with implementations for specific routines will use the internal header, because they need to know the structure details to access them.
xyzcreate.c
#include "xyzintheader.h"
#include <stdlib.h>
P_XYZDATA CreateEmptyXyzData() {
return calloc(1, sizeof(XYZDATA));
};
xyzcreatf.c
#include "xyzintheader.h"
#include <stdlib.h>
P_XYZDATA CreateFilledXyzData(int param1, int param2) {
if (P_XYZDATA pData = malloc(sizeof(XYZDATA))) {
pData->par1 = param1;
pData->par2 = param2;
return pData;
}
return NULL;
};
xyzdestr.c
#include "xyzintheader.h"
#include <stdlib.h>
void DestroyXyzData(P_XYZDATA pData) {
free(pData);
}
xyzmodif.c
#include "xyzintheader.h"
int ModifyXyzData(P_XYZDATA pData, int action, int param) {
if (! pData)
return -1;
switch (action) {
case 0:
// do something
pData->par1 = pData->par2 = 0;
return 0;
case 1:
// do something
pData->par1 += param;
pData->par2 -= param;
return 0;
}
return -2;
}
And external modules will use the public header, because they only need to know the structure exists and what tools to use to handle it.
source.c
#include "header.h"
void SomeExternalLogic()
{
if (P_XYZDATA pData = CreateEmptyXyzData()) {
ModifyXyzData(pData, 0, 0);
ModifyXyzData(pData, 1, 3);
if (ModifyXyzData(pData, 13, 17) < 0)
WriteSomeErrorMessage();
DestroyXyzData(pData);
}
}
struct my_type;
, without actual contents) and prototypes for functions which will know its contents. Other .c files can then reference it as a pointer (struct my_type*
) and use the mentioned functions to access it contents indirectly. – Echino