My experience is that having code grouped into source/headers according to functionality increases ability to understand, test, maintain and reuse it.
How much code goes into each file will really depend on how complex the encapsulated functionality is. For example, I have a source file containing functions to create and append to WAV files. They are relatively small, and because they are cohesive, I can use them in whatever project I have that needs to create WAV files without bringing in a lot of other baggage. Other files may be large (or very large) but if the functionality is cohesive, I get the same benefits.
One thing that tripped me up when I started doing this was “multiple inclusions” caused by including the same header in a project multiple times without “protecting” it. Since you say you are a newbie, I’ll add a quick sample of what you can do to prevent it.
/**
@file my_header.h
*/
ifndef MY_HEADER_H // <- Prevents multiple inclusions
#define MY_HEADER_H // <- ...
#ifdef __cplusplus // <- Allows this to be called from c++
extern "C" { // <- See "name mangling for more info.
#endif // <- ...
/**************************/
// your stuff goes here
struct my_struct
{
// ...
};
// function prototypes, etc.
/**************************/
#ifdef __cplusplus
}
#endif
#endif // MY_HEADER_H