Język programowania C jest niezwykle popularny i łatwo zrozumieć, dlaczego. Programowanie w C jest wydajne i daje programiście dużą kontrolę. Wiele innych języków programowania, takich jak C++, Java i Python, zostało opracowanych przy użyciu C.
Z każdym dniem wzrastają szanse, że jeśli jesteś programistą, nie będziesz używać C wyłącznie do swojej pracy. Istnieje jednak kilka nauki C, która jest bardzo korzystna, nawet jeśli nie używasz jej regularnie. Dlatego:
Będziesz mógł czytać i pisać kod do oprogramowania, które może być używane na wielu różnych platformach komputerowych, w tym wszystkim, od małych mikrokontrolerów po komputery stacjonarne, laptopy i mobilne systemy operacyjne.
Lepiej zrozumiesz, co robią za kulisami języki wysokiego poziomu, takie jak zarządzanie pamięcią i zbieranie śmieci. To zrozumienie może pomóc w pisaniu programów, które działają wydajniej.
Jeśli jesteś specjalistą ds. technologii informatycznych (IT), możesz również skorzystać z nauki języka C. Informatycy często piszą, utrzymują i uruchamiają skrypty w ramach swojej pracy. Skrypt jest lista instrukcji dla systemu operacyjnego komputera do naśladowania. Aby uruchomić określone skrypty, komputer tworzy środowisko kontrolowanego wykonywania zwane powłoką . Ponieważ większość systemów operacyjnych uruchamia powłoki oparte na języku C, powłoka C jest popularną adaptacją skryptową języka C używaną przez informatyków.
W tym artykule omówiono historię C, przyjrzymy się, dlaczego C jest tak ważne, pokazuje przykłady podstawowego kodu C i bada niektóre ważne cechy C, w tym typy danych, operacje, funkcje, wskaźniki i zarządzanie pamięcią. Chociaż ten artykuł nie jest instrukcją obsługi programowania w C, opisuje, co sprawia, że programowanie w C jest wyjątkowe w sposób, który wykracza poza te kilka pierwszych rozdziałów przeciętnego przewodnika po programowaniu w C.
Zacznijmy od przyjrzenia się, skąd wziął się język programowania C, jak się rozwinął i jaką rolę odgrywa w dzisiejszym tworzeniu oprogramowania.
- Co to jest C?
- Edycja i kompilacja kodu C
- Najprostszy program C C
- Wspólne koncepcje programowania w C
- Funkcje w C
- Prototypy funkcji
- Typy danych i operacje w C
- Nie zaczynaj od zera, używaj bibliotek
- Kilka wskazówek na temat wskaźników w C
- Poprawne używanie wskaźników w C
- Znaczenie zarządzania pamięcią w C
Co to jest C?
Najprostszym sposobem zdefiniowania języka C jest nazwanie go językiem programowania komputerowego, co oznacza, że można za jego pomocą pisać oprogramowanie, które może wykonywać komputer. Rezultatem może być duża aplikacja komputerowa, taka jak przeglądarka internetowa, lub niewielki zestaw instrukcji osadzonych w mikroprocesorze lub innym komponencie komputera.
Język C został opracowany na początku lat 70. w Bell Laboratories, głównie dzięki pracy Kena Thompsona i Dennisa Ritchiego. Programiści potrzebowali bardziej przyjaznego dla użytkownika zestawu instrukcji dla systemu operacyjnego UNIX, który w tamtych czasach wymagał programów napisanych w języku asemblerowym. Programy asemblerowe, które przemawiają bezpośrednio do sprzętu komputerowego, są długie i trudne do debugowania, a dodawanie nowych funkcji wymagało żmudnej, czasochłonnej pracy [źródło: King].
Pierwsza próba Thompsona dotycząca języka wysokiego poziomu została nazwana B, w hołdzie systemowemu językowi programowania BCPL, na którym została oparta. Kiedy Bell Labs nabył model systemu UNIX PDP-11 firmy Digital Equipment Corporation (DEC), Thompson przerobił B, aby lepiej odpowiadał wymaganiom nowszego, lepszego sprzętu systemowego. Tak narodził się następca B, C. W 1973 r. C było na tyle stabilne, że sam UNIX mógł zostać przepisany przy użyciu tego innowacyjnego nowego języka wyższego poziomu [źródło: King].
Zanim C mogło być efektywnie używane poza Bell Labs, inni programiści potrzebowali dokumentu wyjaśniającego, jak go używać. W 1978 roku książka Briana Kernighana i Dennisa Ritchie „The C Programming Language”, znana entuzjastom C jako K&R lub „Biała Księga”, stała się ostatecznym źródłem programowania w C. W chwili pisania tego tekstu drugie wydanie K&R, pierwotnie opublikowane w 1988 roku, jest nadal powszechnie dostępne. Oryginalna, przedstandardowa wersja C nosi nazwę K&R C na podstawie tej książki.
Aby upewnić się, że ludzie nie stworzyli z czasem własnych dialektów, programiści C pracowali przez lata 80., aby stworzyć standardy dla języka. Amerykańska norma C, norma American National Standards Institute (ANSI) X3.159-1989, stała się oficjalna w 1989 roku. Norma Międzynarodowej Organizacji Normalizacyjnej (ISO), ISO/IEC 9899:1990, pojawiła się w 1990 roku. Wersje C po tym, jak K&R odwołuje się do tych norm i ich późniejszych wersji (C89, C90 i C99). Możesz również zobaczyć C89 określany jako „ANSI C”, „ANSI/ISO C” lub „ISO C”.
C i jego użycie w systemie UNIX były tylko częścią boomu w rozwoju systemów operacyjnych w latach 80-tych. Mimo wszystkich ulepszeń w stosunku do swoich poprzedników, C nadal nie był łatwy w użyciu do tworzenia większych aplikacji. W miarę jak komputery stawały się coraz bardziej wydajne, wzrosło zapotrzebowanie na łatwiejsze programowanie. To zapotrzebowanie skłoniło programistów do zbudowania własnych kompilatorów, a tym samym własnych nowych języków programowania przy użyciu C. Te nowe języki mogą uprościć kodowanie złożonych zadań z dużą ilością ruchomych części. Na przykład języki takie jak C++ i Java, oba opracowane z C, to uproszczone programowanie obiektowe, podejście programistyczne, które optymalizuje zdolność programisty do ponownego wykorzystania kodu.
Teraz, gdy znasz już trochę tła, spójrzmy na mechanikę samego C.
Edycja i kompilacja kodu C
C jest językiem skompilowanym, co oznacza, że musisz użyć kompilatora, aby przekształcić kod w plik wykonywalny, zanim będziesz mógł go uruchomić. Kod jest zapisywany w jednym lub kilku plikach tekstowych, które można otwierać, czytać i edytować w dowolnym edytorze tekstu, takim jak Notatnik w systemie Windows, TextEdit na komputerze Mac i gedit w systemie Linux. Plik wykonywalny to coś, co komputer może uruchomić (wykonać). Kompilator sprawdza kod pod kątem błędów i jeśli wydaje się bezbłędny, tworzy plik wykonywalny.
Zanim przyjrzymy się, co wchodzi w kod C, upewnijmy się, że możemy znaleźć i użyć kompilatora C. Jeśli używasz systemu Mac OS X i większości dystrybucji Linuksa (takich jak Ubuntu), możesz dodać kompilator C do komputera, jeśli zainstalujesz oprogramowanie narzędzi programistycznych dla tego konkretnego systemu operacyjnego. Te darmowe kompilatory C są narzędziami wiersza poleceń, co oznacza, że zazwyczaj uruchamiasz je z wiersza poleceń w oknie terminala. Polecenie do uruchomienia jednego z tych kompilatorów C to „cc” lub „gcc” plus kilka opcji i argumentów wiersza poleceń, czyli innymi słowami wpisywanymi po poleceniu przed naciśnięciem klawisza Enter.
Jeśli używasz systemu Microsoft Windows lub wolisz używać graficznego interfejsu użytkownika zamiast wiersza poleceń, możesz zainstalować zintegrowane środowisko programistyczne (IDE) do programowania w języku C. IDE to pojedynczy interfejs, w którym możesz napisać swój kod, skompilować go, przetestować oraz szybko znaleźć i naprawić błędy. W przypadku systemu Windows można kupić oprogramowanie Microsoft Visual C ++, IDE do programowania zarówno w C, jak i C ++. Innym popularnym środowiskiem IDE jest Eclipse, bezpłatne środowisko IDE oparte na Javie, które działa w systemach Windows, Mac i Linux i ma dostępne rozszerzenia do kompilowania C i wielu innych języków programowania.
W przypadku C, podobnie jak w przypadku innych języków programowania komputerowego, bardzo ważna jest wersja używanego kompilatora. Zawsze chcesz używać wersji kompilatora C, która jest tak samo nowa lub nowsza niż wersja języka C, której używasz w swoim programie. Jeśli używasz IDE, pamiętaj o dostosowaniu ustawień, aby upewnić się, że IDE używa docelowej wersji C dla programu, nad którym pracujesz. Jeśli jesteś w wierszu poleceń, możesz dodać argument wiersza poleceń, aby zmienić wersję, jak w następującym poleceniu:
gcc –std c99 –o myprogram.exe myprogram.c
W powyższym poleceniu „gcc” to wywołanie do uruchomienia kompilatora, a wszystko inne to opcja lub argument wiersza poleceń. Dodano opcję "-std", a następnie "c99", aby poinformować kompilator, aby podczas kompilacji używał standardowej wersji C99 C. Dodano opcję „-o”, a następnie „myprogram.exe”, aby zażądać, aby plik wykonywalny, plik wyjściowy kompilatora, otrzymał nazwę myprogram.exe. Bez "-o" plik wykonywalny zostanie automatycznie nazwany a.out. Ostatni argument "myprogram.c" wskazuje plik tekstowy z kodem C do skompilowania. Krótko mówiąc, to polecenie mówi: „Hej, gcc, skompiluj myprogram.c przy użyciu standardu programowania C99 C i umieść wyniki w pliku o nazwie myprogram.exe”.Przeglądaj sieć, aby znaleźć pełną listę opcji, których możesz użyć w swoim konkretnym kompilatorze, niezależnie od tego, czy jest to gcc, czy coś innego.
Po zainstalowaniu kompilatora jesteś gotowy do programowania w C. Zacznijmy od przyjrzenia się podstawowej strukturze jednego z najprostszych programów w C, jakie możesz napisać.
Najprostszy program C C
Przyjrzyjmy się prostemu programowi w C i użyjmy go zarówno do zrozumienia podstaw C, jak i procesu kompilacji C. Jeśli masz własny komputer z zainstalowanym kompilatorem języka C, jak opisano wcześniej, możesz utworzyć plik tekstowy o nazwie sample.c i użyć go do śledzenia postępów w tym przykładzie. Zauważ, że jeśli zostawisz .c w nazwie pliku lub jeśli twój edytor doda .txt do nazwy, prawdopodobnie otrzymasz jakiś błąd podczas kompilacji.
Oto nasz przykładowy program:
/* Przykładowy program */
#zawierać
int main()
{
printf("To jest wynik mojego pierwszego programu!\n");
zwróć 0;
}
Po skompilowaniu i uruchomieniu ten program instruuje komputer, aby wypisał wiersz "To jest wyjście z mojego pierwszego programu!" a potem zatrzymaj się. Nie ma nic prostszego! Przyjrzyjmy się teraz, co robi każda linia:
Linia 1 — jest to jeden ze sposobów pisania komentarzy w C, pomiędzy /* a */ w jednej lub kilku liniach.
Wiersz 2 -- Komenda #include mówi kompilatorowi, aby przejrzał inne źródła istniejącego kodu C, szczególnie biblioteki, które są plikami zawierającymi typowe instrukcje wielokrotnego użytku. Odwołuje się do standardowej biblioteki C z funkcjami pobierania danych wejściowych od użytkownika i zapisywania wyników na ekranie. Bibliotekom przyjrzymy się bliżej później.
Linia 3 — ten wiersz jest pierwszym wierszem definicji funkcji. Każdy program w C ma co najmniej jedną funkcję lub blok kodu reprezentujący coś, co komputer powinien zrobić podczas działania programu. Funkcja wykonuje swoje zadanie, a następnie tworzy produkt uboczny, zwany wartością zwracaną, który może być używany przez inne funkcje. Jako minimum, program ma funkcję o nazwie main, taką jak ta pokazana tutaj, z wartością zwracaną o typie danych int, co oznacza liczbę całkowitą. Kiedy później przyjrzymy się bliżej funkcjom, zobaczysz, co oznaczają puste nawiasy.
Wiersze 4 i 7 -- Instrukcje w funkcji są ujęte w nawiasy klamrowe. Niektórzy programiści rozpoczynają i kończą blok w nawiasach klamrowych w oddzielnych wierszach, jak pokazano tutaj. Inni umieszczą otwarty nawias klamrowy ({) na końcu pierwszego wiersza definicji funkcji. Chociaż wiersze kodu w programie nie muszą być wpisywane w oddzielnych wierszach, programiści zazwyczaj umieszczają każdą instrukcję w oddzielnym wierszu, wciętym spacjami, aby kod był łatwiejszy do późniejszego czytania i edycji.
Wiersz 5 — jest to wywołanie funkcji o nazwie printf. Ta funkcja jest zakodowana w bibliotece stdio.h zawartej w wierszu 1, więc nie musisz jej pisać samodzielnie. To wywołanie printf mówi mu, co ma wydrukować na ekranie. Jednak \n na końcu w cudzysłowie nie jest drukowane; jest to sekwencja ucieczki, która instruuje printf, aby przesunął kursor do następnej linii na ekranie. Jak widać, każda linia w funkcji musi kończyć się średnikiem.
Wiersz 6 — każda funkcja zwracająca wartość musi zawierać instrukcję return, taką jak ta. W C funkcja main musi zawsze mieć zwracany typ liczby całkowitej, nawet jeśli nie jest używana w programie. Zauważ, że kiedy uruchamiasz program w C, zasadniczo uruchamiasz jego główną funkcję. Tak więc, kiedy testujesz program, możesz powiedzieć komputerowi, aby pokazał wartość zwracaną przez uruchomienie programu. Preferowana jest wartość zwracana 0, ponieważ programiści zazwyczaj szukają tej wartości podczas testowania, aby potwierdzić, że program został pomyślnie uruchomiony.
Kiedy będziesz gotowy do testowania swojego programu, zapisz plik, skompiluj i uruchom program. Jeśli używasz kompilatora gcc w wierszu poleceń, a program znajduje się w pliku o nazwie sample.c, możesz go skompilować za pomocą następującego polecenia:
gcc -o sample.exe sample.c
Jeśli w kodzie nie ma błędów, po uruchomieniu tego polecenia powinien znajdować się plik o nazwie sample.exe w tym samym katalogu co sample.c. Najczęstszym błędem jest błąd składni, co oznacza, że coś pomyliłeś, na przykład pominiesz średnik na końcu wiersza lub nie zamykasz cudzysłowów lub nawiasów. Jeśli musisz wprowadzić zmiany, otwórz plik w edytorze tekstu, napraw go, zapisz zmiany i spróbuj ponownie wykonać polecenie kompilacji.
Aby uruchomić program sample.exe, wprowadź następujące polecenie. Zwróć uwagę na ./, który zmusza komputer do przejrzenia bieżącego katalogu w celu znalezienia pliku wykonywalnego:
./przykład.exe
To są podstawy kodowania i kompilacji dla C, chociaż o kompilacji można dowiedzieć się o wiele więcej z innych zasobów programistycznych C. Teraz otwórzmy pudełko i zobaczmy, jakie elementy C ma do budowania programów.
Wspólne koncepcje programowania w C
Przyjrzyjmy się, jak wprowadzić w życie niektóre popularne koncepcje programowania w kodzie C. Poniżej znajduje się krótkie podsumowanie tych pojęć:
Funkcje — jak wspomniano wcześniej, funkcja jest blokiem kodu reprezentującym coś, co komputer powinien wykonać podczas działania programu. Niektóre języki nazywają te struktury metodami, chociaż programiści C zazwyczaj nie używają tego terminu. Twój program może zdefiniować kilka funkcji i wywoływać te funkcje z innych funkcji. Później przyjrzymy się bliżej strukturze funkcji w C.
Zmienne — Kiedy uruchamiasz program, czasami potrzebujesz elastyczności, aby uruchomić program bez wiedzy o wartościach z wyprzedzeniem. Podobnie jak inne języki programowania, C pozwala na używanie zmiennych, gdy potrzebujesz takiej elastyczności. Podobnie jak zmienne w algebrze, zmienna w programowaniu komputerowym jest symbolem zastępczym, który oznacza pewną wartość, której nie znasz lub której jeszcze nie znalazłeś.
Typy danych — Aby przechowywać dane w pamięci podczas działania programu i wiedzieć, jakie operacje można wykonać na tych danych, język programowania, taki jak C, definiuje pewne typy danych, które rozpozna. Każdy typ danych w C ma określony rozmiar, mierzony w bitach lub bajtach binarnych, oraz określony zestaw reguł dotyczących tego, co reprezentują jego bity. Wkrótce zobaczymy, jak ważny jest wybór odpowiedniego typu danych do zadania, gdy używasz C.
Operacje — w języku C można wykonywać operacje arytmetyczne (takie jak dodawanie) na liczbach i operacje na ciągach (takie jak konkatenacja) na ciągach znaków. C ma również wbudowane operacje zaprojektowane specjalnie z myślą o rzeczach, które możesz chcieć zrobić ze swoimi danymi. Kiedy sprawdzamy typy danych w C, przyjrzymy się też pokrótce operacjom.
Pętle — Jedną z najbardziej podstawowych rzeczy, które programista będzie chciał zrobić, jest kilkakrotne powtórzenie działania w oparciu o pewne warunki, które pojawiają się podczas działania programu. Blok kodu zaprojektowany do powtarzania w oparciu o określone warunki nazywa się pętlą, a język C zapewnia następujące typowe struktury pętli: while, do/while, for, continue/break i goto. C zawiera również typowe warunki warunkowe if/then/else oraz instrukcje switch/case.
Struktury danych — gdy twój program ma dużo danych do obsłużenia i musisz je posortować lub przeszukać, prawdopodobnie użyjesz pewnego rodzaju struktury danych. Struktura danych to ustrukturyzowany sposób przedstawiania kilku fragmentów danych tego samego typu danych. Najpopularniejszą strukturą danych jest tablica, która jest po prostu zaindeksowaną listą o określonym rozmiarze. C ma dostępne biblioteki do obsługi niektórych typowych struktur danych, chociaż zawsze możesz pisać funkcje i konfigurować własne struktury.
Operacje na preprocesorze — Czasami będziesz chciał dać kompilatorowi kilka instrukcji dotyczących tego, co należy zrobić z kodem przed skompilowaniem go do pliku wykonywalnego. Te operacje obejmują podstawianie stałych wartości i dołączanie kodu z bibliotek C (które widzieliście wcześniej w przykładowym kodzie).
C wymaga również od programistów obsługi pewnych koncepcji, które wiele języków programowania uprościło lub zautomatyzowało. Należą do nich wskaźniki, zarządzanie pamięcią i odśmiecanie. Dalsze strony zawierają ważne rzeczy, które należy wiedzieć o tych pojęciach podczas programowania w C.
Ten krótki przegląd pojęć może wydawać się przytłaczający, jeśli nie jesteś jeszcze programistą. Zanim przejdziesz do gęstego przewodnika po programowaniu w języku C, przyjrzyjmy się w sposób przyjazny dla użytkownika podstawowym pojęciom spośród wymienionych powyżej, zaczynając od funkcji.
Funkcje w C
Większość języków programowania komputerów umożliwia tworzenie pewnego rodzaju funkcji. Funkcje pozwalają podzielić długi program na nazwane sekcje, dzięki czemu można je ponownie wykorzystać w całym programie. Programiści dla niektórych języków, zwłaszcza używających technik programowania obiektowego, używają terminu metoda zamiast funkcji .
Funkcje przyjmują parametry i zwracają wynik. Blok kodu zawierający funkcję jest jej definicją. Poniżej przedstawiono podstawową strukturę definicji funkcji:
{
powrót
}
Program w C ma przynajmniej jedną funkcję o nazwie main. Kompilator będzie szukał funkcji main jako punktu początkowego programu, nawet jeśli funkcja main wywołuje inne funkcje w nim zawarte. Poniżej znajduje się główne, które widzieliśmy w prostym programie w C, który widzieliśmy wcześniej. Ma zwracany typ liczby całkowitej, nie przyjmuje parametrów i ma dwie instrukcje (instrukcje w ramach funkcji), z których jedną jest instrukcja powrotu:
int main()
{
printf("To jest wynik mojego pierwszego programu!\n");
zwróć 0;
}
Funkcje inne niż main mają definicję i jedno lub więcej wywołań funkcji. Wywołanie funkcji to instrukcja lub część instrukcji w ramach innej funkcji. Wywołanie funkcji nazywa funkcję, którą wywołuje, po której następuje nawias. Jeśli funkcja ma parametry, wywołanie funkcji musi zawierać odpowiednie wartości, aby pasowały do tych parametrów. Ta dodatkowa część wywołania funkcji nazywana jest przekazywaniem parametrów do funkcji.
Ale jakie są parametry? Parametrem funkcji jest fragment danych określonego typu, którego funkcja wymaga do wykonania swojej pracy. Funkcje w C mogą przyjmować nieograniczoną liczbę parametrów, czasami nazywanych argumentami. Każdy parametr dodany do definicji funkcji musi określać dwie rzeczy: typ danych i nazwę zmiennej w obrębie bloku funkcyjnego. Wiele parametrów oddziela się przecinkiem. W poniższej funkcji występują dwa parametry, obie liczby całkowite:
int doubleAndAdd(int a, int b)
{
powrót ((2*a)+(2*b));
}
Następnie przyjrzyjmy się funkcjom, oddalając widok, aby zobaczyć, jak mieszczą się w większym programie w języku C.
Prototypy funkcji
W C można dodać definicję funkcji w dowolnym miejscu programu (z wyjątkiem innej funkcji). Jedynym warunkiem jest wcześniejsze poinformowanie kompilatora, że funkcja istnieje gdzieś w dalszej części kodu. Zrobisz to z prototypem funkcji na początku programu. Prototyp to stwierdzenie, które wygląda podobnie do pierwszego wiersza definicji. W C nie musisz podawać nazw parametrów w prototypie, tylko typy danych. Oto jak wyglądałby prototyp funkcji dla funkcji doubleAndAdd:
int doubleAndAdd(int, int);
Wyobraź sobie prototypy funkcji jako listę pakowania dla Twojego programu. Kompilator rozpakuje i złoży twój program tak, jakbyś rozpakowywał i składał nową półkę z książkami. Lista rzeczy do spakowania pomaga upewnić się, że masz wszystkie potrzebne elementy w pudełku, zanim zaczniesz składać regał. Kompilator używa prototypów funkcji w ten sam sposób, zanim zacznie asemblować twój program.
Jeśli śledzisz razem z programem sample.c, który omówiliśmy wcześniej, otwórz i edytuj plik, aby dodać prototyp funkcji, definicję funkcji i wywołanie funkcji dla pokazanej tutaj funkcji doubleAndAdd. Następnie skompiluj i uruchom program jak poprzednio, aby zobaczyć, jak działa nowy kod. Możesz użyć następującego kodu jako przewodnika, aby go wypróbować:
#zawierać
int doubleAndAdd(int, int);
int main()
{
printf("To jest wynik mojego pierwszego programu!\n");
printf("Jeśli podwoisz to dodasz 2 i 3, wynikiem będzie: %d \n", doubleAndAdd(2,3));
zwróć 0;
}
int doubleAndAdd(int a, int b)
{
powrót ((2*a)+(2*b));
}
Do tej pory przyjrzeliśmy się kilku podstawowym elementom strukturalnym w programie C. Przyjrzyjmy się teraz typom danych, z którymi możesz pracować w programie w języku C i jakie operacje możesz wykonać na tych danych.
Deklaracje funkcji
W C prawdopodobnie usłyszysz termin deklaracja funkcji bardziej niż prototyp funkcji, szczególnie wśród starszych programistów C. W tym artykule używamy jednak terminu „prototyp funkcji”, ponieważ zawiera on ważne rozróżnienie. Pierwotnie deklaracja funkcji nie wymagała żadnych parametrów, więc typ zwracany, nazwa funkcji i para pustych nawiasów były wystarczające. Jednak prototyp funkcji dostarcza kompilatorowi ważnych dodatkowych informacji, włączając liczbę i typy danych parametrów, które będzie wywoływać. Prototypy stały się obecnie najlepszym podejściem wśród programistów w C i innych językach programowania.
Typy danych i operacje w C
Z punktu widzenia komputera dane to nic innego jak seria jedynek i zer reprezentujących stany włączenia i wyłączenia bitów elektronicznych na dysku twardym lub w procesorze lub pamięci komputera. To oprogramowanie, które uruchamiasz na komputerze, określa, jak zrozumieć te miliardy cyfr binarnych. C jest jednym z niewielu języków wysokiego poziomu, które oprócz interpretacji danych w oparciu o dany typ danych, mogą z łatwością manipulować danymi na poziomie bitowym.
Typ danych to mały zestaw reguł, które wskazują, jak nadać sens serii bitów. Typ danych ma określony rozmiar oraz własny sposób wykonywania operacji (takich jak dodawanie i mnożenie) na danych tego typu. W języku C rozmiar typu danych jest powiązany z używanym procesorem. Na przykład w C99 fragment danych typu danych całkowitych (int) ma długość 16 bitów w procesorze 16-bitowym, podczas gdy dla procesorów 32-bitowych i 64-bitowych ma długość 32 bitów.
Inną ważną rzeczą dla programistów C jest to, jak język obsługuje typy danych ze znakiem i bez znaku. Typ ze znakiem oznacza, że jeden z jego bitów jest zarezerwowany jako wskaźnik, czy jest to liczba dodatnia, czy ujemna. Tak więc, podczas gdy int bez znaku w systemie 16-bitowym może obsługiwać liczby od 0 do 65 535, osoba zalogowana w tym samym systemie może obsługiwać liczby od -32 768 do 32 767. Jeśli operacja powoduje, że zmienna int wychodzi poza jej zakres, programista musi poradzić sobie z przepełnieniem dodatkowym kodem.
Biorąc pod uwagę te ograniczenia i specyficzne dla systemu osobliwości typów danych i operacji C, programiści C muszą wybierać swoje typy danych w oparciu o potrzeby swoich programów. Niektóre z typów danych, które mogą wybrać, to prymitywne typy danych w C, czyli te wbudowane w język programowania C. Zajrzyj do swojego ulubionego przewodnika po programowaniu C, aby uzyskać pełną listę typów danych w C i ważne informacje o tym, jak konwertować dane z jednego typu na inny.
Programiści C mogą również tworzyć struktury danych, które łączą prymitywne typy danych i zestaw funkcji definiujących sposób organizacji danych i manipulowania nimi. Chociaż wykorzystanie struktur danych jest zaawansowanym tematem programowania i wykracza poza zakres tego artykułu, przyjrzymy się jednej z najczęstszych struktur: tablicom. Tablica to wirtualna lista zawierająca fragmenty danych o tym samym typie danych. Nie można zmienić rozmiaru tablicy, chociaż jej zawartość można skopiować do innych większych lub mniejszych tablic.
Chociaż programiści często używają tablic liczb, tablice znaków, zwane łańcuchami, mają najbardziej unikalne cechy. Ciąg znaków pozwala zapisać coś, co możesz powiedzieć (np. „cześć”) w serii znaków, które Twój program w C może odczytać od użytkownika lub wydrukować na ekranie. Manipulacja ciągami ma tak unikalny zestaw operacji, że ma własną dedykowaną bibliotekę C (string.h) z typowymi funkcjami ciągów.
Wbudowane operacje w C są typowymi operacjami, które można znaleźć w większości języków programowania. Łącząc kilka operacji w jedną instrukcję, należy znać pierwszeństwo operatorów lub kolejność, w jakiej program wykona każdą operację w wyrażeniu matematycznym. Na przykład (2+5)*3 równa się 21, podczas gdy 2+5*3 równa się 17, ponieważ C wykona mnożenie przed dodaniem, chyba że istnieją nawiasy wskazujące inaczej.
Jeśli uczysz się języka C, priorytetem będzie zapoznanie się ze wszystkimi podstawowymi typami danych i operacjami oraz pierwszeństwo operacji w tym samym wyrażeniu. Eksperymentuj także z różnymi operacjami na zmiennych i liczbach różnych typów danych.
W tym momencie zarysowałeś powierzchnię kilku ważnych podstaw C. Następnie spójrzmy, jak C umożliwia pisanie programów bez zaczynania za każdym razem od zera.
Nie zaczynaj od zera, używaj bibliotek
Biblioteki są bardzo ważne w C, ponieważ język C obsługuje tylko najbardziej podstawowe funkcje, których potrzebuje. Na przykład C nie zawiera funkcji wejścia-wyjścia (I/O) do odczytywania z klawiatury i zapisywania na ekranie. Wszystko, co wykracza poza podstawy, musi być napisane przez programistę. Jeśli fragment kodu jest przydatny dla wielu różnych programów, często jest umieszczany w bibliotece, aby można go było łatwo ponownie wykorzystać.
W naszej dotychczasowej dyskusji na temat C widzieliśmy już jedną bibliotekę, standardową bibliotekę we/wy (stdio). Linia #include na początku programu instruowała kompilator C, aby załadował bibliotekę z pliku nagłówkowego o nazwie stdio.h. Opiekunowie C zawierają standardowe biblioteki C dla I/O, funkcji matematycznych, manipulacji czasem i typowych operacji na niektórych strukturach danych, takich jak ciąg znaków. Przeszukaj Internet lub swój ulubiony przewodnik programowania C, aby uzyskać informacje o standardowej bibliotece C89 oraz aktualizacjach i dodatkach w C99.
Ty też możesz pisać biblioteki C. W ten sposób możesz podzielić swój program na moduły wielokrotnego użytku. To modułowe podejście nie tylko ułatwia umieszczanie tego samego kodu w wielu programach, ale także sprawia, że pliki programów są krótsze, co ułatwia czytanie, testowanie i debugowanie.
Aby użyć funkcji w pliku nagłówkowym, dodaj dla niego linię #include na początku programu. W przypadku standardowych bibliotek należy umieścić nazwę odpowiedniego pliku nagłówkowego biblioteki między znakami „większe niż” i „mniejsze niż” (). W przypadku bibliotek, które tworzysz samodzielnie, umieść nazwę pliku między podwójnymi cudzysłowami. W przeciwieństwie do instrukcji w innych częściach twojego programu w C, nie musisz umieszczać średnika na końcu każdego wiersza. Poniższe pokazy zawierają jedną z każdego typu biblioteki:
#zawierać
#include "mylib.h"
Kompleksowe źródło programowania w C powinno zawierać instrukcje potrzebne do napisania własnych bibliotek w C. Definicje funkcji, które napiszesz, nie różnią się niczym, niezależnie od tego, czy znajdują się w bibliotece, czy w głównym programie. Różnica polega na tym, że skompilujesz je osobno w czymś zwanym plikiem obiektowym (z nazwą kończącą się na .o), a utworzysz drugi plik, zwany plikiem nagłówkowym (z nazwą kończącą się na .h), który zawiera prototypy funkcji odpowiadające każdej funkcji w bibliotece. Jest to plik nagłówkowy, do którego będziesz się odwoływać w linii #include w każdym głównym programie, który używa twojej biblioteki, i dołączysz plik obiektowy jako argument w poleceniu kompilatora za każdym razem, gdy kompilujesz ten program.
Funkcje C, które do tej pory zbadaliśmy, są również typowe dla innych języków programowania. Następnie porozmawiamy o tym, jak C zarządza pamięcią komputera.
Kilka wskazówek na temat wskaźników w C
Kiedy twój program w C jest ładowany do pamięci (zazwyczaj pamięci o dostępie swobodnym lub RAM w twoim komputerze), każdy element programu jest powiązany z adresem w pamięci. Obejmuje to zmienne, których używasz do przechowywania określonych danych. Za każdym razem, gdy twój program wywołuje funkcję, ładuje tę funkcję i wszystkie powiązane z nią dane do pamięci na tyle długo, aby uruchomić tę funkcję i zwrócić wartość. Jeśli przekazujesz parametry do funkcji, C automatycznie tworzy kopię wartości do użycia w funkcji.
Czasami jednak, gdy uruchamiasz funkcję, chcesz dokonać trwałej zmiany danych w oryginalnej lokalizacji pamięci. Jeśli C utworzy kopię danych do użycia w funkcji, oryginalne dane pozostaną niezmienione. Jeśli chcesz zmienić te oryginalne dane, musisz przekazać wskaźnik do jego adresu pamięci (przekaż przez referencję) zamiast przekazywać jego wartość do funkcji (przekaż przez wartość).
Wskaźniki są używane wszędzie w C, więc jeśli chcesz w pełni używać języka C, musisz dobrze rozumieć wskaźniki. Wskaźnik jest zmienną jak inne zmienne, ale jego celem jest przechowywanie adresu pamięci niektórych innych danych. Wskaźnik ma również typ danych, więc wie, jak rozpoznać bity pod tym adresem pamięci.
Kiedy patrzysz na dwie zmienne obok siebie w kodzie C, nie zawsze możesz rozpoznać wskaźnik. To może być wyzwaniem nawet dla najbardziej doświadczonych programistów C. Kiedy po raz pierwszy tworzysz wskaźnik, jest to bardziej oczywiste, ponieważ bezpośrednio przed nazwą zmiennej musi znajdować się gwiazdka. Jest to znane jako operator pośredni w C. Poniższy przykładowy kod tworzy liczbę całkowitą i oraz wskaźnik do liczby całkowitej p:
wew;
int *p;
Obecnie nie ma żadnej wartości przypisanej do i lub p. Następnie przypiszmy wartość do i, a następnie przypiszmy p do wskazywania na adres i.
i = 3;
p = &i;
Tutaj możesz zobaczyć znak ampersand (&) używany jako operator adresu bezpośrednio przed i, co oznacza „adres i”. Nie musisz wiedzieć, jaki jest ten adres, aby dokonać przydziału. To dobrze, ponieważ prawdopodobnie za każdym razem, gdy uruchomisz program, będzie inaczej! Zamiast tego operator adresu określi adres powiązany z tą zmienną podczas działania programu. Bez operatora adresu przypisanie p=i przypisałoby p adres pamięci 3, dosłownie, a nie adres pamięci zmiennej i.
Następnie przyjrzyjmy się, jak możesz używać wskaźników w kodzie C i wyzwaniach, na które będziesz chciał się przygotować.
Poprawne używanie wskaźników w C
Gdy masz już wskaźnik, możesz użyć go zamiast zmiennej tego samego typu danych w operacjach i wywołaniach funkcji. W poniższym przykładzie wskaźnik do i jest używany zamiast i w ramach większej operacji. Gwiazdka użyta z p (*p) wskazuje, że operacja powinna użyć wartości, na którą wskazuje p w tym adresie pamięci, a nie samego adresu pamięci:
intb;
b = *p + 2;
Bez wskaźników jest prawie niemożliwe dzielenie zadań na funkcje spoza main w twoim programie C. Aby to zilustrować, załóżmy, że utworzyłeś zmienną w main o nazwie h, która przechowuje wzrost użytkownika z dokładnością do centymetra. Wywołujesz również napisaną przez siebie funkcję o nazwie setHeight, która prosi użytkownika o ustawienie tej wartości wysokości. Linie w głównej funkcji mogą wyglądać mniej więcej tak:
mig;
ustawWysokość(h); /* Istnieje potencjalny problem. */
To wywołanie funkcji spróbuje przekazać wartość h do setHeight. Jednak po zakończeniu działania funkcji wartość h pozostanie niezmieniona, ponieważ funkcja użyła tylko jej kopii, a następnie odrzuciła ją po zakończeniu działania.
Jeśli chcesz zmienić samo h, powinieneś najpierw upewnić się, że funkcja może pobierać wskaźnik do istniejącej wartości, a nie do nowej kopii wartości. W pierwszym wierszu setHeight użyłby zatem wskaźnika zamiast wartości jako parametru (zwróć uwagę na operator pośredni):
setHeight(int *height) { /* Instrukcje funkcyjne idą tutaj */ }
Następnie masz dwie możliwości wywołania setHeight. Pierwszym z nich jest użycie operatora adresu dla h jako przekazanego parametru (&h). Drugim jest utworzenie osobnego wskaźnika do h i przekazanie go zamiast tego. Poniżej przedstawiono obie opcje:
ustawWysokość(&h); /* Przekaż adres h do funkcji */
int *p;
p = &h;
ustawWysokość(p); /* Przekaż osobny wskaźnik na adres h do funkcji */
Druga opcja ujawnia typowe wyzwanie podczas używania wskaźników. Wyzwaniem jest posiadanie wielu wskaźników do tej samej wartości. Oznacza to, że każda zmiana tej jednej wartości wpływa na wszystkie jej wskaźniki jednocześnie. Może to być dobra lub zła rzecz, w zależności od tego, co chcesz osiągnąć w swoim programie. Ponownie, opanowanie posługiwania się wskaźnikami jest ważnym kluczem do opanowania programowania w C. Ćwicz ze wskaźnikami tak często, jak to możliwe, aby być gotowym na stawienie czoła tym wyzwaniom.
Funkcje C, które do tej pory zbadaliśmy, są również typowe dla innych języków programowania. Następnie przyjrzymy się wymaganiom C w zakresie ostrożnego zarządzania pamięcią.
Znaczenie zarządzania pamięcią w C
Jedną z rzeczy, które sprawiają, że C jest tak wszechstronnym językiem, jest to, że programista może skalować program w dół, aby działał z bardzo małą ilością pamięci. Kiedy po raz pierwszy napisano C, była to ważna funkcja, ponieważ komputery nie były tak potężne, jak dzisiaj. Przy obecnym zapotrzebowaniu na małą elektronikę, od telefonów komórkowych po małe urządzenia medyczne, ponownie pojawia się zainteresowanie utrzymaniem niewielkich wymagań dotyczących pamięci w przypadku niektórych programów. C jest językiem podstawowym dla większości programistów, którzy potrzebują dużej kontroli nad wykorzystaniem pamięci.
Aby lepiej zrozumieć znaczenie zarządzania pamięcią, zastanów się, jak program wykorzystuje pamięć. Kiedy po raz pierwszy uruchamiasz program, ładuje się on do pamięci komputera i zaczyna działać, wysyłając i odbierając instrukcje z procesora komputera. Gdy program musi uruchomić określoną funkcję, ładuje ją do jeszcze innej części pamięci na czas jej działania, a następnie porzuca tę pamięć, gdy funkcja jest zakończona. Dodatkowo każda nowa porcja danych używana w programie głównym zajmuje pamięć na czas trwania programu.
Jeśli chcesz mieć nad tym większą kontrolę, potrzebujesz dynamicznej alokacji pamięci. C obsługuje dynamiczną alokację pamięci, czyli możliwość rezerwowania pamięci w razie potrzeby i zwalniania jej, gdy tylko skończysz jej używać. Wiele języków programowania ma automatyczną alokację pamięci i wyrzucanie elementów bezużytecznych, które obsługują te zadania zarządzania pamięcią. Jednak C pozwala (a w niektórych przypadkach wymaga) wyraźnego określenia alokacji pamięci za pomocą następujących kluczowych funkcji ze standardowej biblioteki C:
- malloc - Skrót od alokacji pamięci, malloc służy do zarezerwowania bloku pamięci o danym rozmiarze do napisania określonego typu danych, które program musi przetworzyć. Kiedy używasz malloc, tworzysz wskaźnik do przydzielonej pamięci. Nie jest to konieczne dla pojedynczego elementu danych, takiego jak jedna liczba całkowita, która jest alokowana zaraz po pierwszym zadeklarowaniu (jak w int i). Jest jednak ważną częścią tworzenia i zarządzania strukturami danych, takimi jak tablice. Alternatywnymi opcjami alokacji pamięci w C są calloc , które również czyszczą pamięć, gdy jest ona zarezerwowana, oraz realloc , która zmienia rozmiar wcześniej zarezerwowanej pamięci.
- free -- Użyj free, aby zmusić program do zwolnienia pamięci wcześniej przypisanej do danego wskaźnika.
Najlepszą praktyką podczas korzystania z malloc i free jest to, że wszystko, co przydzielisz, powinno zostać zwolnione. Za każdym razem, gdy coś przydzielisz, nawet w funkcji tymczasowej, pozostaje to w pamięci, dopóki system operacyjny nie oczyści miejsca. Aby mieć pewność, że pamięć jest wolna i gotowa do natychmiastowego użycia, należy ją zwolnić przed zakończeniem bieżącej funkcji. To zarządzanie pamięcią oznacza, że możesz ograniczyć zużycie pamięci programu do minimum i uniknąć wycieków pamięci. Wyciek pamięci to usterka programu, w której nadal używa coraz więcej pamięci, aż nie ma jej już do przydzielenia, co powoduje zatrzymanie lub awarię programu. Z drugiej strony, nie martw się tak bardzo o uwolnienie pamięci, że zwolnisz, a tym samym stracisz coś, czego potrzebujesz później w tej samej funkcji.
W tym artykule poznałeś podstawową strukturę i podstawowe koncepcje języka programowania C. Przyjrzeliśmy się jego historii, cechom wspólnym z innymi językami programowania oraz ważnym cechom, które czynią go wyjątkową i wszechstronną opcją dla oprogramowania do kodowania. Przejdź na następną stronę, aby uzyskać więcej informacji, w tym kilka przewodników po programowaniu, które poprowadzą Cię dalej w Twojej podróży do C.
Dużo więcej informacji
Powiązane artykuły
- Podstawy programowania w C
- Jak działa Java
- Jak działa logika logiczna
- Jak działa PERL
- Jak działa BIOS
- Jak działa skrypty CGI
- Jak działają bity i bajty
Więcej świetnych linków
- Samouczek programowania w języku C
- Programowanie w C w Wikibooks
- Kolekcja kompilatorów GNU (GCC)
- Oprogramowanie do zintegrowanego środowiska programistycznego Eclipse
Źródła
- Kernighan, Brian W. i Ritchie, Dennis M. „Język programowania C, wydanie drugie”. Sala Prezydencka. 1988.
- King, KN "Programowanie C: nowoczesne podejście, wydanie drugie." WW Norton & Company, Inc. 2008.