GCC/G++ is used to compile and link C/C++ programs, long story short, a compiler. Its history is not described here, so those who are interested can check it out for themselves.

Compilation process of C/C++

Before introducing GCC/G++, let’s briefly explain the process of compiling and linking C/C++ to generate executable files.

The process of compiling and linking C/C++ to generate executable files

  1. Source file preprocessing: c preprocessing (cpp.exe) to process macros in source files, etc.

    1
    
    cpp hello.c > hello.i
    
  2. Compile: gcc/g++ compiles the preprocessed code into an assembly program, the -S option indicates the generation of an assembly program file (.s)

    1
    
    gcc -S hello.i
    
  3. Generate the target file (.o) from the assembler using as.exe

    1
    
    as -o hello.o hello.s
    
  4. Finally, use the connector (ld.exe) to link the target file (.o) to generate the executable file (.out/.exe)

    1
    
    ld -o hello.out hello.o ...libraries...
    

Use of GCC/G++

Let’s take GCC as an example and introduce the basic usage. Create a new hello.c file and write the following contents.

1
2
3
4
5
6
#include <stdio.h>

int main() {
    printf("Hello, World");
    return 0;
}
  1. Compile and link the c file

    1
    2
    3
    
    $ gcc hello.c
    $ ls
    a.out  hello.c
    

    The default is to generate a.out (the executable is a.out on linux, a.exe on windows). You can use the -o option to specify the name of the generated file.

    1
    2
    3
    
    $ gcc -o hello hello.c
    $ ls
    hello hello.c
    
  2. Separate compilation and linking processes

    A file has changed in the actual project and we want to compile it separately and then link its compiled target file (.o) with other target files or library files. The source file can be compiled to the target file (.o) using the -c option.

    1
    2
    3
    
    $ gcc -c hello.c
    $ ls
    hello.c hello.o
    

    Linking a target file into an executable file executes the same commands as compiling and linking. The linking process is executed when the source file is a target file, and the compiling and linking process is executed when the source file is a C file.

    1
    2
    3
    
    $ gcc -o hello hello.o
    $ ls
    hello hello.c hello.o
    
  3. Multi-file compilation link

    The gcc command supports multiple source file input if you want to compile source1.c and source2.c together.

    1
    2
    3
    
    $ gcc -o a.out source1.c source2.c
    $ ls
    a.out source1.c source2.c
    

    More often than not, you may want to compile them separately.

    1
    2
    3
    
    $ gcc -c source1.c
    $ gcc -c source2.c
    $ gcc -o a.out source1.out source2.out
    
  4. Print all warning messages at compile time

    The -Wall option prints all warning messages, so it is usually turned on when using gcc.

    1
    
    $ gcc -Wall -o hello hello.c
    
  5. Debugging

    With the -g option, you can make the file contain debugging information to be made available to gdb for debugging.

    1
    
    $ gcc -Wall -g -o hello hello.c
    
  6. Print the complete message at compile time

    -v prints the complete compilation process information.

    1
    
    $ gcc -v -o hello hello.c
    

Static and Shared Libraries

Usually we compile some common methods, variables, classes, into a separate library for the business side to call. A library is essentially a file containing one or more object files (object files) for use in the linking phase.

There are two types of libraries: static library and shared library, which is also called dynamic library. What are the differences in their behavior?

  • Static libraries are a collection of target files (.o) with the suffix .a on linux and mac, and .lib on windows. It is linked with other target files (.o) to form an executable (.out) during the linking phase of the program compilation process. So the static library only works in the compilation phase, it is not needed at runtime.
  • Shared libraries are also collections of target files, with the suffix .so (shared object) in linux, .dylib in mac, and .dll in windows, and play a role in both the linking phase and the running phase of program compilation. During the linking phase, the linker verifies that the symbols (variables, methods, etc.) needed for program execution have been linked into the program or exist in one of the shared libraries. But the target file in the shared library is not linked to the final generated executable. In the Run phase, when the program starts, there is a program in the system - dynamic loader that checks which dynamic libraries are linked into the program and loads these dynamic libraries into memory to be executed with the program. So the shared libraries must be present when the program is running.

Static libraries are equivalent to the code contained in them being copied into the executable at compile time, while the code of shared libraries is loaded into memory at runtime, so if you compile with static libraries, the final generated executable will be larger than the one obtained by compiling with shared libraries.

Static library creation and linking

Use the ar tool to create static libraries. ar has the following functions.

  • Create static libraries.
  • Change the target files in the static library (as said before a static library is a collection of target files).
  • View the names of the target files contained in the static library.

You can use man ar to see the usage of the ar command in linux.

  1. Create a static library

    A static library can be created with the following command.

    1
    
    $ ar -rc libutil.a util_file.o util_net.o util_math.o
    

    The above command packages util_file.o, util_net.o and util_math.o into the static library libutil.a, and if the libutil.a file already exists, then it will add the util_file.o, util_net.o and util_math.o files to the static library. files to the static library. The c option creates the library file if it does not exist, and the r option replaces the target file if the target file to be packaged already exists in the library.

    You can also use gcc’s -static to generate static libraries.

    1
    
    $ gcc *.o -static -o libname.a
    

    Static file names generally start with the lib prefix and end with the .a suffix, because the compiler looks for library files based on this feature when linking (explained below).

  2. Using static libraries

After we create the static library, we want to use it in our program. The library file can be linked into an executable file along with the target file by using the following command.

1
gcc {{target file1 target file2 ...}} -L<library file directory> -l<library file> -o <executable file name>

For example, the following example links the target file main.o with the library libutil.a into the executable prog.

1
gcc main.o -L. -lutil -o prog

It is important to note that.

  • The -L option is used to indicate the library file directory, and . indicates the current directory, so the library to be linked is in the current directory.
  • The -l option is used to indicate the name of the library file to be linked. Note that we have removed the prefix lib and the suffix .a from the library file name, so the library file name starts with lib and ends with .a.
  • Note that the library file should be placed after the target file, as detailed below

In the execution of gcc main.o -L. -lutil -o prog, assuming that main.o uses a function symbol from libutil.a, such as the add method, the linker executes as follows.

  1. The linker scans the target file and the static library from left to right
  2. when scanning main.o, it finds an unresolved symbol add, remember this unresolved symbol
  3. Scan libutil.a and find the previous unresolved symbol, so extract the relevant code
  4. Finally, there are no unresolved symbols, the compilation and linking is completed, and the executable prog is generated

If the static library is placed in front of the target file, for example by executing gcc -L. -lutil main.o -o prog, then the linker will execute as follows.

  1. the linker scans the static library and the target file in order from left to right
  2. scan libutil.a without extracting any code from the library because there are no unresolved symbols in front of it
  3. When scanning main.o, the unresolved symbol exp is found
  4. At the end of the scan, there are unresolved symbols, so the compilation and linking reports an error

Shared library creation and linking

The process of creating a shared library (dynamic library) is not much different from that of a static library, in that a set of target files are compiled and inserted into a library file. However, when linking a dynamic library, instead of “copying” all the required binary code into the executable, only some relocation and symbolic information is “copied”, with which the real linking process will be completed when the program runs.

  1. Creating shared libraries The -shared argument to gcc creates shared libraries

    1
    2
    3
    4
    
    # Generate target files
    $ gcc -fPIC -c *.c
    # Compile the target file into a shared library
    $ gcc -shared -o *.so *.o
    
  2. Links to shared libraries

    The link shared library is the same as the link dynamic library command. Since gcc uses dynamic linking by default, if there are dynamic and static libraries with the same name, gcc will link the dynamic library, and you can use -static to specify the static library to be linked.

    1
    
    $ gcc main.o -L. -lutil -o prog
    

    The above command will link the libutil.so file.

    Normally, when a program is run, the system dynamic loader will look for shared libraries in some directory specified by the system (e.g. /lib, /usr/x11/lib, etc.). When we create a shared library, we can use the LD_LIBRARY_PATH environment variable to tell the dynamic loader to look in the specified directory.

    1
    2
    
    LD_LIBRARY_PATH=/full/path/to/library/directory:${LD_LIBRARY_PATH}
    export LD_LIBRARY_PATH
    

    You can use the ldd command to view the shared libraries on which the executable depends.

    1
    
    $ ldd prog