Introduction to objects

In terms of linking, an object is a file containing compiled code which is ready to be linked with other objects to form larger entities such as libraries or executable files.

An object may contain the code for one or more functions depending on how the source code was organized. Usually, an object contains the compiled code from a single source file, along with the #included files, but can also contain code from other files as we shall see. Each such grouping of sources forms a module, and each module is compiled to a single object. These terms are often used interchangeably.

In this section, we'll take a look at some of the ways sources can be organized, and some of the effects of this organization on the objects created.

A basic code module

Let us return to the example from the previous page. Recall that we have a file called mymain.c which contains the following code:

	#include 

	int a_global;

	int func ( int a, int b )
	{
		return (a+b);
	}

	void main (void)
	{
		int a_local = 7;

		a_global = 5;

		printf ( "The result is %d\n", func(a_local, a_global);
	}

If we were to compile mymain.c, we would normally wind up with a single executable file containing compiled code from several places:

It is this last point that makes things interesting. Because the entire module gets copied into the executable when any entity in it is used, our executables can become unreasonably large. Let's move on to another example. Suppose we have a file called one2ten.c containing the following code:


	int one(void) { return 1; }
	int two(void) { return 2; }
	.
	.
	.
	int nine(void) { return 9; }
	int ten(void) { return 10; }

If we compile this file to an object with cc -c one2ten.c, the compiler will create a single object file containing the code for each of these functions.

Suppose we now write an application called Three.c which calls the functions three():


int main(void)
{
	return three();
}

If we compile Three.c into the object Three.o with cc -c Three.c, then the code for main() is compiled and placed in the object, but the reference to three() is left unresolved. In other words, the code for the function three() does not get put in the object.

Suppose that we instead compile Three.c into an executable called Three with cc -o Three Three.c one2ten.o. Then, the code for main() is compiled, the reference to three() is resolved, and the code from Three.c is linked together with the code in the object one2ten.o. Because this object contains the code for all of the functions in the source file one2ten.c, the executable will contain code for nine functions that it will never use.

Reorganizing to minimize executable size

We now see why executables can become very large very easily. Let's change how our sources are organized to make the size of the final executable smaller. The big problem in the above example is that three() is contained in an object with nine similar, but not prerequisite, functions. three() does not need any of the other functions in one2ten.c to operate, but because they are in the same source, they will all be compiled together.

If we split one2ten.c into smaller pieces, then the final executable will be smaller. Let's divide it into two parts one2five.c:


	int one(void) { return 1; }
	.
	.
	.
	int five(void) { return 5; }

and six2ten.c:

	int six(void) { return 6; }
	.
	.
	.
	int ten(void) { return 10; }

Now, we compile each of these to an object using cc -o one2five.c and cc -o six2ten.c. This will create the files one2five.o and six2ten.o. We can now recompile the application Three.c using the command line cc -o Three Three.c one2five.o. Note that we didn't include six2ten.o because we don't call any of the functions contained in it.

Now, the executable will contain the code for main() and for the functions one(), two(), three(), four(), and five(). Thus, it will be smaller than the previous version which included the rest of the functions originally found in one2ten.c.

The current version still contains four functions that are never used, so maybe we should break the source file one2ten.c into ten separate pieces, each of which contains one function. This is okay because none of the functions in one2ten.c need to be hidden from the outside world. We won't worry about that for now though.

We now have ten source files, say one.c through ten.c, which we can compile to ten objects, one.o through ten.o. When we link these with our application Three.c, the final executable will contain the code for main() and three(), but not for any of the other functions that were originally in one2ten.c. This minimizes the size of the executable, but it also maximizes the number of files we have laying around that we have to keep track of. To make this task less painful, the concept of libraries was developed. We'll look at these in a later section.


This page was last modified .