I plan to create a zip folder in which to put some files with the extension .c and .h (for C programs) and .txt (for README for example or other) in order to create a C program template. Consider the following prototype file with many libraries and many functions called in main, called for example myprogram_bigfile.c
//(FROM HERE)//
/*Author(s): ...
Title: ...
Description(s): ...
Copyright and License(s): ...
*/
/*Start List of libraries*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <getopt.h>
#include "gen.h"
#include "defs.h"
#include "readlua.h"
#include "logic.h"
#include "state.h"
#include "session.h"
#include "files.h"
#include "play.h"
#include "io.h"
// and many more libraries...
/*End List of libraries*/
//(TO HERE)//
/*Start Function List*/
//Many functions...
/*End Function List*/
int main()
{
...
return something;
}
I ask you if it is possible to break down myprogram_bigfile.c into:
Thank you very much in advance.
So, let's go there - the canonical way of having larger projects is to have each C file built into a (non standalone) "object" file - in modern systems these will have the .o
extension. (Back in my time, handcrafting some C in DOS these were .obj
s)
I will be using gcc
for the example - other compilers should be analogue:
when you have a single file project, with the main
function and everything else in the same file (and I can tell you colleges in my country to teach this and nothing else, getting a lot of quiters), you can call gcc like:
gcc myprogam.c -o myprogram
(I guess for Windows, once GCC is installed it is the same, just the executable must have the .exe
extension: gcc myprogram.c -o myprogram.exe
)
Now let's settle for two different source files:
project.c
# include <stdio.h>
#include "project.h"
int main() {
int value;
value = other_func();
printf("The value is %d\n", value);
return 0;
}
And
somelib.c
int other_func() {
return 23;
}
This could have a single .h
file that would expose other_func
-but as projects gets larger, the practice is to: each .c
file have a corresponding ".h" file, and the final project have a .h
file which includes all the others.
By making hierarchical .h
files this way, you can have all the functions in your project with just one or a few includes, ready to use.
somelib.h
int other_func(); // this is the only thing the main file needs to see about the library
project.h
#include "somelib.h"
And with these files in place, you could manually build the object files - not to executable, with the -c
GCC flag:
$ gcc somelib.c -c somelib.o
$ gcc project.h -c somelib.o
Due to the .h
file being included into project.c
it won't complain about not knowing about other_function
due to the forward declarations in the included header files.
Then the final step links the object files together:
$ gcc project.o somelib.o -o project
Notice the -o
switch telling it should generate a standalone executable.
ANd that is it.
You can see this gets complex fast -so back in the 1970s people came around with Makefiles - which can still be used for simple projects. It is really cryptical (in my opinion) in today's world of automations and very high level language - for these four files, a Makefile that works is this:
Makefile
TARGET = project
SRCS = project.c somelib.c
OBJS = $(SRCS:.c=.o)
HEADERS = project.h somelib.h
all: $(TARGET)
$(TARGET): $(OBJS)
gcc $(OBJS) -o $(TARGET)
%.o: %.c $(HEADERS)
gcc -c $< -o $@
clean:
rm $(OBJS) $(TARGET)
The Makefile has really to be named literally "Makefile" -
and once it is there, you type make
in the same directory, to have everything built - and moreover: with dependencies checked, so that upon changing one file, it will rebuild only the files which depend on that one.
(sorry, don't ask me more about the Makefile- I just modified something I got on the internet) - this can be fine up to a few tens of files, and a fixed compiler and build stack
Over the 90s as projects got more complex there came the "GNU automake" - a project which will generate the Makefile based on config options you pass to the autogen.sh
script - and it goes from testing which compilers are available in the system, and generating the proper changes into the makefile (it also needs a template for the makefile).
More modern workflows use frameworks called ninja and cmake - those are known to be much more maintainable and friendly - both project for authors as for users who will occasionally build a project. You could get some tutorials for one of those, and maybe skip the Makefile part altogether.
-- And
finally - that is all there is to it:
cut your C files in proper subfolders, with a few functions grouped by domain in each file, and have a simple main file which just has the entrypoint and calls upon the others.