Proces kompilacji programu C składa się z kilku etapów: preprocesor (preprocessing), kompilacja (compilation), asembler (assembling), linkowanie (linking). Historycznie taka struktura pozwalała na rozdzielenie obowiązków między narzędziami i ułatwia zaspokajanie i dostosowywanie procesu budowy.
Problem: Jeśli nie rozumiesz, jak działa każdy z etapów, możesz napotkać błędy takie jak "undefined reference", duplikację kodu, nieczytelne błędy wynikające z niewłaściwego użycia makr i problemy przy skalowaniu kodu na kilka plików.
Rozwiązanie: Musisz zrozumieć, że każdy etap pełni konkretną funkcję: preprocesor przetwarza dyrektywy #define, #include i inne, kompilator tłumaczy źródłowy kod C na asembler, asembler na kod maszynowy, a linker łączy wszystkie pliki obiektowe i biblioteki w ostateczny plik wykonywalny.
Przykład kodu:
Fragment kodu źródłowego:
#include <stdio.h> #define PI 3.14 int main() { printf("%f ", PI); return 0; }
Przykład wywołania gcc z wyraźnym określeniem etapów:
gcc -E program.c # Preprocesor gcc -S program.c # Kompilacja (do asemblera) gcc -c program.c # Asembler (do pliku obiektowego) gcc program.o -o prog # Linkowanie (linking)
Kluczowe cechy:
Co robi dyrektywa #include "file.h" na etapie kompilacji?
#Include wstawia zawartość pliku bezpośrednio w miejsce wywołania na etapie preprocesora, przed rozpoczęciem kompilacji. Jeśli dwa razy podłączysz ten sam plik, może to prowadzić do wielokrotnego zdefiniowania obiektów lub błędów podczas linkowania.
Czy zawsze definicja funkcji w jednym pliku jest wystarczająca do jej użycia w innym?
Nie, konieczne jest zadeklarowanie (prototyp) w pliku nagłówkowym lub przynajmniej zadeklarowanie extern. W przeciwnym razie mogą wystąpić błędy takie jak "implicit declaration of function" lub "undefined reference" podczas linkowania.
Czy można używać zmiennych zadeklarowanych jako static z innego pliku?
Nie. static ogranicza widoczność zmiennej lub funkcji do bieżącego pliku. To oznacza, że takie symbole nie są widoczne dla linkera w innych plikach obiektowych.
Nowicjusz implementuje funkcję o tej samej nazwie w dwóch plikach źródłowych. Na etapie linkowania pojawia się dziwny błąd "multiple definition of function".
Zalety:
Wady:
Tworzenie plików .h tylko do deklaracji, plików .c do definicji. Użycie #ifdef do ochrony plików nagłówkowych. Wszystkie pliki są łączone przez linkera w ostateczny program.
Zalety:
Wady: