Develop with GCC on Ubuntu¶
This tutorial shows how to build and debug C programs on Ubuntu using GCC and GDB. For instructions on installing GCC and related tooling (including IDEs, debuggers, and the build system), see the dedicated guide on How to set up a development environment for GCC on Ubuntu. This article assumes that the tooling suggested in that article has been installed.
The GNU Compiler Collection (GCC) provides support for several programming languages. Working with the GCC toolchain in Ubuntu is straightforward, so this tutorial is limited to showing a basic ‘Hello, world!’ program in the C language and an introductory debugging session.
Writing a sample C program¶
Create a project directory:
mkdir -p ~/c-projects/hello-world cd ~/c-projects/hello-world
Write a ‘Hello, world!’ program that includes setting a variable. Create a
hello.cfile with the following content:hello.c¶#include <stdio.h> int main() { int number = 42; printf("Hello, world!\nThe answer to everything is %d.\n", number); return 0; }
Build the source code using the
gcccompiler, specifyinghelloas the file name of the resulting executable:gcc hello.c -o hello
This command incorporates the intermediate steps of preprocessing, compiling, and linking to generate an executable in one go. Use the
-voption to display detailed information on howgccinvokes the individual tools that handle the intermediate steps.Note
gcccan compile source code in many programming languages, including C, C++, and Assembly. The compiler guesses the language based on the file extension (for example,.c,.cpp, or.s). You can explicitly specify the language using the-xoption as well.Run the program executable:
dev@ubuntu:~/projects/hello-world$./helloHello, world! The answer to everything is 42.
Fixing simple bugs¶
The gcc compiler provides helpful output when encountering a problematic part of the source code.
Modify the ‘Hello, world!’ program source to omit an expected argument. Note the missing
numberargument on line 5:hello.c¶1#include <stdio.h> 2 3int main() { 4 int number = 42; 5 printf("Hello, world!\nThe answer to everything is %d.\n"); 6 return 0; 7}
Build the source:
$ gcc hello.c -o hello hello.c: In function ‘main’: hello.c:5:57: warning: format ‘%d’ expects a matching ‘int’ argument [-Wformat=] 5 | printf("Hello, world!\nThe answer to everything is %d.\n"); | ~^ | | | int
gccdisplays a warning but still compiles the source code.Run the program to see how it behaves without the integer argument specified:
dev@ubuntu:~/projects/hello-world$./helloHello, world! The answer to everything is -1350680728.
Using the Make build system¶
Compiling manually by invoking gcc directly can help understand the build process. It is also adequate for simple projects like ‘Hello, world!’. A build system simplifies and automates the process.
See below for a simple example of using the GNU Make build system to build the ‘Hello, world!’ program from Writing a sample C program.
Create a basic Makefile (a file named
Makefilein the project directory) with the following contents:## Variables: # Compiler CC = gcc # Target executable TARGET = hello # Source files SRCS = hello.c # Object files OBJS = $(SRCS:.c=.o) ## Targets: # Default target all: $(TARGET) # Linking $(TARGET): $(OBJS) $(CC) -o $@ $^ # Compiling %.o: %.c $(CC) -c $< -o $@ # Cleaning clean: rm -f $(OBJS) $(TARGET) .PHONY: all clean
The
Makefiledivides the build process into separate compilation and linking steps. It also uses “automatic variables” that allow for writing the compilation rules without specifying absolute file names. For an overview, see Automatic Variables in the GNU Make manual.Important
Makefile rules must start with the tab character. See Rule Syntax.
Compile the program using
make:dev@ubuntu:~/projects/hello-world$makegcc -c hello.c -o hello.o gcc -o hello hello.o
Checking the resulting files would show:
dev@ubuntu:~/projects/hello-world$ls -1hello hello.c hello.o Makefile
Run the program executable:
dev@ubuntu:~/projects/hello-world$./helloHello, world! The answer to everything is 42.
Debugging with GDB¶
GDB is the standard debugger accompanying GNU GCC. While you can use the debugging capabilities of your preferred IDE, GDB offers adequate debugging support for the command line.
See the example below for a quick introduction to GDB use with the ‘Hello, world!’ program from Writing a sample C program.
Recompile the program with the
-goption, which produces debugging information specifically for GDB:gcc -g hello.c -o hello
Run the debugger:
1$ gdb hello 2 3#[snip] 4 5Reading symbols from hello... 6(gdb) start 7Temporary breakpoint 1 at 0x1155: file hello.c, line 4. 8Starting program: /home/rkratky/c-projects/hello-world/hello 9 10[Thread debugging using libthread_db enabled] 11Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". 12 13Temporary breakpoint 1, main () at hello.c:4 144 int number = 42; 15(gdb) print number 16$1 = 32767 17(gdb) next 185 printf("Hello, world!\nThe answer to everything is %d.\n", number); 19(gdb) print number 20$2 = 42 21(gdb)
In the above example:
Line 6: the
helloprogram is started.Line 15:
numberis queried before being assigned.Line 17: program execution is stepped forward.
Line 19:
numberis queried again after being assigned.
Press Ctrl+X+A to switch to the text user interface (TUI) of GDB for a more interactive experience:
┌─hello.c───────────────────────────────────────────────────────────┐
│ 1 #include <stdio.h> │
│ 2 │
│ 3 int main() { │
│ 4 int number = 42; │
│ > 5 printf("Hello, world!\nThe answer to everything is %d│
│ 6 return 0; │
│ 7 } │
│ 8 │
│ │
│ │
│ │
└───────────────────────────────────────────────────────────────────┘
Thread 0x7ffff7f897 (src) In: main L5 PC: 0x55555555515c
(gdb) print number
$1 = 32767
(gdb) next
(gdb) print number
$2 = 42
(gdb)Type
helpat any point to see available commands.Press Ctrl+D to quit GDB.