Home / Alt manpages / clang-18(1)

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

Compile a Small C Program Safely with Clang 18

Before you trust clang-18 on a real project, run it through one small C program: syntax check, object file, linked executable, and a successful run. 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 none of it installs packages, alters system files or touches a running service.

1. Confirm the compiler you will use

Check the executable and version before you interpret a diagnostic. The unversioned clang command may point at 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 through 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 fine 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 stops the shell expanding anything in the source. The program uses a standard library declaration and returns zero from main, a useful baseline before testing a larger build.

Don't overwrite an existing project file with this example. If you're 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 the warnings worth having on 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

-std=c17 selects C17, and -Wall, -Wextra and -pedantic ask for extra diagnostics. -fsyntax-only stops before code generation, so a successful run creates neither hello.o nor an executable.

A warning is not a failed syntax check. Read every warning before proceeding, especially in code headed for production. You can make warnings fatal with -Werror, but do that deliberately: a compiler upgrade can expose new warnings and stop a build that used to complete cleanly.

Checkpoint

Confirm 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. -o 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 runnable on its own. It holds machine code and relocation information that a linker still has to combine with the C runtime and any 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 hold 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. Pass several source files without a stage-selection option and it runs 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 still earns its keep when you need to identify whether a failure happened at compilation or at linking. A missing function definition, for instance, can pass the syntax check and object compilation and only fail at the link stage.

6. Inspect a command before it runs

When a build command surprises you, use -### to print the tools and arguments Clang would invoke, without actually running 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 target and installation. This is a diagnostic view, not a replacement for compilation, and it's particularly good 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 throwing random flags at it. Re-run the syntax-only command before rebuilding every object.

Keep the old executable while you test a replacement. Compile to a temporary name, check it, then move it into place only once you've deliberately chosen that change:

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

Warning

That final mv replaces the old hello name. Don't use this pattern on 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.

Once testing is complete, the scratch directory can be removed as a single, explicitly identified directory. Don't apply recursive deletion to a project or system path by accident:

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

Done means

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