ENTER 8/2000
infoserwis
|
kiosk
|
email/www
|
technologie
|
enter extra
|
słownik
|
prenumerata
|
na CD
infoserwis
subskrypcja
technologie
aktualny numer
spis treści
na krążkach
kiosk on-line
słownik
enter extra
internet od @ do WWW
cyfrowy świat
anatomia pc 1
internet praktyczny
komputer w firmie
małe sieci
muzyka i film
cudowny świat gsm
enter special
kopiujmy co się da
windows xp bez tajemnic
enter w firmie
programy i sprzęt
enter power
więcej mocy
archiwum
numer
cd
prenumerata
reklama
email/www
administracja
nowe konto
pomoc
faq
forum
redakcja
ENTER
8/2000
klub użytkownika
poprzedni
artykuł
następny
artykuł
Sterowanie programem
- Marek
BartosiewiczW tej części cyklu dokładniej przypatrzymy się instrukcjom sterującym,
poznamy wbudowane typy danych z pochodnymi, a także wspomnimy o operatorach arytmetycznych i logicznych. Nabyte wiadomości
wykorzystamy do napisania prostego pseudobenchmarku, mierzącego prędkość zapisu danych przez procesor w pamięci RAM.
Kompilator C/C++ tłumaczy kod w postaci zrozumiałej dla człowieka (plik tekstowy ASCII napisany zgodnie z regułami języka C/C++) na postać strawną dla procesora (kod binarny instrukcji maszynowych). Język C został zaprojektowany jako „asembler wysokiego poziomu”, dlatego znacząca większość jego instrukcji, a także formatów danych, ma odzwierciedlenie w kodzie maszynowym. Zapewnia to sprawne i szybkie działanie tak przetłumaczonego programu.
Jak już wiemy z poprzedniego odcinka, zanim użyjemy w naszym programie jakiejkolwiek nazwy (zmiennej, stałej, obiektu itp.), musimy poinformować kompilator o swoich zamierzeniach. Potrzebne nam obiekty definiujemy, pisząc np.:
float z1;
long int z2 = 1;
Druga linijka definiując „z2” jako zmienną typu całkowitego (32 bity) przeprowadza jej inicjalizację i nadaje wartość „1”. Deklaracja typu zmiennej, która jest reprezentowana przez liczbę całkowitą, może zostać poprzedzona modyfikatorem: „signed” (oznaczającym, że zmienna przyjmuje wartości dodatnie lub ujemne) lub „unsigned” (tylko wartości nieujemne). Tym samym mamy do wyboru (kompilator dla 32-bitowych systemów operacyjnych):
Typ
Rozmiar
Zakres
Short int
16 bitów
od –32768 do +32767
Unsigned short int
16 bitów
od 0 do 65535
int
32 bity
od –2147483648 do +2147483647
unsigned int
32 bity
od 0 do 4294967295
signed char
8 bitów
od –128 do +127
unsigned char
8 bitów
od 0 do 255
W razie wątpliwości możemy skorzystać z operatora, który zwraca rozmiar obiektu, podany jako argument, w bajtach. Nosi on nazwę „sizeof”:
cout « “Rozmiar typu int: ”
« sizeof(int) « endl;
cout « ”Rozmiar zmiennej z1: ”
« sizeof(z1) « endl;
Natomiast jeśli chodzi o liczby zmiennoprzecinkowe, istnieją trzy ich typy:
float
32 bity
double
64 bity
long double
80 bitów
Pamiętajmy, że wyższa precyzja obliczeń (więcej bitów przeznaczonych dla zmiennej) okupiona jest dłuższym czasem ich trwania. Trzy typy liczb rzeczywistych pozwalają dokonać wyboru między wydajnością a dokładnością obliczeń.
Na podstawie wymienionych typów fundamentalnych możemy tworzyć tzw. typy pochodne. Najlepszym przykładem są tablice. Tworzymy (definiujemy) je w taki sposób:
float tab1[32];
int tab2[100][100];
W nawiasach kwadratowych podana jest liczba elementów danego typu, z których będzie się składać tablica. Nawiasy kwadratowe są w języku C/C++ operatorem odwołania się do określonego elementu tablicy. Pamiętajmy, że numeracja elementów zaczyna się od zera: „tab1[0]” oznacza pierwszy element tablicy zmiennych rzeczywistych, a „tab1[31]” – ostatni. Tablica „tab2” jest dwuwymiarowa: do jej obsługi potrzebne są dwa indeksy, np. „wynik = tab2[1][1] + tab2[1][2];”.
Do najczęściej używanych tablic w języku C/C++ należą łańcuchy znaków (stringi):
...
char komunikat[] =
”To jest komunikat.”;
...
cout « komunikat « endl;
...
Zauważmy, że w definicji tablicy „komunikat” nie został wyspecyfikowany jej rozmiar. Kompilator uprości nam życie i zrobi to automatycznie, zliczając znaki występujące po prawej stronie znaku przypisania.
Pozostałe typy pochodne tworzy się za pomocą operatorów:
* – wskaźnik kierujący ku obiektowi danego typu,
& – referencja obiektu danego typu, operator pobrania adresu,
() – funkcja zwracająca wartość danego typu.
Przykłady użycia:
int i = 0; //obiekt ”int” o wartości 0
int *w1; //deklaracja wskaźnika kierującego ku ”int”
w1 = &i; //wskaźnik z1 pokazuje obiekt i
...
float wynik;
wynik = pierwiastek(x);
Wskaźnikami obiektów i referencjami, a także funkcjami, zajmiemy się dokładniej w dalszych odcinkach. Teraz wspomnimy jedynie, że nazwa tablicy jest jednocześnie wskaźnikiem jej pierwszego elementu (czyli przechowuje adres pierwszego elementu). Poprawne są więc następujące dwa sposoby odwoływania się do elementów tablicy:
int tablica[100];
...
wynik1 = tablica[7] + tablica[16];
wynik2 = *(tablica + 7) +
*(tablica + 16);
if (wynik1 == wynik2) cout
« ”To samo!”;
...
Pojawia się tutaj znowu gwiazdka, ale nie oznacza ona tym razem po prostu wskaźnika, lecz operator odwołania się do zawartości pamięci wskazywanej przez ten wskaźnik (w naszym przykładzie w nawiasach są liczby powodujące „przeskoczenie” o 7 lub 16 elementów „do przodu” od wskazania „tablica”). To bardzo ważne rozróżnienie: w definicji „w1” wystąpiła gwiazdka, która poinformowała kompilator, że „w1” ma być wskaźnikiem. Jeśli korzystanie ze wskaźnika w dalszej części programu odbywa się już bez tej gwiazdki, operujemy na adresie przechowywanym przez „w1”. Dopiero użycie „*w1” powoduje odczyt lub zapis zawartości pamięci wskazywanej przez „w1”.
Operatory
Język C++ został wyposażony w 56 operatorów o różnym działaniu: manipulacyjnych, arytmetycznych i logicznych. Ograniczymy się do podania jedynie najpotrzebniejszych w początkowym etapie nauki – przedstawia je tabela. Działania z operatorami o wyższym priorytecie wykonywane są przed działaniami korzystającymi z operatorów o niższym priorytecie. Wystarczy jednak zapamiętać, że działania arytmetyczne przebiegają w takiej kolejności, jaką znamy ze szkoły. W razie wątpliwości podczas pisania bardziej skomplikowanych wyrażeń używajmy nawiasów – poprawią czytelność i zagwarantują poprawne obliczenie wyrażenia.
Instrukcje sterujące
Ogólna zasada działania instrukcji sterujących polega na podejmowaniu decyzji, co do dalszego wykonywania fragmentu kodu programu, na podstawie obliczanego podczas pracy programu wyrażenia stanowiącego warunek. Wynik tych obliczeń przyjmuje jedną z dwóch wartości: „prawda” lub „fałsz”. W odróżnieniu od języka Pascal, C/C++ nie ma specjalnego typu zmiennych do przechowywania wartości logicznych (w Pascalu taką funkcję pełni typ „boolean”). Można użyć dowolnego z istniejących (np. zmienną typu „int”) lub zdefiniowanych przez programistę obiektów. Podczas kontroli warunku sprawdzana jest zawartość obiektu: jeśli okaże się różna od zera, oznacza to „prawdę”, gdy wynosi zero – „fałsz”.
Pętle
Pętla typu „for” ma postać:
for (inicjalizacja; warunek; krok)
{
...
}
Inicjalizacja to instrukcja (lub kilka instrukcji rozdzielonych przecinkami) wykonywana przed rozpoczęciem pracy pętli. Warunek to wyrażenie obliczane za każdym obiegiem pętli, przed wykonaniem instrukcji składających się na ciało pętli. Jeśli ma wartość „0”, praca pętli zostanie przerwana, w przeciwnym wypadku zostaną wykonane instrukcje wewnątrz pętli. Krokiem nazywamy instrukcję (lub kilka instrukcji) wykonywanych na koniec obiegu pętli, bezpośrednio przed powtórnym obliczeniem wyrażenia warunkowego, np. pętla
for (i = 0; i < 10; i = i + 1)
wykonana zostanie dziesięć razy.
Pętlę „do {...} while(warunek);” poznaliśmy już w poprzednim odcinku. Jak pamiętamy, wyrażenie warunkowe obliczane jest po wykonaniu (co najmniej raz) ciała pętli, a sama pętla wykonywana jest, dopóki warunek przyjmuje wartość niezerową (prawda). Jej druga forma wygląda następująco:
while(warunek) {...}
Tym razem wyrażenie warunkowe zostanie obliczone w pierwszej kolejności, przed wykonaniem ciała pętli. Jej wykonywanie zostanie przerwane, gdy warunek przyjmie wartość zero (fałsz).
Instrukcje warunkowe
Podstawową instrukcję warunkową „if” również mieliśmy okazję poznać w poprzednim spotkaniu. Drugą przydatną instrukcją tej klasy jest „switch” – dzięki niej możemy dokonać wyboru wielowariantowego:
int wybor;
...
cin » wybor;
switch (wybor)
{
case 1:
...
break;
case 2:
...
break;
case 8:
...
break;
default:
...
break;
}
Działanie instrukcji wyboru wielowariantowego: obliczane jest wyrażenie umieszczone w nawiasach po słowie „switch”. Jeśli jego wartość zgadza się z którąś z etykiet, występujących po słowach „case”, wykonywane są instrukcje zaczynające się od tej etykiety, aż do najbliższej instrukcji „break” (która powoduje wyskoczenie z instrukcji „switch”). Gdy wartość wyrażenia nie pasuje do żadnej z etykiet, wykonany zostaje blok instrukcji po słowie kluczowym „default” (ta część jest opcją, nie musi występować w każdej instrukcji „switch”), aż do najbliższego „break”.
Instrukcje pomocnicze dla instrukcji sterujących
Poznane przed chwilą „break” powoduje bezwarunkowe przerwanie instrukcji „switch”, a w wypadku użycia wewnątrz pętli (dowolnego rodzaju) natychmiast przerwie jej wykonanie. Istnieje jeszcze druga, przydatna wewnątrz pętli instrukcja: „continue”. Sprawia ona, iż zostaje przerwany aktualny obieg pętli, ale nie samo jej wykonywanie. Rozpocznie się po prostu następny jej obieg:
for (int i = 0; i < 4; i++)
{
if (i == 1) continue;
cout « i « endl;
}
W przykładzie nie zostanie wyświetlona liczba ”1”.
Teoria w praktyce
Naszym zadaniem będzie pomiar tempa, w jakim procesor zapisuje dane w pamięci. Ze względu na prędkość dzisiejszych procesorów nie można zmierzyć czasu trwania pojedynczej operacji zapisu danych, lecz wielu milionów. Aby zminimalizować wpływ pamięci cache na wynik, dane należy zapisywać w różnych lokacjach pamięci, najlepiej w kolejności rosnącej. Najprostszym rozwiązaniem jest użycie odpowiednio (w zależności od prędkości komputera) dużej tablicy liczb całkowitych.
Na płytce zamieściliśmy gotowe programy, obrazujące kolejne kroki rozbudowy naszego programu. Polecamy ich przeanalizowanie, gdyż ograniczona objętość artykułu nie pozwala na zamieszczenie całości kodu źródłowego. Pliki mają nazwy „speed.cpp”, „speed1.cpp”, „speed2.cpp”, począwszy od najprostszego (i najmniej przydatnego) rozwiązania problemu pomiaru. Wszystkie zostały wyposażone w komentarze wyjaśniające działanie poszczególnych instrukcji.
Ze względu na „nadgorliwość” kompilatora, aby otrzymać w miarę sensowne wyniki, musimy wyłączyć wszystkie optymalizacje kodu (przełącznikiem „-Od” – co zapewni nam przełożenie kodu C/C++ na maszynowy dosłownie, polecenie kompilacji przyjmie postać „bcc32 -Od program.cpp”). Na moim komputerze otrzymałem wyniki: 8 bitów – ok. 43 MB/s, 16 bitów – ok. 69 MB/s, 32 bity – ok. 80 MB/s. W zależności od prędkości komputera warto poeksperymentować z ustawieniem stałej „ile”. Właściciele szybkich maszyn muszą uważać, aby nie wstawić zbyt mało. Jeśli zmierzony czas będzie mniejszy od 1 ms, wartość zwracana przez funkcję „clock()” nie zdąży się zmienić i w wyniku późniejszych obliczeń dostaniemy komunikat „błąd dzielenia przez zero”.
Lista przydatnych operatorów
Operator Nazwa Priorytet
[] element tablicy 16
() wywołanie funkcji 16
() wyrażenie w nawiasach 16
sizeof rozmiar obiektu, typu 15
++ inkrementacja 15
– dekrementacja 15
! negacja 15
& pobranie adresu 15
* referencja wskaźnika 15
() rzutowanie 15
* mnożenie 13
/ dzielenie 13
% modulo (reszta z dzielenia) 13
+ suma 12
- różnica 12
« przesunięcie bitów w lewo 11
» przesunięcie bitów w prawo 11
< mniejsze 10
<= mniejsze lub równe 10
> większe 10
>= większe lub równe 10
== równe 9
!= nierówne 9
& iloczyn bitowy (and) 8
^ różnica symetryczna (xor) 7
| suma bitowa (or) 6
&& koniunkcja (logiczne and) 5
|| alternatywa (logiczne lub) 4
= przypisanie 3
ENTER
12/2002
ENTER POWER
ENTER W FIRMIE
ENTER SPECIAL
ENTER
EXTRA
INDEKS CEN
IMPREZY
patronuje:
Seminaria StatSoft
KONKURSY
KRZYŻÓWKA ENTERA
W Y N I K I:
KRZYŻÓWKA ENTERA 10'02
KOMIKS 7'02
ANKIETA
Z jakiego komunikatora korzystasz najczęściej?
Gadu-Gadu
ICQ
MSN Messenger
Tlen.pl
z innego
Wyniki ankiet >>
PCkurier |
CRN |
Telenet forum |
CADCAM FORUM |
AEC forum
Copyright © 1997-2002 by LUPUS Sp. z o.o.
Polityka prywatności
uwagi
odnośnie stron internetowych prosimy kierować do webmastera
Wyszukiwarka
Podobne podstrony:
automatyka i sterowanie wykladSterownik dwubarwnych diod LEDSterownik nadajnika do lowow na lisasterowniki programowalne plc, cz??? 3Sterownik oswietlenia kabiny samochoduOptymalne sterowanie i tradycyjny rachunek wariacyjny Dwuwymiarowe zagadnienie NewtonaPRZYCISKI STEROWANIA RT3Moduł zdalnego sterowania PC 1Sterowce latające dinozaurywięcej podobnych podstron