Home / Alt manpages / clang-18(1)

  • clang-18(1)
  • User command
  • linux

Compile a Small C Program Safely with Clang 18

You will finish with a small C program that has passed a syntax check, been compiled to an object file, linked into an executable and run successfully. The examples use the installed Clang 18 driver, which reports Ubuntu Clang 18.1.3 on this machine. Allow about fifteen minutes, plus time to investigate any warnings in your own source tree.

You need a shell, a writable working directory and the clang-18 package. These are ordinary user commands. Nothing here needs sudo, and the examples do not install packages, alter system files or change a running service.

1. Confirm the compiler you will use

Check the executable and version before interpreting a diagnostic. The unversioned clang command may be a distribution-managed alternative, so use clang-18 when you need this guide's version-specific result:

$ command -v clang-18
/usr/bin/clang-18
$ clang-18 --version
Ubuntu clang version 18.1.3 (1ubuntu1)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

The exact package revision and target lines can differ on another installation. The command name, driver options and compilation stages described here are the interface documented by the local clang-18(1) manpage.

Checkpoint

If command -v prints nothing, stop here. Install or repair the compiler using your normal package-management process rather than guessing a path.

2. Create a source file in a separate directory

Make a small workspace and write one C source file. Keeping generated files beside the source is convenient for a test; for a real project, use its existing build directory.

$ mkdir -p "$HOME/clang18-check"
$ cd "$HOME/clang18-check"
$ cat > hello.c <<'EOF'
#include <stdio.h>

int main(void)
{
    puts("clang-18-ok");
    return 0;
}
EOF

The quoted here-document keeps the shell from expanding anything in the source. The program uses a standard library declaration and returns zero from main, which is a useful baseline before testing a larger build.

Do not replace an existing project file with this example. If you are working in a repository, create a new scratch directory or choose a new filename first.

3. Run a syntax-only check

Ask Clang to preprocess, parse and semantically analyse the source without producing an object file. Add warnings that are useful for a small C program:

$ clang-18 -std=c17 -Wall -Wextra -pedantic -fsyntax-only hello.c
$ printf 'syntax check status: %s\n' "$?"
syntax check status: 0

The -std=c17 option selects C17, while -Wall, -Wextra and -pedantic request additional diagnostics. -fsyntax-only stops before code generation, so a successful run does not create hello.o or an executable.

A warning is not the same as a failed syntax check. Read every warning before proceeding, especially in code that will be deployed. You can make warnings fatal with -Werror, but do that deliberately: compiler upgrades can expose new warnings and then stop a build that previously completed.

Checkpoint

Confirm that the directory still contains only your source file:

$ ls -l hello.c
-rw-r--r-- 1 you you ... hello.c

4. Compile the source into an object file

Use -c when you want compilation and assembly but not linking. The -o option gives the output a predictable name:

$ clang-18 -std=c17 -Wall -Wextra -pedantic -c hello.c -o hello.o
$ file hello.o
hello.o: ELF 64-bit LSB relocatable, x86-64, ...

An object file is not normally runnable. It contains machine code and relocation information that a linker still needs to combine with the C runtime and other objects. This two-stage form is the useful boundary for multi-file programs:

$ clang-18 -c first.c -o first.o
$ clang-18 -c second.c -o second.o
$ clang-18 first.o second.o -o application

Each input file must exist and contain the language you expect. Clang usually infers C from the .c suffix; use the documented -x option when a file has a non-standard suffix and you need to state its language explicitly.

With the object file ready, invoke the driver without -c. It runs the linker and writes the named executable:

$ clang-18 hello.o -o hello
$ ./hello
clang-18-ok
$ printf 'program status: %s\n' "$?"
program status: 0

The driver is more than the compiler front end: it coordinates preprocessing, compilation, assembly and linking. When you pass several source files without a stage-selection option, it can perform the full pipeline in one command:

$ clang-18 -std=c17 -Wall -Wextra -pedantic hello.c -o hello
$ ./hello
clang-18-ok

The explicit object-file step remains useful when you need to identify whether a failure happened during compilation or linking. A missing function definition, for example, can pass the syntax check and object compilation but fail at the link stage.

6. Inspect a command before it runs

When a build command is surprising, use -### to print the tools and arguments Clang would invoke without executing them:

$ clang-18 -### -c hello.c -o hello.o
Ubuntu clang version 18.1.3 (1ubuntu1)
 ... "-emit-obj" ... "-o" "hello.o" ...

The exact internal arguments depend on the target and installation. This is a diagnostic view, not a replacement for compilation. It is particularly useful for spotting an unexpected input file, output path or target option before a larger build runs.

7. Diagnose failures without destroying useful files

Read the first diagnostic and the source location it names. A missing header, typo or incompatible declaration is usually fixed in the source or include path, not by adding random flags. Re-run the syntax-only command before rebuilding all objects.

Keep the old executable while testing a replacement. Compile to a temporary name, check it, then move it into place only when you have chosen that change deliberately:

$ clang-18 -std=c17 -Wall -Wextra -pedantic hello.c -o hello.new
$ ./hello.new
clang-18-ok
$ mv hello.new hello

Warning

The final mv replaces the old hello name. Do not use this pattern for a live service binary without following that service's deployment and rollback procedure. If the test fails, leave the old executable alone and investigate; if an unused temporary remains, remove only that known file after checking its path.

When the test is complete, the scratch directory can be removed as a single, explicitly identified directory, but do not apply recursive deletion to a project or system path by accident:

$ cd ..
$ rm -r -- clang18-check

Done means

  • clang-18 --version identified the intended compiler.
  • The source passed -fsyntax-only with the selected C standard and warnings.
  • -c produced an object file, and the driver linked it into an executable.
  • The executable printed the expected line and returned status zero.
  • Any replacement was tested under a new name before an existing file was replaced.