C to kompilowany język programowania ogólnego przeznaczenia, stworzony przez Dennisa Ritchiego w Bell Labs w 1972 roku do napisania systemu operacyjnego Unix. Przetrwał niemal wszystko ze swojej epoki, a powód jest prosty: C leży jedną cienką warstwę nad maszyną. Daje bezpośredni dostęp do pamięci i rzeczywistych możliwości procesora, a mimo to czyta się go jak coś, co napisał człowiek.
Pół wieku później wciąż jest językiem, w którym buduje się inne języki.
Kompletny program w C
Oto kształt całego języka w sześciu liniach. Kliknij Uruchom.
#include <stdio.h> dołącza standardowe deklaracje wejścia i wyjścia, dzięki czemu printf jest znana. main to miejsce, od którego zaczyna się każdy program w C. printf wypisuje tekst, \n to znak nowej linii, a return 0 informuje system operacyjny, że program zakończył się pomyślnie. To kompletny program, który da się skompilować: nie ma klasy, w którą trzeba go opakować, ani frameworka pod spodem.
Co wyróżnia C
Większość nowoczesnych języków ukrywa przed tobą maszynę. C celowo tego nie robi.
- Kompiluje się do kodu natywnego. Program w C staje się instrukcjami maszynowymi dla konkretnego procesora i działa bezpośrednio, bez interpretera i bez maszyny wirtualnej pośrodku.
- Pamięcią zarządzasz sam. Nie ma garbage collectora. Prosisz o pamięć i ją oddajesz. To więcej pracy, ale też powód, dla którego programy w C zużywają tak mało pamięci i mają tak przewidywalne czasy działania.
- Jest mały. C ma około 32 słów kluczowych. Cały język da się zmieścić w głowie, czego nie można powiedzieć o C++, Javie czy Pythonie.
- Prawie nie ma środowiska uruchomieniowego. Program w C może działać na mikrokontrolerze z kilkoma kilobajtami RAM i zupełnie bez systemu operacyjnego.
Ceną jest szczerość zamiast bezpieczeństwa. C pozwoli ci czytać za końcem tablicy albo używać już zwolnionej pamięci i cię nie zatrzyma. Te błędy mają swoje nazwy, na przykład segmentation fault, wycieki pamięci czy niezdefiniowane zachowanie, a nauka C w dużej mierze polega na nauce ich unikania.
Gdzie naprawdę działa C
C to nie historyczna ciekawostka. Działa na urządzeniu, na którym to czytasz, i to w wielu miejscach naraz.
- Systemy operacyjne. Jądro Linuksa to około 30 milionów linii kodu w C. Duże części Windowsa i jądra Apple także są napisane w C.
- Systemy wbudowane. Mikrokontrolery w samochodach, termostatach, urządzeniach medycznych, routerach i pralkach programuje się w przytłaczającej większości w C. Gdy cały układ ma 32 KB pamięci, C często jest jedyną realną opcją.
- Bazy danych. SQLite, PostgreSQL, MySQL i Redis to programy w C.
- Środowiska uruchomieniowe języków. CPython, referencyjny interpreter Pythona, jest napisany w C. Podobnie MRI Ruby'ego, silnik Zend PHP i rdzeń Lua. Gdy Python jest "za wolny", rozwiązaniem zwykle jest rozszerzenie w C.
- Narzędzia i biblioteki. Git, curl, FFmpeg, OpenSSL, zlib i znowu SQLite: warstwa infrastruktury informatyki to C.
Dlatego też liczy się binarny interfejs aplikacji (ABI) języka C: niemal każdy język potrafi wywołać funkcję w C. Python, Rust, Go, Java i JavaScript mają sposoby, by sięgnąć do bibliotek w C. C to lingua franca między językami.
Dlaczego warto uczyć się C
Są dwie szczere odpowiedzi i dotyczą różnych osób.
Jeśli chcesz pracować nad oprogramowaniem systemowym, czyli jądrami, sterownikami, firmware'em czy bazami danych, C jest językiem roboczym tej dziedziny, a większość ofert pracy w tych obszarach wymienia go wprost (często obok C++ lub Rusta). Trudno wnieść coś do Linuksa, PostgreSQL czy kodu mikrokontrolera, jeśli nie czytasz swobodnie kodu w C.
Jeśli chcesz lepiej znać język, którego już używasz, C pokazuje, co jest pod spodem. Po C wiesz, dlaczego indeksy tablic zaczynają się od zera, czym naprawdę jest string, dlaczego przekazywanie dużego obiektu przez wartość jest kosztowne, czym są stos i sterta i na co tak naprawdę wskazuje wskaźnik NULL. Te pojęcia występują w każdym języku; w C są widoczne, a nie ukryte.
Ten program to trzy linie C i jednocześnie pełna demonstracja tego, czym jest zmienna: nazwanym pudełkiem pod pewnym adresem. W większości języków nie da się nawet zadać tego pytania.
C i C++
C++ powstał w 1979 roku jako "C z klasami" i wyrósł na jeden z największych istniejących języków: klasy, szablony, wyjątki, przeciążanie operatorów, STL, inteligentne wskaźniki i biblioteka standardowa, przy której cała specyfikacja C wydaje się drobna.
W praktyce wygląda to tak:
- C++ zachował składnię C, więc
if,for,while, operatory, wskaźniki i tablice wyglądają w obu językach tak samo. - Większość dobrze napisanego kodu C kompiluje się jako C++, z kilkoma wyjątkami (na przykład C++ wymaga rzutowania wyniku
malloc). - Kod C++ prawie nigdy nie jest poprawnym kodem C.
Jeśli planujesz nauczyć się obu, rozpoczęcie od C to realna przewaga: C++ bezpośrednio opiera się na modelu pamięci C, a części C++, które najbardziej mylą ludzi, czyli wskaźniki, referencje, semantyka kopiowania i destruktory, to odpowiedzi na problemy, które C pokazuje wprost.
C nie jest jednak w żadnym oficjalnym sensie podzbiorem C++. To osobne standardy rozwijane niezależnie, a nowoczesne C ma cechy, których C++ nie ma (inicjalizatory z desygnatorami, które pojawiły się w C99, wskaźniki restrict i tablice o zmiennej długości).
Które C: wersje standardu
C jest ustandaryzowany, a wersja ma znaczenie przy czytaniu starszego kodu.
| Standard | Rok | Warto znać ze względu na |
|---|---|---|
| C89 / C90 | 1989 | Pradawna baza. Zmienne deklarowane tylko na początku bloku. |
| C99 | 1999 | Komentarze //, deklarowanie zmiennych w dowolnym miejscu, bool przez stdbool.h, long long |
| C11 | 2011 | Wątki, operacje atomowe, bezpieczniejsze funkcje biblioteczne |
| C17 | 2018 | Wydanie C11 z poprawkami błędów: dziś typowe ustawienie domyślne |
| C23 | 2024 | true/false/bool jako słowa kluczowe, nullptr, literały binarne |
Ta dokumentacja dotyczy C17 i zaznacza cechy C99+ tam, gdzie mają znaczenie. Kompilatory domyślnie używają czegoś nowoczesnego, ale możesz to określić wprost:
gcc -std=c17 -Wall -Wextra hello.c -o hello
Czego C nie ma
Zrozumienie C to także wiedza, czego w nim brakuje, bo niemal każde pytanie "jak zrobić..." ma tu inną odpowiedź niż w nowoczesnym języku.
- Brak stringów. String w C to tablica
charzakończona bajtem zerowym. Połączenie dwóch stringów oznacza wywołanie funkcji i pilnowanie, czy miejsce docelowe jest wystarczająco duże. - Brak kolekcji. Nie ma list, map ani zbiorów. Masz tablice o stałym rozmiarze i wskaźniki; tablicę mieszającą piszesz sam albo dołączasz z biblioteki.
- Brak wyjątków. Funkcje zgłaszają niepowodzenie, zwracając specjalną wartość, na przykład
-1,NULLalbo kod statusu, a wywołujący ma to sprawdzić. - Brak typów generycznych i przeciążania. Jedna nazwa funkcji to jedna funkcja. Dlatego biblioteka standardowa ma
abs,labsifabsdla trzech różnych typów. - Brak przestrzeni nazw i modułów. Wszystkie funkcje, które nie są
static, dzielą jedną globalną przestrzeń nazw, dlatego biblioteki C dodają prefiks do wszystkiego (sqlite3_open,png_read_info). - Brak garbage collectora. Każdy
mallocpotrzebuje odpowiadającego mufree, a zapomnienie o nim to wyciek pamięci.
Żadne z tych braków nie są przeoczeniami. Część to mechanizmy czasu wykonania, których C nie chce dźwigać (garbage collector, odwijanie stosu przy wyjątkach, rosnące stringi), i w dużej mierze dlatego mieści się w układzie z 2 KB RAM; inne, jak przestrzenie nazw i typy generyczne, to cechy języków, które pojawiły się później niż projekt C i zostały pominięte, żeby język pozostał mały.
Bufor ma 32 bajty, bo to twoja decyzja, że tyle wystarczy. C tego nie sprawdzi i ta decyzja to cały charakter języka w jednej linii.
Czy C jest trudny?
C jest mały, ale bezlitosny. Składni do zapamiętania nie ma wiele: cały język przeczytasz w tydzień. Czasu wymaga dyscyplina: śledzenie, kto jest właścicielem którego fragmentu pamięci, pamiętanie, że string to tylko bajty zakończone zerem, sprawdzanie, czy wskaźnik nie jest NULL, zanim za nim podążysz.
Większość trudności C nie leży w jego pojęciach, tylko w braku zabezpieczeń: kompilator nie uratuje cię przed zapisem poza zakresem ani przed wiszącym wskaźnikiem, więc uczysz się precyzji. (Same wskaźniki za pierwszym razem wymagają prawdziwego wysiłku, to normalne, a rozdział o wskaźnikach omawia je powoli.) Ta precyzja to umiejętność, która przenosi się na każdy język, którego użyjesz później.
Najczęściej zadawane pytania
Czym jest język programowania C?
C to kompilowany język programowania ogólnego przeznaczenia, stworzony przez Dennisa Ritchiego w Bell Labs w 1972 roku. Daje bezpośrednią kontrolę nad pamięcią i blisko odpowiada temu, co naprawdę robi procesor, dlatego systemy operacyjne, sterowniki urządzeń i bazy danych wciąż pisze się właśnie w nim.
Do czego dziś używa się języka C?
Do jąder systemów operacyjnych (Linux, części Windowsa i macOS), oprogramowania wbudowanego w samochodach, sprzęcie AGD i mikrokontrolerach, baz danych takich jak SQLite i PostgreSQL, środowisk uruchomieniowych języków, takich jak CPython, i programów takich jak Redis, a także bibliotek o krytycznej wydajności, z których korzystają inne języki.
Czy w 2026 roku wciąż warto uczyć się C?
Tak, z dwóch powodów. To wciąż język programowania systemowego i wbudowanego, a to duża i dobrze płatna dziedzina. Poza tym C uczy, jak naprawdę działają pamięć, wskaźniki i stos, a ta wiedza sprawia, że lepiej radzisz sobie w każdym innym języku, bo większość z nich zaimplementowano w C.
Czym różni się C od C++?
C++ zaczynał jako "C z klasami" i zachował większość składni C, dodając programowanie obiektowe, szablony, bibliotekę standardową i wiele więcej. C jest dużo mniejszy: bez klas, bez wyjątków, bez typów generycznych. Większość poprawnego kodu C kompiluje się jako C++, ale nie odwrotnie.