Answering your first question, I recommend to encapsulate your structures using opaque pointers (a.k.a Handles).
For example, you may declare a handle to a linked list (here MS-like naming):
typedef struct linked_list_t* HLINKEDLIST;
We assume that linked_list_t is a generic one (composed of void pointers).
This way you can hide what a "handle" to a linked list is, or in what form is implemented (information hiding):
HLINKEDLIST LinkedListCreate();
LinkedListAdd(LLELEMENT v);
LinkedListCopy(HLINKEDLIST dst, const HLINKEDLIST src);
Handle subtypes are also commonly defined, such as PHLINKEDLIST (pointer to a linked list handle).
Related types can also be defined for convenience (and to use the limited information hiding available in C). e.g: the linked list element type can be defined as
typedef void* LLELEMENT;
There are good books on Data Structures in C to check. This is good: http://www.amazon.com/Interfaces-Implementations-Techniques-Creating-Reusable/dp/0201498413
Also note that LLELEMENT is actually compatible with void* so if you are defining other type def as:
typedef void* SYSTEMDATA;
SYSTEMDATA is compatible with LLELEMENT, so the compiler won't complain on:
int QuerySystemData(SYSTEMDATA* sd);
and calling:
QuerySystemData(lle);
where lle is of type LLELEMENT.
This type checking can be enforced encapsulating simple members in structs. If I don't remember bad, declaring STRICT in a program using windows.h causes handles to be type-safer (incompatible between). Definitions like the following are common:
typedef struct __HWND
{
int __handle;
} __HWND;
typedef __HWND* HWND;
If the simpler definitions were:
typedef int HWND;
typedef int HBITMAP;
The two handles would be type compatible and interchangeable on functions expecting to work with windows and functions expecting to work with bitmaps (potential horrible bugs).