.h files are, generally, used to explain how something works, and .c files actually do the work.
Your headers will potentially contain prototypes (so functions can be properly called), structs and typedefs (so types your functions make use of can be used), macros (such as “#define PATH_MAX 1024”); things of that nature.
If you have a function that, say, gets a username, you might need to use that from various source files you have. So you have a file username.h:
Code:
#define MAX_USERNAME_LEN 16
int get_user_name(char *);
You'll also have a .c file (perhaps called username.c) that implements the get_user_name() function. It includes the .h file, because it might make use of MAX_USERNAME_LEN, and so you can type-check the function: if you accidentally implement the function as returning void, but the header says int, your compiler will complain. This is a good thing.
Then, in each of your .c files that needs to call get_user_name(), you #include "username.h". username.h should be in the same directory as everything else, although you can usually tell your compiler to look in various different places for headers. Once a .c file includes username.h, it can create a buffer of the appropriate size (because MAX_USERNAME_LEN is visible), and it knows how to call the function (because get_user_name() is visible).
All #include does is paste the contents of one file inside another. It'd be the same as copying the contents of a header in place of the #include, but of course using #include is much less error prone. Sometimes, in old code, you'll see .c files that manually include declarations for functions that are defined in other .c files. That should be avoided (for maintenance reasons), and header files are the way to avoid it.
You will, in your .h file, want to use include guards: these prevent a header from being included multiple times in the same translation unit (for example, you could #include "foo.h" and #include "bar.h", but foo.h also does #include "bar.h", which would copy it in twice). That's as simple as:
Code:
#ifndef PROJECTPREFIX_HEADERNAME_H
#define PROJECTPREFIX_HEADERNAME_H
/* header contents go here */
#endif
You can use any legal macro name as an include guard. Do not start the name with an underscore, though: you'll see a lot of people do that, but it's wrong. Such a name belongs to the implementation, and you should not violate its namespace.