Język C/C++
Typy, stałe, zmienne, tablice
Typy zmiennych
Języki C dostarczają dwa rodzaje typów:
typy podstawowe,
typy pochodne, wyprowadzane z typów
podstawowych za pomocą operatorów deklaracji i
mechanizmu deklarownia struktury
Typ danej określa jakie operacje mogą być
wykonywane na danej danego typu i jak te
operacje są intrerpretowane
Z każdą daną związany jest typ określony:
jawnie (w deklaracji/definicji),
poprzez użycie literału (napisu z którego
ten typ wynika)
Typy podstawowe
(nazwy są słowami kluczowymi)
Typami podstawowymi są:
char
int
do reprezentownia zmiennych całkowitych różnych rozmiarów,
float
double
do reprezentownia zmiennych zmiennopozycyjnych
Modyfikatory typu unsigned / signed (domniemany) short /
long do zmiany dokładności i znakowości danych
(np. unsigned long int to całkowita, 4 bajtowa, zakres od 0 –do
ok.4 mld)
CHAR jest LICZBĄ
Typy
(kombinacje signed/unsigned;
short/long;
nazw int, char, float, double)
char (ze znakiem)
- 1 bajt
-128 do 127
unsigned char
- 1 bajt
0 do 255
unsigned short
- 2 bajt
0 do 65535
signed short
- 2 bajty
-32768 do 32767
unsigned int
- 2 bajty
0 do 65535
signed int
- 2 bajty -32768 do 32767
unsigned long int - 4 bajty
0 do 4294967295
signed long int - 4 bajty
-2147483648 do 2147483647
float
- 4 bajty 3.4 E- 38 do 3.4e+38
double
- 8 bajtów 1.7 E-308 do 1.7 E+308
long double
- 10 bajtów 3.4 E-4932 do1.1E+4932
Gdy brak nazwy – to domyślnie jest int
Przy wyborze typu należy pamiętać, że za lepszą dokładność płaci się
większymi żądaniami zasobowymi, a powyższa elastyczność ma różnice
implementacyjne
Standard języka zapewnia
signed char, unsigned char, char – to różne typy,
choć zajmują tyle samo miejsca w pamięci
1=sizeof(char)<=sizeof(short)<=sizeof(int)<=size
of(long)
sizeof(float)<=sizeof(double)<=sizeof(long
double)
sizeof(T)=sizeof(signed T)=sizeof(unsigned T)
gdzie T – typ char, short, int, long
Literały (stałe)
W programach często posługujemy się
stałymi. Możemy używać:
- stałych całkowitych (np. 15)
- stałych zmiennopozycyjnych (np. –1.3E-2)
- stałych znakowych (np. ‘A’)
- literałów napisowych (np. ”Ala ma kota”)
W większości implementacji brak
logicznych
Stałe całkowite
Dziesiętne (bez znaków wiodących) np.:
15, -100
Ósemkowe (jeśli napis zaczniemy od cyfry 0)
014 to 1*8
1
+4*8
0
=12
10
Szesnastkowe (jeśli zapis zaczniemy od 0x/0X)
z cyframi a-f lub A-F (10-15)
0xA1 to 10*16
1
+1*16
0
=17
10
Stałe całkowite traktowane są jak int, chyba,
że trzeba traktować je jako long – wtedy
dopisujemy (przyrostkowo) l/L np. 200L
Stałe zmiennoprzecinkowe
Zapis klasyczny (z kropką), np. 8.22 3. -.6
Notacja naukowa z wykładnikiem
8e2 (to 800) -6.8E-5 (to -0,000068)
Część całkowitą i ułamkową można pominąć, ale
nie obie naraz.
Stałe są traktowane jako double, chyba że
musimy ustalić rozmiar jawnie - używamy
przyrostka f/F lub l/L
Jak widać tutaj nie rozróżnia się wielkości liter!!!
Stałe znakowe
Ujmujemy znaki w symbole apostrofu!!!
‘a’ ‘7’ (cyfra 7 – nie liczba 7)
Podajemy kod znaku ósemkowo lub
szesnastkowo (ale też w apostrofach)
‘\x6B’ to mała litera k (kod 107)
‘\061’ to znak cyfry 1 (kod 49)
Są jednak znaki, których nie można bezpośrednio
umieścić między apostrofami. Służą one do
sterowania wypisywanym tekstem. Są to znaki nie
mające reprezentacji graficznej
'\b' - cofacz(Bsp )
'\v' - tabulator pionowy
'\f' - nowa strona
'\a' - sygnał alarmowy
'\n' - nowa linia -NL(LF)
'\r' - powrót karetki
'\t' - tabulator poziomy
'\\' backslash '\' ' - apostrof
'\" ' - cudzysłów
'\?' = pytajnik.
'\0' - NULL specjalny znak o kodzie 0
‘\0oo’
- ASCII ósemkowo (oktalna)
‘\xhh’
- ASCII szesnastkowo
(heksadecymalna)
Stałe tekstowe (stringi)
Ciągi znaków ujęte w cudzysłów
Przechowywane są w pamięci jako ciąg znaków zakończony
znakiem o kodzie 0(zero) - NULL, dlatego jednoznakowy
string ma długość 2 char’y (jeden na znak i drugi na NULL)
Ze względu na szczególny charakter znaków w rodzaju ” ‘ \
i podobnych, musimy je w stringu poprzedzić
backslashem \
”Witamy w świecie \”C\\C++\””
co daje napis : Witamy w świecie ”C\C++”
Jeśli w napisie umieścimy znak zero \0 to tam się napis
skończy (na ogół)
Jeśli napis jest długi i nie mieści się w jednej linii programu,
kończymy cudzysłowem w jednej i zaczynamy od
cudzysłowu w kolejnej linii. Kompilator je złączy.
Deklaracje stałych (nie-
literałów)
Za pomocą słowa kluczowego const np.:
const float pi=3.14;
Każda próba przypisania stałej pi
wartości (nawet 3.14) jest traktowana
jako błąd
Za pomocą dyrektywy preprocesora
#define np.:
#define pi 3.14
Przed analizą tekstu przez kompilator
następuje automatyczna zamiana tokenów pi
na stałą liczbową 3.14
Typ void
Specyfikuje pusty zbiór wartości
Nie występuje jako typ podstawowy (nie
może być zmiennej/stałej tego typu)
Jest typem funkcji, która nie przekazuje
żadnej wartości
Jest wskaźnikiem do obiektu nieznanego
typu
(coś w rodzaju Pointer pascala)
To były typy proste – a
złożone?
Istnieje nieskończenie wiele typów
pochodnych konstruowanych z typów
podstawowych (tablice, funkcje, wskaźniki,
stałe <const>, klasy, struktury, unie)
Można je wyprowadzić z prostych, za pomocą
operatorów:
Indeksowania []
Wskazania *
Deklarowania funkcji ()
Referencji &
Indeksowanie i funkcje są przyrostkowe,
wskazanie i referencja przedrostkowe.
Deklaracje zmiennych
Postać deklaracji:
typ
lista
;
Gdzie:
lista
to oddzielone przecinkami:
nazwa zmiennej
nazwa zmiennej=stała
nazwa tablicy[liczba elementów]
nazwa tablicy[liczba elementów]={lista
stałych}
nazwa tablicy[ ]={lista stałych}
*nazwa zmiennej adresowej
Należy pamiętać, że w C/C++ jak w każdym języku o znaczeniu praktycznym,
użycie zmiennej musi być poprzedzone jej deklaracją
Tablice
To dane jednego typu (niekoniecznie
prostego) zajmujące ciągły (przyległy) obszar
pamięci (wektor pamięci)
Lokalizacja pojedynczego (atomowego)
składnika tablicy wymaga podanie jej nazwy
oraz indeksu (indeksów) ujętego (dla każdego
wymiaru) w operator indeksowania []
Rozmiar/rozmiary tablicy muszą być znane w
trakcie kompilacji (dynamiczna alokacja
pamięci to praca ze wskaźnikami – później)
Deklaracja tablicy
(jednowymiarowej)
Typ nazwa[liczba elementów]
indeks może zmieniać się od zero do liczba
elementów-1.
Uwaga program nie kontroluje (na ogół)
przekroczenia zakresu, np.
float boki[3];
deklaruje tablicę zmiennoprzecinkowych :
boki[0], boki[1], boki[2]
przypisanie boki[3]=5; spowoduje zniszczenie
(wstawienie float 5.0) w komórki pamięci
leżące bezpośrednio za tablicą boki
Tablica jednowymiarowa cd
Nadanie wartości elementom tablicy może
odbywać się:
Programowo np. boki[2]=7.e-3;
Podczas deklaracji np.
float boki[3]={3,7.5,20e-2};
gdy wymienionych stałych jest mniej, reszta
uzupełniana jest zerami, np.
float boki[3]={3}; //boki
0
=3, boki
1,2
=0
Możliwa jest kombinacja bez podania rozmiaru, jeśli
podana jest lista stałych – rozmiar jest taki jak długość
listy, np.
int a[]={1,2,3,4,5,0,0}; // tak jak int a[7]={1,2,3,4,5};
Tablice wielowymiarowe
To tablice tablic, operator indeksowania działa na
jednym indeksie
int tab[2][3]; // macierz 2 wierszex3 kolumny
Nadajemy wartości jak poprzednio, pamiętając, że:
Odwołanie w programie – każdy indeks w nowym
nawiasie
Nadanie wartości w deklaracji - najszybciej zmienia się
ostatni indeks
Niepełna lista stałych generuje brakujące zera
Kombinacja z brakującym rozmiarem jest
dozwolona tylko dla pierwszego indeksu
Uwagi o deklaracji
zmiennych
C/C++ są elastyczne, jeśli chodzi o
miejsce deklaracji (nie jak w Pascalu,
gdzie sekcje deklaracji i instrukcji były
rozdzielone)
Istnieją różnice implementacyjne
dotyczące zakresu ważności deklaracji
w wewnętrznych blokach (albo od
deklaracji do końca funkcji, albo tylko
wewnątrz bloku)
Program o algorytmie liniowym
to nieporozumienie (albo
przykład)
Wykonaniem instrukcji trzeba sterować
Instrukcją wyboru
Instrukcją wielokrotnego wyboru
Instrukcjami iteracyjnymi
Rekurencją
Pozostałymi instrukcjami sterującymi
Prawie zawsze za wyborem stoją
uwarunkowania logiczne
W języku C/C++ brak typu
logicznego
Decyduje wartość wyrażenia dowolnego
typu
Wartość 0 (zero) odpowiada fałszowi
Wartość niezerowa odpowiada prawdzie
Operacje relacji i operacje logiczne dają
wynikową wartość zero gdy wynikiem
jest fałsz i różną od zera gdy wynikiem
jest prawda
Instrukcja if
Składnia instrukcji if
if (wyrażenie decydujące)instrukcja1;
Składnia instrukcji if else
if (wyrażenie decydujące)instrukcja1;
else instrukcja2;
Instrukcja1 zostanie wykonana gdy wyrażenie będzie niezerowe
a instrukcja2 tylko w przypadku gdy wystąpiła konstrukcja z else
i wyrażenie ma wartość zero
Gdy instrukcja1 lub/i instrukcja2 są złożone, należy użyć
nawiasów { }, uwarunkowane jest wówczas wykonanie
wszystkich instrukcji wewnątrz nawiasów. Użycie nawiasów { }
jest manierą tzw. „prawdziwych Cecowców” nawet gdy
uwarunkowana jest tylko jedna instrukcja
Jeszcze o zagnieżdżonych if
else
Instrukcja else wiązana jest z ostatnią if,
która nie ma else
Do zmiany tego przyporządkowania
używamy { }
if(a>0) if (b<0) printf(”a>0 i b<0”);else
print(”a>0 i b>=0”);
if(a>0){if (b<0) printf(”a>0 i b<0”);}else
print(”a<=0”);
Namiastka if else - ?:
W C/C++ występuje unikalna konstrukcja
szacowania wartości, która w prostych
przypadkach alternatywego wyznaczania wartości
zastępuje if else. Ma postać:
(warunek)?wyrażenie1:wyrażenie2
wynikiem jest wartość wyrażenia1 gdy warunek
jest prawdziwy (różny od zera) lub wartość
wyrażenia2 gdy warunek jest fałszywy (równy
zeru)
Uwaga: ta konstrukcja (zwana operatorem
pytajnikowym) zastępuje if else tylko w
określonych przypadkach. if else służy bowiem do
warunkowania wykonania instrukcji (a nie tylko
pojedynczego podstawienia)
Wybór wielokrotny
W przypadku, gdy od wartości jednego
wyrażenia (o typie porządkowym) zależy
wybór instrukcji używa się zdania switch
o postaci:
switch (wyrażenie desygnujące)
{
case wyrażenie stałe1:instrukcje1;
…
default : instrukcje_w_przeciwnym_razie:
}
Uwagi o switch
Wyrażeń stałych nie można grupować, tworzyć
z nich listy, okrojeń itp. – przed każdą
wartością musi stać słowo case a po niej
dwukropek
Równość wyrażenia stałego z desygnującym to
”punkt wejścia” do listy instrukcji. Od tego
miejsca do końca switch wykonywane są
kolejne instrukcje (także z innymi case). Do
przerwania służy instrukcja break
Manierą jest ujmowanie instrukcji (po
dwukropku) w klamry
Instrukcje iteracyjne
Wykonywanie pętli programowych to
wykorzystanie naturalnych zalet komputera.
Jak wszystkie praktyczne języki, C/C++
mają kilka konstrukcji iteracyjnych:
while, do i for
Zawsze należy dbać o poprawne
skonstruowanie warunków zakończenia pętli
– powszechność pętli w algorytmach to
także powszechność zawieszania się
programów w wyniku złej konstrukcji końca
pętli
Instrukcja while
Instrukcja while
Składnia tej instrukcji jest następująca:
while (wyrażenie) instrukcja;
W pętli while wykonanie instrukcji
powtarza się tak długo, jak długo wartość
wyrażenia jest różna od zera (prawdziwa).
Sprawdzenia tego warunku dokonuje się
przed każdym wykonaniem instrukcji.
Przykłady z while
#include<stdio.h>
void main()
{
char znak;
znak = getchar(); // pobierz znak, <enter>
while(znak != ‘t’)
{ // blok instrukcji
putchar(znak); //wypisz znak
znak = getchar();
}
}
#include<stdio.h>
void main()
{
char znak;
while ((znak = getchar())!= ‘t’)
{ // blok instrukcji
putchar(ch);
}
}
Pętle są częstym (i wdzięcznym) polem „manieryzmu” skutecznie
zmniejszającego czytelność tekstu programu
Instrukcja do..while
Instrukcja do...while
Składnia instrukcji jest następująca:
do instrukcja while wyrażenie;
W pętli do...while wykonanie instrukcji
powtarza się tak długo, jak długo wartość
wyrażenia jest różna od zera (prawdziwa).
Sprawdzenia tego dokonuje się po każdym
wykonaniu instrukcji, dlatego pętla wykona się
zawsze conajmniej raz; to różni ją od pętli while
która może nie wykonać się wcale.
Od znanej z Pascala repeat..until różni ją
przeciwne uwarunkowanie kontynuacji
Porównanie while z do while
#include<stdio.h>
#include<conio.h>
void main()
{
int i=0; float licznik=0;
clrscr();
while(i)
{ // blok instrukcji
printf (”i=%d\n”,i);
i--;
licznik++;
}
printf (”po pętli licznik=%f”,licznik);
}
Wartość licznika 0
#include<stdio.h>
#include<conio.h>
void main()
{
int i=0; float licznik=0;
clrscr();
do
{ // blok instrukcji
printf (”i=%d\n”,i);
i = i-1;
licznik++;
} while (i);
printf(”po pętli licznik=%f”,licznik);
}
Wartość licznika 65536 (???)
Potężna pętla for
Instrukcja for
Składnia pętli for ma postać:
for (inst_ini; wyr_war; wyr_krok) instrukcja;
inst _ini wykorzystywana jest zwykle do deklarowania
(C++)
i inicjowania początkowych wartości zmiennych na
potrzeby pętli,
wyr_war wyrażenie warunkowe wykorzystywane jest do
testowania warunków wykonywania pętli,
wyr_krok określa zmianę stanu pętli i używane jest
zwykle do zwiększania lub zmniejszania zmiennej
indeksującej pętlę.
Wszystkie składniki w nawiasie są opcjonalne, jednak
muszą być zachowane średniki je oddzielające
Dalszy ciąg for
Praca pętli for wykonywana jest według
nastepującego algorytmu:
1. Wykonują się instrukcje inicjujące pętlę inst_inic.
2. Obliczana jest wartość wyrażenia wyr_war. Jeśli
wartość tego wyrażenia jest równa zero, praca pętli
jest przerywana.
3. Jeśli powyżej okazało się, że wyr_war jest != 0,
wtedy wykonywana jest instrukcja
4. Po wykonaniu instrukcji, obliczana jest wartość
wyrażenia wyr_krok.
Powtarzany jest punkt 2.
Jeszcze o pętlach
Pętlami tzw. nieskończonymi są
pętle:
for (;;)
{
//instrukcje
}
while( 1)
{
// instrukcje
}
do
{
//instrukcje
} while (1);
Pozostałe konstrukcje
sterujące
Do tej grupy instrukcji należą:
break;
continue;
return wyrażenie;
goto etykieta;
pozostałe sterujące
break – Służy do przerwania działania
pętli for, while, do...while oraz
instrukcji switch. Instrukcja ta, przenosi
sterowanie do instrukcji bezpośrednio
występującej po pętli lub po instrukcji
switch. Jeśli mamy do czynienia z pętlami
zagnieżdżonymi break przerywa tylko tę
petlę lub instrukcję switch w której
zostało użyte. Instrukcja break przerywa
pętlę zanim wyrażenie testujące pętlę
osiągnie wartość 0.
Pozostałe sterujące
continue -Instrukcja ta może być
używana tylko w pętli for, while,
do...while. Powoduje przeniesienie
sterowania na koniec pętli, pomijając
ciąg instrukcji do końca ciała pętli –
inaczej przechodzi do następnego
„obiegu” pętli.
Pozostałe sterujące
return pozwala funkcji na
natychmiastowe przeniesienie
sterowania do miejsca wywołania
funkcji i przekazania wartości stojącej
po słowie return (w przypadku funkcji
main do systemu operacyjnego).
Pozostałe sterujące
Instrukcja goto
Instrukcja ta, wykonuje bezwarunkowe
przeniesienie sterowania do miejsca
gdzie występuje dana etykieta. Etykieta
musi być umieszczona w tej samej
funkcji. Etykietą jest identyfikator po
którym występuje : (dwukropek)
Często używane w pętlach
operatory inkrementacji i dekrementacji
++;
--;
Realizują operacje zwiększania i zmniejszania o jeden.
Operatory dekrementacji i inkrementacji mogą mieć dwie formy:
- przedrostkową ( prefix):
++i; --i;
- przyrostkową (postfix):
i++; i--;
W obu przypadkach wynikiem jest zmiana wartości i, ale
wyrażenie ++i zwiększa i przed użyciem jej wartości,
natomiast wyrażenie i++ zwiększa zmienną i dopiero po użyciu
jej poprzedniej wartości. Jest to istotne w kontekście, w którym
ważna jest wartość zmiennej i, a nie tylko jej zmiana
Przykład :
i=10; x=++i; //daje i=11 oraz x=11
i=10; x=i++; // daje i=11 oraz x=10
Pytanie (do domu) co daje: i=1;j=3; a=i+++j;