The ".a", ".o", and ".out" files in C
Today, I will show you what is ".a" and ".o" files in C/C++, which are unavoidable to develop products in C/C++. Let's dive in.
How is a program executed?
As you know, computers cannot directly execute programs written in C/C++, Java, Swift, JavaScript, Python and any other programming language. What they can execute is "binary code" which consists only of 0s and 1s. Therefore, you have to convert your programs into binary code, which is called "compile" in C/C++ and in "compiled languages" (script languages like Python and JavaScript have a different process).
What ".out" file are
In C/C++, the compilation process is like this:
cc your_program.c
And then, you can get a file as the output of the compilation. The file name would be "a.out" or something similar.
An ".out" file is binary, so machines are able to execute it. You can run the binary like this:
./a.out
Note
- ".out" is a conventional file extension on Linux and Mac. On Windows the standard file extension for executable is ".exe".
What ".o" files are
On the other hand, though ".o" files are generated by compile commands like cc
or gcc
and they contain some binary code, they are not directly executable.
Before learning what ".o" files are, let's see an example:
// example0.c
#include <stdio.h>
int main() {
printf("Hello, World!");
}
You can execute the program with ".out" file after compilation. But what about the following program?
// example1.c
#include "your_header.h"
int main() {
// this function is defined in another file
// and its prototype is declared in the header your_header.h
your_function();
}
Note
- You can include headers with either
<>
or""
. However, while<>
first searches the system headers like "stdio.h" and "stdlib.h"""
first searches local headers.
The file is dependent on another file, and you cannot execute the program only by compiling "example1.c". C/C++ programs usually consist of many source files and they reference each other, which is ".o" files come into play.
An ".o" file is called an "object file" and is generated by the compiler during the build process. It indeed contains binary code, but it is not a final executable or a library.
When you compile a C/C++ source file to get ".o" file, the compiler defines placeholders for any function or variable used in the file but defined in other source files. After all source files are compiled into ".o", a "linker" will take all the ".o" files and replace all the placeholders with the actual memory address for the functions and the variables. This process is called "linking". The linker will then output the final executable. In general, these files have no extension on Linux or Mac, or have the ".exe" extension on Windows.
In short, an ".o" file is an intermediate file that is generated temporarily before all the source files are compiled.
Here is an example:
SRCS = main.c helper.c
OBJS = $(SRCS:.c=.o)
EXEC = program
$(EXEC): $(OBJS)
gcc $(OBJS) -o $(EXEC)
%.o: %.c
gcc $(SRCS) -o $(OBJS)
When you run make program
or simply make
, make will:
Looks at the dependencies of the "program" target (in this case, "main.c" and "helper.c").
Compile to create ".o" files for the ".c" source files if they ".o" files do not exist or are older than their corresponding ".c" files. Make uses the pattern rule
%.o: %.c
to compile each ".c" file into a ".o" file.Execute the task for the
program
target:gcc $(OBJS) -o $(EXEC)
. This links the ".o" files into the final executable named "program".
Note
%.o: %.c
is a kind of target but you can not runmake %.o
as a normal target; the rule%.o: %.c
just defines how to get object files from ".c" files.For more information about Makefile, see the link.
More about ".o" files (you can skip it)
A typical ".o" files contains several sections, each serving a different purpose.
Here are some of common sections:
text: This section contains the actual machine code - the binary instructions that the processor will execute. It is the main binary part of the ".o" file.
data: This section initialized global and static variables. The values of these variables are stored directly in the ".o" file not as a memory address.
bss: This section contains initialized global and static variables: The ".o" file does not contain the value of the variables (since they are uninitialized), but it does contain information about their size and location.
rodata: This section contains read-only data, such as string literals and constants.
debug: This section contains debugging information, which includes things like the original source code line numbers, variable name, and types. This information is used by debuggers to make debugging easier.
symtab and strtab: These sections contain the symbol table and string table, respectively. The symbol table contains information about the functions and global variables defined in the ".c" file, as well as placeholders for functions and variables defined elsewhere. The string table contains the names of these symbols.
What ".a" files are
A ".a" file is a static library file in C/C++. It is a collection of object files (".o" files) that are linked into your program during the linking phase of compilation.
Note
A static library is, as mentioned earlier, a collection of object files that are linked directly into your executable at compile time.
A dynamic library, on the other hand, is a file that contains compiled code that is loaded into your program at runtime. It ends with ".so" on Linux and Mac, ".dll" on Windows.
Here are key points of ".a" files:
"a" stands for "archive".
".a" is a conventional static library format for Linux or Mac. On Windows, that is ".lib".
".a" is generated by the compiler and the linker and written in binary, so it is a part of your executable. In this sense, it is similar to ".o".
However, ".a" is a package of ".o" files and is commonly referenced in your executable.
Due to these features of ".a" files, they are useful when you have code that you want to use in multiple programs.
You can create a ".a" file from object files like this:
lib.a: utils.c helper.c
gcc -c utils.c helper.c
ar -rcs lib.a utils.o helper.o
Note
Compiling with the "-c" option will skip the link process. With the option, you can compile each source into an object file file separately and then link all the object files together into a final executable when you are ready.
Using "ar" with the "-r" option will replace an existing ".a" file if it has the same name. In this case, if there is already "lib.a", a new "lib.a" will be created.
Using "ar" with the "-c" option will create a ".a" file if it does not exist yet.
Using "ar" with the "-s" option will write an index to the archive, which makes linking faster.