Przejdź do treści

Kompilacja i budowanie programów

Etapy kompilacji

gcc uruchamia kolejno cztery programy. Każdy etap można wykonać osobno:

Etap Polecenie Wynik
preprocessing gcc -E prog.c -o prog.i kod C z wklejonymi #include i rozwiniętymi #define
compilation gcc -S prog.c prog.s: kod w asemblerze
assembly gcc -c prog.c prog.o: plik obiektowy
linking gcc prog.o -o prog program
  • Kompilator przetwarza jeden plik .c naraz.
  • Plik obiektowy zawiera kod i listę symboli: nm prog.o pokazuje zdefiniowane (T) i brakujące (U).
  • Linker łączy pliki obiektowe i biblioteki, uzupełniając brakujące symbole.

Program z wielu plików:

gcc -c main.c -o main.o
gcc -c util.c -o util.o
gcc main.o util.o -o prog

Rodzaje błędów

main.c:6:10: fatal error: util.h: No such file or directory    preprocessor
main.c:12:5: error: implicit declaration of function 'foo'     compiler
undefined reference to `foo'                                    linker
multiple definition of `foo'                                    linker
  • Błąd z numerem linii: kompilacja pojedynczego pliku.
  • undefined reference: brak definicji, np. brak pliku .o lub biblioteki w poleceniu, albo nazwa w nagłówku różni się od nazwy w bibliotece.
  • multiple definition: ta sama funkcja zdefiniowana w dwóch plikach.

Nagłówki i biblioteki

Nagłówek (.h) zawiera deklaracje, biblioteka zawiera kod.

Opcja Znaczenie
-Iinclude katalog z nagłówkami
-Llib katalog z bibliotekami
-lfoo dołącz libfoo.so lub libfoo.a
-lm biblioteka matematyczna

Biblioteki podajemy po plikach obiektowych: linker czyta argumenty od lewej.

gcc main.o util.o -Llib -lfoo -lm -o prog      # dobrze
gcc -lm -lfoo main.o util.o -o prog            # undefined reference

nm -D libfoo.so wypisuje funkcje udostępniane przez bibliotekę.

Biblioteki statyczne i dzielone

Statyczna Dzielona
plik libfoo.a libfoo.so (Windows .dll, macOS .dylib)
tworzenie ar rcs libfoo.a a.o b.o gcc -shared -fPIC foo.c -o libfoo.so
kod kopiowany do programu wczytywany przy uruchomieniu przez loader

Loader szuka bibliotek w katalogach systemowych:

$ ./prog
./prog: error while loading shared libraries: libfoo.so: cannot open shared object file
$ ldd ./prog
        libfoo.so => not found
  • LD_LIBRARY_PATH=lib ./prog: dodatkowy katalog na jedno uruchomienie.
  • gcc ... -Wl,-rpath,'$ORIGIN/lib': ścieżka zapisana w programie; $ORIGIN to katalog programu. Sprawdzenie: readelf -d prog. W macOS: @loader_path.

Przydatne opcje

Opcja Działanie
-Wall -Wextra ostrzeżenia
-Werror ostrzeżenia jako błędy
-g informacje dla debuggera
-O0, -O2 bez optymalizacji / z optymalizacjami
-std=c17 wersja języka
-fsanitize=address,undefined wykrywanie błędów pamięci i niezdefiniowanych zachowań w czasie działania

Sanitizer zgłasza błąd, gdy program wykona błędną linię; z -g podaje jej numer:

palette.c:20:19: runtime error: index 256 out of bounds for type 'rgb [256]'

make

Makefile składa się z reguł: cel, zależności, polecenie (wcięte tabulatorem).

prog: main.o util.o
    gcc main.o util.o -o prog

main.o: main.c util.h
    gcc -c main.c -o main.o

util.o: util.c util.h
    gcc -c util.c -o util.o

Cel jest budowany, gdy nie istnieje lub któraś zależność jest od niego nowsza. make buduje pierwszy cel w pliku, make util.o wskazany.

Ze zmiennymi i regułą wzorcową:

CC = gcc
CFLAGS = -Wall -Wextra -g -Iinclude
LDFLAGS = -Llib
LDLIBS = -lm
OBJS = src/main.o src/util.o

prog: $(OBJS)
    $(CC) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@

src/%.o: src/%.c
    $(CC) $(CFLAGS) -c $< -o $@

src/main.o: include/util.h
src/util.o: include/util.h

clean:
    rm -f prog $(OBJS)

.PHONY: clean
  • $@ cel, $< pierwsza zależność, $^ wszystkie zależności.
  • .PHONY: cele, które nie są plikami.
  • make -n wypisuje polecenia bez wykonania, make -B buduje wszystko od nowa.
  • -MMD -MP w CFLAGS i -include $(OBJS:.o=.d) na końcu: zależności od nagłówków generuje kompilator.

Większe projekty używają generatorów, np. CMake, które tworzą Makefile z opisu projektu.