Projekty¶
Przy tworzeniu rozbudowanych aplikacji pojawia się potrzeba podziału projektu na osobne pliki, tak aby zachować porządek i czytelność. Standardowy podział polega na zgrupowaniu funkcjonalności i wydzieleniu ich w postaci osobnych plików. W ten sposób tworzymy niejako własne biblioteki, czy też zależności projektu. W wyniku takiego podziału otrzymujemy dwa pliki dla każdej zależności:
- plik nagłówkowy z rozszerzeniem .h,
- plik z implementacją z rozszerzeniem .c.
W pliku nagłówkowym zawieramy zależności, struktury, a także deklaracje funkcji. W pliku z implementacją natomiast definiujemy funkcje z pliku nagłówkowego, czyli tworzymy ich implementację. Taki podział pozwala między innymi na łatwą podmianę implementacji bez modyfikacji interfejsu biblioteki.
Przyjrzyjmy się dokładniej opisanemu konceptowi na poniższym przykładzie.
Przykład¶
Jako przykład rozważymy implementację prostej biblioteki pozwalającej na wykonywanie podstawowych operacji na dwuwymiarowych punktach. Doprecyzowując zaimplementujemy jedynie strukturę do reprezentacji punktu o współrzędnych rzeczywistych, a także funkcję obliczającą odległość między dwoma punktami.
point.h - plik nagłówkowy¶
#ifndef POINT_H
#define POINT_H
#include <math.h>
typedef struct Point {
float x;
float y;
} Point;
float distance(const Point *a, const Point *b);
#endif
Zacznijmy od wyjaśnienia makra #ifndef POINT_H
, które znajduje się na początku pliku nagłówkowego.
Jego znaczenie jest bardzo istotne, gdy pracujemy z wieloplikowymi projektami.
Dzięki zastosowaniu takiego makra unikniemy problemów związanych z wielokrotnym dołączaniem zależności.
Poniżej definiujemy zależności.
W tym przypadku skorzystamy z biblioteki math.h
, ponieważ przyda nam się funkcja obliczająca wartość pierwiastka kwadratowego.
Następnie tworzymy naszą strukturę, a także deklarujemy funkcję obliczającą odległość pomiędzy dwoma punktami na płaszczyźnie.
W celu oszczędzania pamięci nasza funkcja przyjmuje wskaźniki na punkty, zamiast ich kopii.
point.c - plik z implementacją¶
#include "point.h"
float distance(const Point *a, const Point *b) {
return sqrt((a->x - b->x) * (a->x - b->x) + (a->y - b->y) * (a->y - b->y));
}
Na początku, za pomocą instrukcji include
dołączamy plik nagłówkowy point.h
.
main.c¶
#include <stdio.h>
#include "point.h"
int main(void) {
Point a = {0.0, 0.0};
Point b = {0.0, 5.0};
float dist = distance(&a, &b);
printf("Distance: %.2f\n", dist);
return 0;
}
Kompilacja¶
Makefile¶
Przydatne makra¶
$@
- lewa strona znaku dwukropka, tzn. nazwa polecenia$^
- prawa strona znaku dwukropka, tzn. zależności polecenia$<
- pierwszy element z listy zależności