Memory-File Multi-Module Implementation

At 205 lines of code in last week’s Lesson, my memory-file project is getting larger by the day. At some point, the source code files must be broken out into separate modules, then compiled and linked separately. This is how I handle all large projects when it becomes too unwieldly to edit everything in a single file.

It’s up to the programmer to decide which functions dwell in which source code file. For a game I wrote, I had separate modules for initializing, a main loop, moving pieces, and recording scores. A module, or separate source code file, can exist for each function. No hard rules exist.

Once you separate a single source code file into multiple modules, you should also consider writing a local header file to handle all the defines, includes, and prototypes. This step greatly improves efficiency and makes adding new functions and modules a lot easier.

For the memory-file programs, I split the file from last week’s Lesson into three separate source code files:

main.c contains the main() function only
memfile.c contains all the memory file functions
memfile.h contains the required includes, structure definition, defined constants, and all the function prototypes

The main.c and memfile.c source code files contains the same code shown last week, just in separate files. To each I added this preprocessor directive:

#include "memfile.h"

The double quotes direct the preprocessor to look in the current directory for the file memfile.h. This file contains material pulled from the original, long source code file, plus other necessities:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/* defines */

#define DEFAULT_SIZE 2048

/* structures */

struct mem_file {
    void *address;
    char *name;
    int size;
    int offset;
    char *timestamp;
};

/* prototypes */
int mem_seek(struct mem_file *m, long position, int whence);
int mem_putc(int c, struct mem_file *m);
int mem_getc(struct mem_file *m);
struct mem_file *mem_open(char *fn);
void mem_close(struct mem_file *m);

The file includes all the header files required by the other modules, shown first. Then comes the defined constant and structure used by the various functions.

The last part of the file lists all function prototypes. These are the required. If you create a multi-module project and forget the prototypes, the linker pukes up a lot of errors. Therefore, setting them all in a local header file makes a ton of sense.

To create the program, you must compile each module then link the object code files to create a program. In the terminal window, a single command handles the task:

clang -Wall main.c makefile.c -o main

I use the clang compiler, though you can specify gcc or whatever you use.

The -Wall switch turns on all warnings.

Next come the C source code files, one after the other.

The -o switch directs clang to send output the file name that follows, main.

At the end of the operation, and upon success, the program file main is created.

Things get complicated as the number of source code modules increases. Also, you often don’t need to compile source code files that haven’t been updated. To make the process more effective, you can use the make utility to ensure that the source code files are compiled and linked as needed. I cover this utility in next week’s Lesson.

Leave a Reply