C for Java Programmers: Objects

George Ferguson

Summer 2018
(Updated Summer 2021)

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 implement representations of complex objects, as in object-oriented programming. C is most definitely not an object-oriented programming language, but with a few basic coding patterns and some discipline, you can have many of the benefits of OOP in your C programs.

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 need to represent employees of a company for use in a paroll application. Suppose that all we care about is that employees have a name and an id number. How would you represent such a thing in Java?
A class something like the following: class Employee { String name; int id; }
How would you do it in C?

If you have no idea, check out Chapter~7 of the C for Java Programmers guide.

In C, you use a struct to define a “structured type” (a type composed of other types): struct Employee { char* name; int id; };

The only difference other than class vs. struct is that there is no builtin string type in C. Instead, strings in C are simply arrays of single-byte characters (chars). Since arrays in C are simply pointers to the first element of the array, strings are type char* in C.

By the way, if you skipped the C for Java Programmers document, now is probably the time to take a look.

In Java, what code is responsible for constructing new instances of employees?
The constructor in your Employee class: public Employee(char *name, int id) { this.name = name; this.id = id; } You create a new instance by calling the constructor: Employee e = new Employee("Alan Turing", 123);
How would you do that in C?
First, define a “constructor”—a function that allocates space for an instance of the struct Employee type, initializes it, and returns the pointer to it. struct Employee* new_Employee(char *name, int id) { struct Employee* this = (struct Employee*)malloc(sizeof(struct Employee)); if (this == NULL) { return NULL; // Out of memory... } this->name = name; this->id = id; return this; } This pattern is described and explained in Section 8.7 of the C for Java Programmers document.

Note that you will need to #include <stdlib.h> to use malloc in your program.

Also note that since strings (char*’s) are reference types (in both C and Java), and since C does NOT manage memory for you, there are some subtleties involved in storing strings passed as arguments inside objects. For now it’s ok and we will return to it at the end of this lesson.

Then just like in Java, you create a new instance by calling the “constructor”: struct Employee* e = new_Employee("Alan Turing", 123); The only differences are the explicit need to indicate “pointer to struct” in the type (which you could avoid with a typedef; see the Reusable Code lesson), and the underscore that makes new part of the function name rather than a reserved word (in Java) that invokes a constructor.

In Java, how would you make it so that employee objects get printed in a more meaningful way than the default “Type@Address” form?
Add a toString() method to your Employee class, something like: public String toString() { return "Employee[" + this.name + "]"; } You could also write a method that just prints this to System.out (or some other I/O stream).
How would you do that in C?
Allocating and concatenating strings is not as easy in C as it is in Java. But it's easy to write a function that prints an object (of some type) to standard output: void Employee_print(struct Employee* this) { printf("Employee[%s,%d]", this->name, this->id); } Note that you will need to #include <stdio.h> to use printf in your program.

A couple of things to note about this:

  • The type name (“Employee”) is included in the function name since we may have other types that need a print function, but there can only be one function with a given name. This applies to any function that is intended to be a “method” (that is, to work on instances of a type).
  • The “method” (function) is passed a pointer (reference) to the instance of the type as its first argument (its only argument in this case). In Java, this is magically arranged for you when you write something like e.toString(), and the value of e becomes the value of the special variable this. This C code works the same way, but unmagically.
  • The function does not ask printf to output a newline. That allows whoever is calling the function to handle the layout as it sees fit. You could do otherwise.

Using what you have so far, write a complete program that constructs and prints several employee objects.
Just add a main function to what we have already. Something like: int main(int argc, char* argv[]) { struct Employee* e1 = new_Employee("Alan Turing", 123); Employee_print(e1); struct Employee* e2 = new_Employee("Edsgar Dijkstra", 456); Employee_print(e2); struct Employee* e3 = new_Employee("Alan Kay", 789); Employee_print(e3); } When you run this program, the employees will all print one after each other on the same line with no spacing or newlines. You could add calls to printf in between if you wanted.

Download full program: employee1.c

You should know that encapsulation is good coding practice. You should know what encapsulation is. How would you encapsulate the properties of an Employee object in Java?
First you would make the instance variables private or protected. Then you would write getters and setters for them: public String getName() { return this.name; } public void setName(String name) { this.name = name; } public int getId() { return this.id; } public void setId(int id) { this.id = id; }
C doesn’t have access control modifers like Java’s private and protected (although there are ways to achieve similar effects). But you can still write getters and setters (a.k.a. accessors and mutators) for the properties of an Employee. So do that.
A getter takes a reference to an instance of a class (structured type) and returns the value of some property of the instance. char* Employee_get_name(struct Employee* this) { return this->name; } int Employee_get_id(struct Employee* this) { return this->id; } A setter takes a reference to an instance of a class (structured type) and a value, and sets the value of some property of the instance to the given value. void Employee_set_name(struct Employee* this, char *name) { this->name = name; } void Employee_set_id(struct Employee* this, int id) { this->id = id; }
You can use these patterns to define “classes” in C, and then create and modify instances of them. These patterns are so regular that it is possible to write macros using #define to generate the code for you. Macros are beyond the scope of this tutorial, but suffice to say that C++ started from the macros that programmers had developed for doing object-oriented programming in C.

Download full program: employee2.c

One last issue for this lesson: memory management.

If you read the C for Java Programmers document, you know that Java looks after allocating and deallocating memory for you but C does not. We’ve already seen how to use malloc to allocate memory for new instances of reference types, which is what new does in Java.

What about deallocating instances when we no longer need them? For example, when a local variable goes out of scope, its value “vanishes.” If that value is a reference to an object, and there are no other references to the object, then there is no way for your program to refer to the object. Or consider what happens when an element is removed from a list, tree, or graph. The node storing the element is no longer part of the collection and almost certainly will never be refrenced again. If the memory used by objects like these is not released by to the system, your program may over time run out of memory.

In Java, the Java runtime system looks after recognizing and deallocating “dead” objects. In C, you the programmer must figure out when an object can be deallocated and call free to release the memory back to the C runtime system. You may free any pointer obtained from malloc. Attempting to free a pointer that was not obtained from malloc or attempting to dereference (use) a pointer that has already been free’d will cause a range of problems. These are discussed briefly in the C for Java Programmers document.

For simple programs with simple data structures, you can just call free on pointers when you know they are no longer needed. But for for more complex types, such as in object-oriented programming, it’s better to have an explicit function to free references to instances of the type.

For starters, write a function to free an Employee instance.

void Employee_free(struct Employee *this) { free(this); } It’s that simple. But if this was all there was to it, why would we bother introducing a function to do it?

What if the “object” you are freeing contains references to other objects? For example, an Employee might have a member (“instance variable”) that refers to another Employee that is their manager. A list node would have a reference to the next node in the list (and perhaps the previous node also). A tree node has references to its children. These are complicated cases and the last two are discussed in subsequent lessons.

But our struct Employee does have a member that is of a reference type. What is it?

The name member is of type char*, pointer to char, which is a reference to a block of memory containing characters.
Adjust your Employee_free method so that it frees the name of the Employee instance also.
void Employee_free(struct Employee *this) { free(this->name); free(this); } Note that you must free the name before you free the memory containing the reference to the name (that is, the struct Employee instance). If you did it in the other order, you would be using something, namely the value of the name member of the struct Employee after the memory for the struct Employee had been freed. That is not allowed, as noted above.
Add code to your main function to free one of the allocated Employees.
Add the following at the end of main: Employee_free(e1);
Run the program. Did it work? It might, but you should get a crash, indicated by a more or less helpful message depending on your platform (e.g., “segmentation fault”). What’s the problem?

One of the rules (noted above) is that you may not free a pointer that was not obtained from malloc. The value of the name member of the struct Employee is indeed a pointer. But did we get it from malloc?

No we did not. It comes from the string literal "Alan Turing". Since we did not get it from malloc, we cannot give it to free. On the other hand, the pointer stored in the instance of the struct Employee might have been obtained from malloc. The fact is that the C compiler and runtime can’t know for sure either way.

There are two ways you could solve this problem. What are they?
  1. You could decide not to free the name when you free an instance of a struct Employee. In that case it’s someone else’s problem to free the memory used by the name if and when it is appropriate to do so.
  2. You could require that the name stored in a struct Employee always come from malloc, and therefore always be ok to free.
What are the pros and cons of the two approaches?

The first approach makes sense if names are being used in other places in your program. In other words, if the reference to the name can be stored in program variables or as the value of members of other reference types (other “objects”).

On the other hand, your program somehow needs to know when a name is no longer being used at all, in order to know when to free it. If the name is never freed, then its memory will never be returned to the system, even if the program no longer has any way to refer to it. This is called a “memory leak,” and eventually your program will run out of memory.

A key aspect of Java is that it automates the process of figuring out when to deallocate objects and free memory. This called “garbage collection.” You can look it up. There are pros and cons to it also. Welcome to Computer Science.

The second approach seems easier, but unfortunately C can’t enforce the requirement automatically. So it’s up to you the programmer to make sure that the name in a struct Employee is always allocated by malloc and that the reference is not shared with any other part of your program. The next step in this lesson shows you how.

Furthermore, if each reference to the name is unique, and the name is used in several places in the program, then there will be multiple copies of the same name. Not only is that probably a waste of memory, but it is possible to change the contents of one of the copies of the name without changing the others. Indeed, the code almost certainly doesn’t even know about the other copies.

Another important aspect of Java is that Strings are immutable (cannot be changed). This means that, in principle, only one copy of any string is ever needed and it can be shared between objects that need to refer to that string. And in fact, Java does try to minimize copies of strings, for example by prescribing that all string literals or strings computed by constant expressions, where the strings have the same content, refer to the same String object. However you should know from a discussion of the difference between identity (“==”) and equality (“equals”) that in general, strings with the same contents are not always identical. See The Java Language Specification, 4th Edition, Example 3.10.5-1, page 38.

Let’s fix the program so that the name of a struct Employee is always allocated by malloc. First, write a function name strdup (“string duplicate”) to allocate, initialize, and return a new copy of a given string.
Here’s a function to duplicate (allocate, initialize, and return a new copy of) a given string: char* strdup(const char* s) { char *t = (char*)malloc(strlen(s)+1); strcpy(t, s); return t; } You will need to #include <string.h> to get strlen and strcpy. Or you could write them yourself. Try it!

And by the way, once you’re using the string functions of the C standard library, you may find that strdup is already there for you. But it’s better to know how it works for yourself, right? Furthermore, it's not part of C Standard Library for the “C99” dialect of C, which is what we recommend you stick with until you know better.

Use strdup to ensure that the name of an struct Employee is always allocated by malloc so that it can be safely freed.
Change your “constructor” new_Employee to make a copy of the name and store the copy in the “object”: this->name = strdup(name); Note that you must also do this in any other place that sets the name of a struct Employee, like for example in Employee_set_name. Make both changes, recompile the program, and it should run without crashing.

Download full program: employee3.c

This is the method used by Kernighan and Ritchie in The C Programming Language, 2nd Ed., so it must be right. ;-) But remember the pros and cons discussed a few steps back when you go on to develop your own programs in C. Or for that matter in Java!
Back to Tutorial Home