C for Java Programmers: Reusable Code

George Ferguson

Summer 2018
(Updated Summer 2020)

This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License
Creative Commons License

In this lesson, you will learn how to write code that you can reuse and share between programs using header files and separate compilation.

Each step of the lesson asks you to answer a question or write some code. Do the steps yourself before checking your work by revealing the answer.

Suppose that we are developing several different programs that need to represent employees. To support this, we need to make the struct Employee “class” that we created in the Objects lesson reusable.

To do this, we need to divide our code into two parts: a “header” file that provides all the declarations needed to use the code, and an “implementation” that provides the actual code that implements those declarations. You can think of the header file as defining the interface (in Java terms), and the implementation as providing, well, the concrete implementation of the interface.

Start with the header file. Create a file named Employee.h, that is, the header (“.h”) for the Employee “class.” In this file, put the declarations only of all the functions defined in employee3.c (that is, their “signatures,” but not the body code, like an interface in Java).

struct Employee* new_Employee(char *name, int id); void Employee_print(struct Employee* this); char* Employee_get_name(struct Employee* this); void Employee_set_name(struct Employee* this, char *name); int Employee_get_id(struct Employee* this); void Employee_set_id(struct Employee* this, int id); void Employee_free(struct Employee *this);
There are a couple of things to fix before we can use this:
Your Employee.h should like this: typedef struct Employee* Employee; extern Employee new_Employee(char* name, int id); extern void Employee_print(Employee this); extern char* Employee_get_name(Employee this); extern void Employee_set_name(Employee this, char* name); extern int Employee_get_id(Employee this); extern void Employee_set_id(Employee this, int id); extern void Employee_free(Employee this); Note that the header file does not define what a struct Employee actually is. That information about the implementation is hidden from users of the Employee “class”. This is called an opaque type since other code cannot “see” the implementation inside.

Download file: Employee.h

Now for the implementation of our Employee “class”. Create a file named Employee.c, and copy the structure and function definitions (including bodies) from employee3.c.
#include <stdlib.h> #include <stdio.h> #include <string.h> #include "Employee.h" // This function is in the C standard library but so what char* strdup(const char* s) { char *t = (char*)malloc(strlen(s)+1); strcpy(t, s); return t; } struct Employee { char* name; int id; }; Employee new_Employee(char *name, int id) { Employee this = (Employee)malloc(sizeof(struct Employee)); if (this == NULL) { return NULL; // Out of memory... } this->name = strdup(name); // See text this->id = id; return this; } void Employee_print(Employee this) { printf("Employee[%s,%d]", this->name, this->id); } char* Employee_get_name(Employee this) { return strdup(this->name); } void Employee_set_name(Employee this, char *name) { this->name = name; // See text } int Employee_get_id(Employee this) { return this->id; } void Employee_set_id(Employee this, int id) { this->id = id; } void Employee_free(struct Employee *this) { free(this->name); free(this); } The answer to the question of size of struct vs. size of pointer: a pointer is an address, 32 or 64 bits; a struct is whatever size is required to store its members.

Download file: Employee.c

Now any program that wants to represent employees has only to #include "Employee.h" and then use the type Employee and the functions defined in the header file. The program will have to be linked with the result of compiling Employee.c. We will see how that works shortly.

In a new file named Employee_test.c, write a program that tests the new Employee “class”. Use only the types and functions defined in the Employee.h header file. Hint: Keep it simple to start.

#include <stdio.h> #include "Employee.h" int main(int argc, char* argv[]) { Employee e1 = new_Employee("Alan Turing", 123); Employee_print(e1); printf("\n"); }

Download file: Employee_test.c

To compile your program, you need to compile both Employee.c and Employee_test.c, and link them into a single executable program.

If you are using an IDE, it may have looked after this for you. If so, try running it.

If you are working from the command shell, most compilers can do this for you in one command: cc -o employee_test Employee.c Employee_test.c You may need to use both gcc or clang or something else instead of cc) for the C compiler.

Then run the resulting executable (which you specified with the -o option): ./executable_test You should see the output of the program.

I strongly urge you to use the options -std=c99, -Wall, and -Werror. These tell the compiler to use the “C99” version of the ANSI C Standard, to report all warnings, and to make warnings into errors so that even though you may not think they're important (“they're just warnings, right?”), your program won't compile if there are any warnings. You can do this on the command line as follows: gcc -o employee_test -std=c99 -Wall -Werror Employee.c Employee_test.c You can arrange for this in your IDE also, although the method depends on the IDE.
Now you have a program whose source code is split between two files: Employee.c and Employee_test.c. These are connected by the definitions in Employee.h. Why did we do it this way? Why not just have the code for Employees in the same file as main? Try it! Then think about it.
If you tried it, it should have worked. You can just paste the code from Employee.c into the top of Employee_test.c instead of #include "Employee.h". Or, equivalently, change #include "Employee.h" to #include "Employee.c", which has the effect of having the C compiler do that for you (actually, the pre-processor, but I digress). So why not do it that way?

If all the code was in one file, then it would all have to be recompiled every time we compiled the program (since there's only the one file to compile). That might not be a problem for this small example, but if there was a lot of code defining employees, then it would be a waste to constantly recompile it even though nothing has changed. Furthermore, there might be many programs that need to represent employees, and they would all be constantly recompiling that code even though they never change it. Remember that when C was created, computers were much, much slower and had less memory than they do now. Compiling programs used to take real time.

Furthermore, for you to include the source code for Employees in your program, I need to give it to you. In the first place, this may be an intellectual property issue in some cases of commercial software or homework assignments. There is a long history of debate regarding “free” and “open source” software if you are interested.

But regardless of your opinion about code as intellectual property, if I give you the source code for the implementation, then then you know its internal details. You can write your code to take advantage of that, which would be fine, unless I want to change my implementation. For example, I might want to replace an array list implementation of a list with a linked list. If all I give you is the header file, then I can change the implementation as long as I don't change the interface promised in the header file.

Summarizing:

  • Separating the interface and the implementation improves encapsulation and simplifies reuse and sharing.
  • Implementations need only be compiled when they change. The resulting object code can be linked with programs that use the code as defined it its header file.
This is called separate compilation.

Back to Tutorial Home