Kurs języka C++
Język C/C++
jest językiem systemów operacyjnych i aplikacji użytkowych.
Został zaprojektowany przez Dennisa Ritchiego około roku 1972 i od tamtej pory jest rozwijany.
Praktycznie wszystkie nowoczesne systemy operacyjne pisane są w C++ (jak Linux, czy monopolista) ponieważ jest szybki i zajmuje mało miejsca w pamięci. Jest to niewątpliwie trudniejszy język od Pascala, ponieważ Pascal jest językiem opisowym, a C/C++ to język w którym roi się od symboli. Z tego powodu oraz z powodu swojej budowy język C++ nie jest dobrym rozwiązaniem dla początkujących programistów.
Podstawowymi pojęciami podczas programowania w języku C++ są:
kompilator - narzędzie (program) tłumaczące napisany przez nas kod źródłowy programu (zrozumiały dla nas) na język maszynowy (czyli zrozumiały dla komputera)
środowisko - czyli kompilator i wspierające do aplikacje (programy)
plik nagłówkowy - plik zawierający definicję używanych słów kluczowych oraz funkcji (tzw.:biblioteka statyczna)
słowo kluczowe - zastrzeżone słowo, które jest rozumiane przez kompilator
funkcja - podprogram - część kodu do wielokrotnego wykorzystania (nie dotyczy funkcji main)
struktura programu
#include <nazwa_pliku_nagłówkowego1.h> /* dołączone pliki nagłówkowe*/ #include "nazwa_pliku_nagłówkowego2.h"; /* blok dyrektyw */ int nazwa_funkcji(parametry) /* blok funkcji */ { kod funkcji } main() /* blok programu głównego - funkcji głównej */ { instrukcje programu } |
---|
Polecenie #include powiadamia kompilator, by dołączył plik nagłówkowy, którego nazwę podajemy w nawiasie - jeżeli jest to biblioteka programu, oraz w cudzysłowach - jeśli jest to nasza własna biblioteka(możemy podać do niej ścieżkę dostępu). Instrukcje programu objęte są nawiasami {}. Linijkę kodu zwykle kończymy średnikiem - istnieją od tej reguły wyjątki.
Wcięcia i komentarze
Pisząc program pamiętać należy o stosowaniu wcięć, oraz o stosowaniu komentarzy. Właściwie to każdy blok kodu powinien być opisany - do czego służy, a szczególnie zawiłe linie kodu warto by były opisane linia po linii.
Kompilator ignoruje znaki pozostawione w jednej linii po postawieniu // komentarz, a jeśli chcemy wykonać dłuższy komentarz, to stosujemy ramy /* komentarz */ które mogą zawierać więcej niż jedną linię.
Nazwy programów (kodu programu)
Wszystkie pliki zawierające teksty programów w języku C++ powinny ˙mieć charakterystyczne rozszerzenie nazwa.cpp (CPP to skrót od C Plus Plus) lub nazwa.c (dla języka C). Po tym rozszerzeniu rozpoznaje te programy kompilator.
Preprocesor
Preprocesor jest programem uruchamianym przed kompilatorem. Odpowiedzialny jest on za przetwarzanie dyrektyw, czyli słów poprzedzonych znakiem#, które odpowiedzialne są min. za obsługę plików nagłówkowych (postaci nazwa_pliku_nagłówkowego.h), które dołączane są do kodu źródłowego - w których znajdują się definicje klas oraz funkcji.
dyrektywy
#include <nazwa_pliku.h> lub #include "nazwa_pliku.h"
Dyrektywa dołączająca podany plik nagłówkowy do kodu programu. Jeżeli nazwa pliku jest ujęta w nawiasy trójkątne, to preprocesor wie, że danego pliku należy szukać w standardowym katalogu kompilatora. Jeżeli natomiast plik zawarty jest w cudzysłów, to preprocesor szuka, go w katalogu, gdzie tworzony jest projekt.
#define nazwa_zmiennej wartość_zmiennej
Dyrektywa służąca do definiowania zmiennych globalnych. Zmienna nie musi mieć przypisanej wartości.
#ifdef nazwa_zmiennej
Dyrektywa warunkowa, sprawdzająca czy jest zdefiniowana zmienna o określonej nazwie. Jeżeli tak to wykonuje kod aż do wystąpienia dyrektywy #endif. Służy ona do sprawdzania dla jakiego systemu jest program kompilowany, oraz dla zabezpieczenia przed podwójnym zadeklarowaniem klas, zmiennych, funkcji
#ifndef
Dyrektywa ta działa podobnie do poprzedniej, z tym wyjątkiem, że ta wykonuje część kodu, w momencie nie spełnienia warunku.
#endif
Dyrektywa ta kończy działanie obu powyższych dyrektyw warunkowych.
#else
Dyrektywa występująca w parze z dyrektywami warunkowymi. Wykonuje ona jakiś kod w momencie niespełnienia jakiegoś warunku.
Operatory
Operatory według pierwszeństwa |
---|
Operator |
:: :: |
-> . [] () () |
sizeof ++ -- ~ ! + - * & () new delete |
->* .* |
* / % |
+ - |
<< >> |
< <= > >= |
== != |
& |
^ |
| |
&& |
|| |
?: |
= *= /= % += -= <<= >>= &= |= ^= |
, |
Operatory arytmetyczne
Dodawanie +
Operator sumuje dwie zmienne. Przykład składni: 2+2
Odejmowanie -
Operator odejmuje jedną zmienną od drugiej. Przykład składni: 5-2
Mnożenie *
Operator mnoży przez siebie dwie liczby. Przykład składni: 3*5
Dzielenie /
Operator dzieli przez siebie dwie liczby. Jeśli dzielnik jest zerem, program przerwie działanie z komunikatem błędu dzielenia przez 0. Przykład składni 8/4.
Dzielenie modulo %
Dzielenie modulo zwraca resztę z dzielenia, czyli podczas dzielenia liczby całkowitej przez liczbę całkowitą zwraca liczbę całkowitą, będącą resztą z dzielenia. Przykład działania: 6%2=0; 7%2=1.
Operatory bitowe
Operatory bitowe działają na poszczególnych bitach zmiennych
Przesuwanie bitów w lewo <<
Na zmiennych typu unsigned char, jeżeli zmienna ma wartość przedstawioną w postaci binarnej, przykładowo: 00100100, to wynikiem takiej operacji zmienna << 2 będzie wynik: 10010000. Polecenie przesuwa bity w lewo o dwie pozycje, argument traci bity które w wyniku przesunięcia znajdą się poza nim, po prawej stronie na miejsce brakujących bitów wstawia zera. Składnia: zmienna << liczba bitów
Przesuwanie bitów w prawo >>
j.w. wykonywane w prawo.
Negacja bitowa ~
Negacja bitowa zamienia w zmiennej wszystkie zera na jedynki i jedynki na zera. Przykład: ~10011010=01100101, składnia:~zmienna
Koniunkcja bitowa &
Koniunkcja bitowa jest to mnożenie pojedynczych bitów. Koniunkcja daje wynik jeden gdy oba bity mają wartość jeden. Działa on na argumentach całkowitych, składnia: zmienna1 & zmienna2
Różnica symetryczna ^
Wynikiem różnicy symetrycznej jest na danej pozycji jedynka gdy bity są różnej wartości. Działa na argumentach całkowitych, składnia: zmienna1 ^ zmienna2
Alternatywa bitowa |
Wynikiem alternatywy bitowej jest na danej pozycji jedynka gdy przynajmniej jeden bit jest jedynką. Działa na argumentach całkowitych, składnia: zmienna1 | zmienna2
Operatory porównania, operatory logiczne
Operatory dające w wyniku wartość logiczną TRUE, FALSE czyli PRAWDA, FAŁSZ, gdzie warunek prawdziwy daje wartość 1 natomiast fałszywy 0.
Negacja logiczna !
Negacja logiczna zamienia prawdę w fałsz, a fałsz w prawdę. Np. jeżeli wyrażenie: a==b jest prawdziwe i do niego zastosujemy wyrażenie !(a==b) to w wyniku otrzymamy fałsz. Składnia: !argument
Iloczyn logiczny &&
Wynikiem iloczynu logicznego jest prawda tylko wtedy gdy oba argumenty też są prawdziwe. Składnia: argument1 && argument2
Suma (alternatywa) logiczna ||
Wynikiem sumy logicznej jest prawda wtedy jeżeli przynajmniej jeden argument jest prawdziwy. Składnia: argument1 || argument2
Równość ==
Wynikiem jest prawda w tedy gdy wartości obu argumentów są takie same. Składnia: argument1 == argument2
Nierówność !=
Wynikiem jest prawda w tedy gdy wartości obu argumentów są różne. Składnia: argument1 != argument2
Operatory relacji
Wynikiem jest prawda w tedy gdy jest spełniony warunek że coś jest większe, mniejsze, lub równe od siebie. Zależy on od operatora. Składnia:
argument1 < argument2
argument1 <= argument2
argument1 > argument2
argument1 >= argument2
Operatory przypisania
Argument występujący po lewej stronie przypisania musi być jedną wartością.
a = b (czyli do zmiennej a podstaw wartość zmiennej b)
a *= b (jest skróconym zapisem a = a * b)
a /= b (jest skróconym zapisem a = a / b)
a += b (jest skróconym zapisem a = a + b)
a -= b (jest skróconym zapisem a = a - b)
a %= b (jest skróconym zapisem a = a % b)
Operatory zwiększania (inkrementacji) i zmniejszania (dekrementacji)
Operator zwiększania ++
Dodaje on liczbę 1 do zmiennej przy której ten operator występuje. a++ jest skrótem wyrażenia a = a + 1; lub a += 1;
Składnia: ++zmienna lub zmienna++
Operator zmniejszania --
Odejmuje on liczbę 1 do zmiennej przy której ten operator występuje. a-- jest skrótem wyrażenia a = a - 1; lub a -= 1;
Składnia: --zmienna lub zmienna--
Różnica między operatorami zwiększania i zmniejszania
Istnieją dwa rodzaje operatorów zwiększania ++zmienna oraz zmienna++. Różnica miedzy zmienna++, a ++zmienna jest niewielka, ale istotna. Użycie operatora ++x powoduje, że najpierw jest wykonywana inkrementacja, potem wywoływana jest instrukcja, natomiast x++, jest wykonywany po zakończeniu wszystkich operacji w danej instrukcji. To samo dotyczy operatorów zmniejszania. --x i x--.
Przykład: incdec.cpp
Operatory sizeof
Przekazuje liczbę bajtów będącą rozmiarem wyrażenia lub specyfikatora typu. Składnia: sizeof(specyfikator_typu); lub sizeof wyrażenie;
Proste typy zmiennych
Zmienne to określone miejsce w pamięci komputera gdzie możemy przechowywać informacje w postaci bitowej. Gdy wprowadzisz jakieś informacje do komputera - komputer umieszcza je i przechowuje w swojej pamięci.
Rozmiary i zakresy wartości |
---|
Nazwa typu |
char |
int |
short |
long |
unsigned char |
unsigned |
unsigned short |
unsigned long |
enum |
float |
double |
long double |
w szczególności mamy cztery typy podstawowe:
int - zmienna przechowująca liczby całkowite
char - zmienna przechowująca znaki (litery, itp.)
float - zmienna przechowująca liczby rzeczywiste
double - zmienna przechowująca liczby rzeczywiste
Modyfikowane poprzez kwalifikatory:
unsigned - zmienna przechowuje tylko wartości dodatnie
signed - zmienna przechowuje wartości dodatniej ujemne
short - zmienna ma długość przynajmniej 2 bajtów
long - zmienna ma długość przynajmniej 4 bajtów
const - wartości zmiennej nie można modyfikować, jest to stała
Deklaracja zmiennych
Zmienne mogą być deklarowane, definiowane i inicjowane. Deklarując zmienną musimy podać jej typ i jej nazwę, ewentualnie wartość początkową. Przy nadawaniu nazwy trzeba pamiętać że kompilator ma swoje nazwy zastrzeżone (słowa kluczowe), oraz rozróżnia małe i duże litery.
main() { int liczba1; int liczba2; liczba1=10; liczba2=5; } |
---|
Deklarowanie zmiennych polega na podaniu jej typu a następnie jej nazwy (czyli etykiety). Następnie poprzez operator = możemy dokonać przypisania do zmiennych konkretnych wartości. Pamiętać należy, by w nazwach zmiennych nie używać spacji ani polskich liter, niedopuszczalne są także słowa kluczowe.
stałe
Służą one do jednokrotnego zadeklarowania (zwykle pewnego zakresu obliczeń) i ich wartość nie ulega zmianie podczas działania programu. Możemy nią operować tak jak zmienną ale możemy tylko z niej odczytywać wartość. Jeżeli spróbujemy coś do niej zapisać kompilator przekaże nam błąd. Definiuje się stałą tak samo jak zmienną z tym że musimy podać wartość początkową. Z punktu widzenia komputera stała nie różni się od zmiennej, bo miejsce w pamięci i tak, stosownie do zadeklarowanego typu trzeba zarezerwować, umieścić w tablicy i zapamiętać identyfikator i adres. Stałe możemy zdefiniować na dwa sposoby, przykładowo:
#define maksimum_zakresu 22; #define nazwisko 'Kowalski'; |
---|
Definicja stałej polega na podaniu słowa kluczowego define następnie nazwy stałej i jej wartości. Użycie tego sposobu deklaracji stałych powoduje zwykłe zamienienie tekstu, czyli gdy preprocesor napotka w kodzie nazwę nazwisko, to wstawi na jej miejsce wartość "Kowalski", a ponieważ preprocesor uruchamiany jest przed kompilatorem, ten drugi nigdy nie zobaczy nazwy wiek, tylko wartość stałej. Ponieważ jednak nie używamy tutaj typu zmiennych, zacznie bezpieczniejszą jest deklaracja stałych postaci:
const int maksimum_zakresu=22; const float PI = 3.14; |
---|
co pozwala mieć pewność, że ta deklaracja zostanie dobrze zrozumiana przez kompilator, ponieważ od razu zadeklarowany jest typ stałej. Jest jednocześnie deklaracja, definicja i zainicjowanie stałej.
zasięg zmiennej
Każda zmienna ma określony zasięg, czyli nie wszędzie jest ona dostępna. Największy zasięg ma zmienna która jest zdefiniowana na samym początku pliku przed definicją funkcji. Kod programu składa się z bloków oznaczanych nawiasami {}, jeżeli zmienna została zdefiniowana w bloku, to nie można jej używać w instrukcjach które nie należą do tego bloku. Do tego tematu powrócimy przy funkcjach.
main() { //blok1 intzmienna1; for(zmienna1=1;zmienna1<10;) { //blok2 intzmienna2=1 zmienna1++ //tu można użyć zmiennych z bloku1 zmienna2=zmienna1; } zmienna1=zmienna2; //błąd bo zmienna2 nie ma zasięgu } |
---|
Zmienne lokalne rejestrowe
Jeżeli zmienna w funkcji jest cząsto wykorzystywana, można ją zadeklarować ze słowem register. Wtedy jeżeli tylko to będzie możliwe kompilator umieści zmienną w rejestrze procesora. Warto takie zmienne wykorzystywać zwłaszcza do zmiennych sterującymi pętlami. Np:
for (register int i=0;i>=32476;++i)... |
---|
Można również argument funkcji zadeklarować jako zmienną rejestrową.
int funkcja (register int zmienna1)... |
---|
Przeważnie przy odpowiednim wykorzystaniu może znacznie przyśpieszyć wykonywanie funkcji, a tym samym szybsze działanie programu.
Typy wyliczeniowe
Deklaracja typu wyliczeniowego zaczyna się od słowa kluczowego enum, po którym następuje lista elementów w nawiasach klamrowych. Typ wyliczeniowy kończy się średnikiem. Każdy element wyliczenia jest stałą typu const i indeksowany zbiorem liczb całkowitych, omyślną wartością pierwszego elementu jest 0, a każdego następnego o jeden większy niż poprzedni.
enum BOOL {fałsz, prawda}; |
---|
Tworzy się w ten sposób nowy typ zmiennych, które mogą posiadać wartość w zakresie który my zażądamy.
main() { BOOL zmienna; zmienna=prawda; zmienna=fałsz; } |
---|
Zdefiniowanej w ten sposób zmiennej można przypisać elementy tylko z danego wyliczenia.
drukowanie na ekranie - funkcja printf
Przyszedł czas na napisanie pierwszego programu. Hello World jest uznanym standardem jako pierwszy program - i niech tak już zostanie:
#include <stdio.h> /* dodajemy plik naglowkowy */ main() { printf("Hello World"); } |
---|
Jeśli przeanalizujemy nasz kod linijka po linijce, to zauważymy, że (!) pojawiło się nowe słowo:printf. Jest to funkcja opdpowiedzialna za wyświetlenie napisu określonego w cudzysłowach na ekranie. Funkcja printf jest dostępna po zadeklarowaniu pliku nagłówkowego <stdio.h>.
#include <stdio.h> main() { printf("Hello World"); printf("Hello World"); } |
---|
Powyższy kod spowoduje wyświetlenie napisu Hello WorldHello World, ponieważ printf jest funkcją, która wyświetlając napisy nie przechodzi do następnej linii. Za ten efekt odpowiedzialny jest tag (znacznik) specjalny \n którego użyć możemy w dowolnej części tekstu.
#include <stdio.h> main() { printf("Hello World \n"); printf("Hello World"); } |
---|
znaki specjalne
Znaków specjalnych używanych w funkcji printf, takich jak \n jest więcej, mają one duże znaczenie podczas programowania, są to:
\n - przejście do nowej linii
\t - tabulacja pozioma
\v - tabulacja pionowa
\b - skasowanie znaku w lewo
\r - powrót karetki
\f - wysunięcie strony
\a - sygnał dźwiękowy
\\ - backslash
\? - znak zapytania
\' - apostrof
\" - cudzysłów
pobieranie wartości zmiennych - funkcja scanf
Deklarowanie zmiennych polega na podaniu jej typu a następnie jej nazwy (czyli etykiety). Następnie poprzez operator = możemy dokonać przypisania do zmiennych konkretnych wartości. Pamiętać należy, by w nazwach zmiennych nie używać spacji ani polskich liter. Zalecam także nie używanie dużych liter.
#include <stdio.h> main() { int liczba1; int liczba2; liczba1=10; liczba2=5; } |
---|
By pobrać zmienną wprowadzaną przez nas z klawiatury stosujemy funkcję scanf(), która jest funkcją formatowanego wejścia ze standardowego strumienia wejściowego (stdin), czyli po polsku, funkcja ta jest odpowiedzialna za przypisanie wartości podanej z klawiatury do określonej zmiennej. Iterpretacja pobranych przez funkcję scanf znaków nastąpi zgodnie z życzeniem programisty określonym przez zadany funkcji format, który decyduje, czy pobrane znaki zostaną zinterpretowane np. jako liczba całkowita, znak, łańcuch znaków (napis), czy też w inny sposób. Podając nazwy (identyfikatory) zmiennych należy poprzedzić je w funkcji scanf() operatorem adresowym &. Funkcja scanf jest ona predefiniowana w pliku nagłówkowym stdio.h.
#include <stdio.h> main() { int liczba1; int liczba2; printf("Podaj liczbe: \n"); scanf( "%d" , &liczba1 ); printf( " wpisałeś: %d\n\n " , liczba1 ); printf("Podaj liczbe: \n"); scanf( "%d" , &liczba2 ); printf( " wpisałeś: %d\n\n " , liczba2 ); printf("Liczba x wynosi %d a liczba y wynosi %d\n",liczba1,liczba2); } |
---|
Używając funkcji scanf, najpierw w cudzysłowiu podajemy typ (wzorzec konwersji) pobieranej zmiennej, a po przecinku i ze znakiem & podajemy nazwę zmiennej. Z kolei by wypisać zawartość zmiennej na ekranie, używamy wywołania jej typu (jeszcze w cudzysłowie) a poza cudzysłowiem, po przecinku jej nazwę. Jeśli chcemy wypisać kilka zmiennych jednocześnie - przepis postępowania mamy w ostatniej linijce - wystarczy podać ich typy oraz nazwy tych zmiennych oddzielonych przecinkiem, a zostaną one wypisywane w kolejności w jakiej zostały podane ich nazwy.
Przykład: printfscanf.cpp
Wzorce konwersji
Używane typy (wzorce konwersji) to:
%s - wyprowadź łańcuch znaków (s - string - łańcuch)
Ponieważ onieważ format "%s" jest formatem domyślnym dla funkcji printf() możemy użyć skladni:printf("%s","jakis napis"); ale także printf("Jakis napis");. Ponadto po użyciu printf("%39s","jakis napis"); wyświetlony napis zostanie uzupełniony spacjami do zadanej długości 39 znaków, ponieważ funkcja printf() operuje tzw. polem wyjściowym, gdzie długość pola wyjściowego możemy określić przy pomocy liczb wpisanych pomiędzy znaki % oraz typ - np. s. W ten sposób możemy określić ilość cyfr przed i po przecinku w typach liczbowych.
%c - wyprowadź pojedynczy znak (c - character - znak)
Przykładowo, printf("%c",'X'); spowoduje wydrukowanie litery X.
%d - wyprowadź liczbę całkowitą typu int w postaci dziesiętnej (d - decimal - dziesiętny).
Przykładowo printf("%d", 1994);.
%f - wyprowadź liczbę rzeczywistą typu float w postaci dziesiętnej (f - floating point - zmienny przecinek).
Przykładowo printf("%f", 3.1416); lub korzystając dodatkowo z formatowania pola wyjściowego printf("%f3.2", 3.14159);
%o - wyprowadź liczbę całkowitą typu int w postaci ósemkowej (o - octal - ósemkowa).
Przykładowo printf("%o", 255);
%x - wyprowadź liczbę całkowitą typu int w postaci szesnastkowej (x - heXadecimal - szesnastkowa).
Możemy korzystać opcjonalnie z %x lub %X - cyfry szesnastkowe a,b,c,d,e,f lub A,B,C,D,E,F.
%ld - liczba całkowita "długa" - long int.
%Lf - liczba rzeczywista poczwórnej precyzji typu long double float.
%e - liczba w formacie wykładniczym typu 1.23e-05 (0.0000123)
%g - automatyczny wybór formatu %f albo %e.
Po przytoczeniu przykładów uogólnijmy sposób zastosowania wzorca formatu:
%[przełączniki][szerokość_pola][.precyzja][rozmiar]Typ
W ten sposób, posługując się różnymi sposobami formatowania liczb możemy zażądać wydrukowania liczb w najwygodniejszej dla nas formie. W programie przykładowym hex2dec_dec2hex.cpp dokonujemy zamiany liczb dziesiętnych na szesnastkowe.
Pętle
Pętla to instrukcja powtarzająca fragment kodu programu, bez konieczności jego ponownego wpisywania lub stosowania techniki kopiujemy/wklejamy. Możemy decydować o ilości powtórzeń, oraz czy wykonać kod lub nie.
#inlcude <stdio.h> main() { printf("To jest madry tekst\n"); printf("To jest madry tekst\n"); printf("To jest madry tekst\n"); printf("To jest madry tekst\n"); printf("To jest madry tekst\n"); printf("To jest madry tekst\n"); printf("To jest madry tekst\n"); } |
---|
Co można załatwić jedną pętelką:
pętla for
Ogólna postać pętli for jest następująca:
for (Warunek_inicjujący; Warunek_logiczny; Warunek_kroku) Instrukcja;
Każdy z warunków może zostać pominięty, a wykonanie pętli przebiega następująco:
1. Wykonanie jeden raz warunku inicjującego.
2. Obliczenie wartości logicznej warunku logiczego
3. Jeśli warunek logiczny ma wartość 1 (PRAWDA=TRUE) nastąpi wykonanie instrukcji.
4. Obliczenie wyrażenia kroku.
5. Powtórne sprawdzenie warunku logicznego (czyli powrót do punktu 2).
6. Jeśli warunek logiczny ma wartość 0 (FAŁSZ=FALSE), nastąpi zakończenie pętli.
Przykład:
#include <stdio.h> main() { int i; for(i=0;i<6;i++) printf("To jest madry tekst\n"); } |
---|
czyli powtarzaj instrukcje, od i=0 do 6 razy ze skokiem co 1. Operator inkrementacji i++ spełnia rolę powiększania licznika. Pęta for wykona się określoną ilość razy, zatem uprzednio musimy znać ile powtórzeń kodu dana pętla ma wykonać. Pętla for wymaga trzech parametrów: pierwszy to indeks pętli (czyli licznik typu całkowitego), następnie górny zakres licznika, a trzecim parametrem jest sposób na osiągnięcie górnego zakresu - który jest końcem wykonywania się pętli.
Jeżeli powtarzany kod ma więcej niż jedną linię - obejmujemy go klamrami {} (nie objęcie kodu wieloliniowego spowoduje wykonanie jedynie pierwszej linii).
Zwrócić uwagę, trzeba na fakt, że warunek jest testowany przed wykonaniem instrukcji, zatem jeśli warunek nie zostanie spełniony, instrukcja może wcale się nie wykonać.
pętla while
Czyli wykonuj instrukcje, tak długo jak warunek jest spełniony, przykład:
#include <stdio.h> main() { int i; i=0; while(i<7) { printf("To jest madry tekst\n"); i++; } } |
---|
Pętla ta może udawać pętlę for, ale jej działanie i zastosowanie jest znacznie szersze. Po słowie kluczowym WHILE występuje warunek zakończenia pętli (w naszym przykładzie warunkiem jest przekroczenie wartości zmiennej i), lecz nie jest konieczne przewidywanie dokładnej ilości powtórzeń (tutaj o to zadbaliśmy poprzez i++), jedynie musimy byc pewni, że pętla kiedyś przerwie swoje działanie (bo inaczej program się zawiesi). Warto zauważyć, że pętla może się ani razu nie wykonać, ponieważ pętla ta sprawdza warunek jeszcze przed działaniem i po każdym wykonaniu się ponawia tę czynność. Ma to szerokie zastosowanie w algorytmach gdzie nie jesteśmy w stanie przewidzieć ilości powtórzeń - co od razu wyklucza pętlę FOR.
pętla do...while
Czyli wykonuj instrukcję, do czasu spełnienia warunku, przykład:
#include <stdio.h> main() { int i; i=0; do { printf("To jest madry tekst\n"); i++; } while(i<7); } |
---|
Działanie tej pętli jest identyczne do poprzedniego, ale jest zasadnicza różnica pomiędzy pętlami while i do..while, ponieważ pętla do...while sprawdza warunek po wykonaniu kodu, czyli wykona się przynajmniej raz. Później sprawdzi warunek i zdecyduje co dalej...
Instrukcja warunkowa if...else
Instrukcja if...else, warunkuje wykonanie części kodu od spełnienia (lub nie spełnienia) warunku logicznego. Konstrukcja if-else zakłada, że zawsze zostanie wykonana jedna z instrukcji w zależności od wyniku wyrażenia warunkowego, w szczególności słowo else i instrukcje po słowie else są opcjonalne - możemy je pominąć, pozostawiając samo if. Składnia:
if(warunek) { instrukcje; // wykonane gdy warunek jest spełniony } else { instrukcje; // wykonane gdy warunek jest nieprawdziwy } |
---|
Instrukcja warunkowa pozwala na dokonywanie logicznych wyborów przy konstruowaniu kodu, możemy używać spójników (operatorów) logicznych do konstruowania wyrażeń, zwłaszcza przydatne są podstawy logiki dwuwartościowej, by nasz warunek miał szansę być spełniony. Przykład:
#include <stdio.h> main() { int a,b; a=0; b=0; printf("Podaj a: "); scanf("%d",&a); printf("\nPodaj b: "); scanf("%d",&b); if(a==b) printf("\n a i b sa rowne"); else if(a>b) printf("\n a jest wieksze od b"); else if(a<b) printf("\n a jest mniejsze od b"); } |
---|
instrukcja switch jako decyzyjna instrukcja wyboru opcji
Instrukcja warunkowa if...else doskonale nadaje się do sprawdzania warunków, jednak jeśli mamy wybrać jedną z wielu opcji lepszą instrukcją do zastosowania będzie instrukcja switch. Po słowie kluczowym switch, w nawiasie podajemy zmienną w zależności od której ma być sprawdzany warunek. Następnie w nawiasie klamrowym po słowie case następuje wyliczenie możliwości wyboru (poprzez etykiety wyboru). Jeżeli żaden z warunków nie zostanie spełniony, wówczas uruchomiona zostanie instrukcja przypisana do słowa kluczowego default. Słowo break powoduje przerwanie działania instrukcji w danym miejscu - jeżeli nie użylibyśmy tego słowa, wówczas wszystkie instrukcje poniżej zgodnego warunku także zostałyby wykonane.
Składnia:
switch(warunek) { case wartość1: // jeżeli warunek == wartość1 to wykonuje instrukcje1 instrukcja1; break; case wartość2: instrukcja2; break; default: // jeżeli nie jest spełniony żaden warunek to wykonaj instrukcje3 instrukcja3; break; }; |
---|
Przykład:
#include <stdio.h> main() { int i; printf("podaj liczbe: "); scanf("%d",&i); switch(i) { case 1: printf("wpisales jeden");break; case 2: printf("wpisales dwa");break; case 3: printf("wpisales trzy");break; default: printf("zły wpis....");break; } } |
---|
Po przeanalizowaniu przykładów switch.cpp , switch2.cpp, możemy zaobserwować rolę instrukcji break.
Instrukcje skoku bezwarunkowego
Używać możemy trzech instrukcji skoków bezwarunkowych. Jeżeli program natknie się na jedną z nich, to wykona skok we wskazane (lub domyślne) miejsce.
break
Instrukcję break (przerwij) wykorzystujemy by przerwać wykonywanie się dowolnej z pętli for,while czy do..while, lub w instrukcji warunkowej switch do przerwania sprawdzania warunków. Polecenie break powoduje natychmiastowe bezwarunkowe opuszczenie pętli dowolnego typu i przejście do najbliższej instrukcji po zakończeniu pętli. W przypadku pętli zagnieżdzonych, wywołanie instrukcji break spowoduje zakończenie tej, w której instrukcja się znajduje.
continue
Instrukcja ta kończy działanie przebiegu kodu pętli - czyli jeżeli gdzieś w pętli wystąpi instrukcja continue to program pomija instrukcje występujące po niej i przystępuje do ponownego sprawdzenia warunku. Instrukcja ta powoduje przedwczesne, bezwarunkowe zakończenie wykonania wewnętrznej instrukcji pętli i podjęcie próby realizacji następnego cyklu pętli.
goto
Instrukcja goto jest postrzegana przez programistów jako spadek po BASIC'u, ponieważ za jej pomocą możemy wykonywać skoki do deklarowanej etykiety. Większość domorosłych programistów uważa, że jej stosowanie jest przykładem złej znajomości rzeczy, ale tak na prawdę, użycie goto w uzasadnionym przypadku ma swój sens. Konstrukcja goto jest prosta:
goto nazwa_etykiety; // a w kodzie programu umieszczamy: nazwa_etykiety: instrukcje_etykiety; |
---|
Ograniczenia instrukcji goto nie pozwalają jej na przeskoczenie definicji zmiennej, po każdej etykiecie musi wystąpić co najmniej jedna instrukcja. Instrukcja ta może przydać się choćby do natychmiastowego opuszczenia wielokrotnie zagnieżdżonej pętli. Ponadto goto nie może spowodować przejścia do wykonania instrukcji znajdującej się poza funkcją zawierającą goto.
Instrukcja ta przeczy filozofii języka programowania C, gdzie charakterystyczną cechą jest sekwencyjność: instrukcje są wykonywane w określonym porządku: od góry w dół i z lewej do prawej. Zastosowanie instrukcji skoku powoduje zachwianie tej fundamentalnej zasady. Istnieje naprawdę niewiele sytuacji, w których zastosowanie tej instrukcji byłoby uzasadnione.
Tablice zmiennych
Tablica to indeksowane zbiory komórek zawierających zmienne, przyporządkowane jednej wspólnej nazwie - nazwie tablicy. Można ją rozumieć jako zbiór zmiennych o takiej samej nazwie - różniących się cyferką. Deklaracja tablicy może być postaci:
int tablica[rozmiar];
czyli zadeklarowanie tablicy przechowującej rozmiar (w sensie ilość) wartości typu int. Oczywiście mogą w tablicy być przechowywane wszystkie rodzaje zmiennych. W tym przykładzie zdefiniowana jest tablica jedno wymiarowa. Jednak w razie potrzeby można zdefiniować tablicę kilkuwymiarową. W tedy dopisujemy przy definicji kolejną parę nawiasów kwadratowych i wpisujemy w nie kolejny rozmiar. Rozmiar musi być liczbą stałą, ewentualnie deklarowaną wcześniej zmienną typu const.
Do poszczególnych elementów tablicy odwołujemy się podając w nawiasach kwadratowych numer komórki w których znajduje się interesująca nas wartość. Pamiętać należy, że jeżeli zdefiniujemy tablicę 100 elementową to trzeba się do niej odwoływać od elementu 0 do elementu 99 - jest ich w sumie 100 -, ponieważ tablica indeksowana jest od zera. Odwołanie się do elementu 100 spowoduje błąd, którego kompilator nie wykryje, a będzie sczytywał pamięć poza obszarem przeznaczonym dla tablicy. Jest to częsty błąd, które jest ciężki do wykrycia.
#include <stdio.h> main() { int tab[3]; tab[0]=1; tab[1]=2; tab[2]=3; } |
---|
Analizując powyższy kod, zauważyć możemy sposób deklaracji tablicy. Na początku deklarujemy typ zmiennych przechowywanych w tablicy, następnie jej nazwę, i nawiasach kwadratowych jej rozmiar określający ilość komórek tablicy. Następnie mamy sposób przypisywania konkretnych wartości do poszczagólnych pól tablicy za pomocą indeksów. Po co służą tablice? Pytanie jest proste i odpowiedź jest jeszcze prostsza: spróbuj zadeklarować 300 zmiennych typu int i wklep je pracowicie je do komputera a zobaczysz. Deklarację tablicy możemy połączyć z jednoczesnym jej wypełnieniem, czyli przypisaniem jej wartości.
#include <stdio.h> main() { int tab[5] = {1,2,3,4,5}; } |
---|
co może oszczędzić nam wypełnianie jej później (lub ustawieniu wartości początkowych). Tablice wielowymiarowe stworzymy w sposób: int tablica[10][10]; a odwołania prowadzimy w postaci tablica[1][1]=1; wpisując do tablicy 100 elementowej (10 wierszy 10 kolumn) na pozycję [1][1] liczbę 1. Tablice składające się z większej liczby rozmiarów definiuje się analogicznie.
Przy deklarowaniu tablica, łatwo zapomnieć o fakcie oszczędzania pamięci, ponieważ krótka deklaracja tablicy, może być tak na prawdę deklaracją 1000 zmiennych(!), czyli tyle miejsca musi zostać zarezerwowane - dlatego warto używać wskaźników.
string jako tablica (ciąg) znaków - łańcuch tekstowy
Stringiem określamy tablicę znaków (czyli zmiennych typu char), posiadającą tyle elementów ile wyraz czy zdanie ma liter, plus na samym końcu znak pusty \0 tzw.:wartownik (NULL), oznaczający koniec tablicy. Możemy zadeklarować talicę znaków (string), w postaci:
char string[rozmiar]; |
---|
lub
char string[]="tekst"; |
---|
W tym drugim przypadku kompilator automatycznie przydziela wielkość tablicy dla danego stringa.
manipulowanie na stringach
Łączenie tekstów jest przydatne podczas wprowadzania kilku tekstów i poźniejszym ich wspólnym wyświetlaniu na ekranie. Możemy za pomocą pojedynczych słów zbudować np.: zdanie. Służy do tego funkcja strcat() STRing conCATenation - sklejanie łańcuchów. By połączyć dwa łańcuchy tekstowe napis1 i napis2 w jeden, należy zastosować tę funkcję strcat, w sposób:
strcat(napis1, napis2);
po czym łańcuch znaków napis2 zostanie dołączony do końca łańcucha napis1. Po zakończeniu działania funkcji strcat zmienna napis1 zawiera "swój własny" napis i dołączony na końcu napis zawarty uprzednio w zmiennej napis2. Przeanalizuj przykład strcat.cpp w zakładce przykłady.
Długość łańcucha w Borland/Turbo C++ jest ograniczona do 65536 znaków, możemy używać zerowej długości łańcucha znaków, postaci:
char napis[0] = 0;
czy char *p = "";
lub char napis[50] = "";
jeśli chcemy określić długość łańcucha znaków, stosujemy funkcję strlen() - STRing LENgth, zliczającą kolejne znaki, aż dojdzie do zera. Stosowaną w postaci:
unsigned int dlugosc; ... dlugosc = strlen(tekst); |
---|
Zauważyć należy, że funkcja strlen() w wyniku swojego działania zwraca długość łańcucha tekstowego jako liczbę całkowitą bez znaku (nieujemną), będącą faktyczną długościa tekstu, niezależnie od zadeklarowanej wcześniej długości. Przykładowo:
char string1[30] = "ladny tekst";
gdzie deklarowana maksymalna długość łańcucha znakowego wynosi 30, natomiast faktyczna długość łańcucha znakowego wynosi 11 znaków. Przeanalizuj przykład strlen.cpp w zakładce przykłady.
struktury
Struktura jest to zbiór różnych elementów często różnego typu. Jest odpowiednikiem Pascalowego rekordu. Strukturę deklaruje się za pomocą słowa kluczowego struct. Przykładowo:
struct czlowiek { char imie[10]; char nazwisko[20]; char wiek[10]; }; |
---|
Po słowie kluczowym struct występuje nazwa struktury, dalej w nawiasach klamrowych występują nazwy zmiennych wraz z ich typami. Przykładem zastosowania zmiennych strukturalnych jest tworzenie bazy danych osobowych - wówczas zamiast deklarować zmienne dla każdej osoby - deklarujemy strukturę opisującą jedną osobą i później używamy tej deklaracji. By użyć zmiennej strukturalnej deklarujemy:
struct uczen czlowiek; |
---|
Zmienną typu strukturalnego definiujemy po słowie struct podając nazwę zmiennej a później nazwę struktury. By odwołać się do pól struktury stosujemy odwołanie typu:
uczen.imie='Janek'; uczen.nazwisko='Kowalski'; |
---|
Podajemy nazwę zmiennej strukturalnej, a po kropce podajemy nazwę pola do którego się odwołujemy. Przy deklaracji struktur należy pamiętać że struktura zajmuje tyle miejsca w pamięci, co suma rozmiarów wszystkich jej zmiennych. Warto prześledzić przykład struct.cpp jako przykład sposobu deklaracji struktury i odwoływania się do niej.
Tablice mogą być elementami struktur, ale i odwrotnie - ze struktur, można tworzyć konstrukcje o wyższym stopniu złożoności - struktury struktur i tablice struktur. Jeśli tablica składa się z liczb typu int, to deklarujemy ją:
int TABLICA[10];
jeśli tablica składa się ze struktur, to deklarujemy ją:
struct TABLICA[50];
Przykład struct2.cpp pokazuje sposób zagnieżdzania struktur i deklaracji tablicy w której elementami sa struktury.