WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
1
PODSTAWY
PROGRAMOWANIA
kurs I - część 2
PROWADZĄCY: dr inż. Marcin Głowacki
E-Mail:
Marcin.Glowacki@pwr.wroc.pl
Pok.907 C-5
Wrocław 2011
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
2
3.
INSTRUKCJE WEJŚCIA – WYJŚCIA
Operacje wejścia-wyjścia służą do bezpośredniej komunikacji użytkownika z programem
poprzez ekran i klawiaturę oraz do zapisu i odczytu danych z plików a także urządzeń
peryferyjnych.
W języku C występują funkcje
standardowe wejścia-wyjścia
zdefiniowane w pliku
nagłówkowym (ang. header)
<stdio.h>
W C++ używane są obiekty klas
istream
i
ostream
tzw. strumienie wejścia – wyjścia (ang.
streams).
INSTRUKCJE WYJŚCIA
Funkcja
printf
int
printf
( const char *format [, argumenty]... );
Inne funkcje z rodziny printf to: fprintf, sprintf i snprintf
Parametry:
format
– ciąg znaków definiujący format, w jakim zostaną wypisane kolejne argumenty
%
[
flagi
] [
szerokość
] [.
precyzja
] [{h | l | I | I32 | I64}]
typ
•
określenie
typu
formatu.
o
opcjonalne użycie dowolnej liczby flag,
o
opcjonalne określenie minimalnej szerokości pola,
o
opcjonalne określenie precyzji – dokładności wyświetlanych liczb,
o
opcjonalne określenie rozmiaru argumentu,
Typ
Funkcje z rodziny
printf
obsługują następujące typy formatów:
•
d, i
- argument typu int jest przedstawiany jako liczba całkowita ze znakiem w postaci
[-]ddd
.
•
o, u, x, X
- argument typu unsigned int jest przedstawiany jako nieujemna liczba
całkowita zapisana w systemie oktalnym (
o
), dziesiętnym (
u
) lub heksadecymalnym (
x
i
X
).
•
f, F
- argument typu double jest przedstawiany w postaci
[-]ddd.ddd
.
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
3
•
e, E
- argument typu double jest reprezentowany w postaci
[i]d.ddde+dd
, gdzie liczba
przed kropką dziesiętną jest różna od zera, jeżeli liczba jest różna od zera, a + oznacza
znak wykładnika. Format E używa wielkiej litery E zamiast małej.
•
g, G
- argument typu double jest reprezentowany w formacie takim jak f lub e
(odpowiednio F lub E) zależnie od liczby znaczących cyfr w liczbie oraz określonej
precyzji.
•
a, A
- argument typu double przedstawiany jest w formacie [-]0xh.hhhp+d czyli
analogicznie jak dla e i E, tyle że liczba zapisana jest w systemie heksadecymalnym.
•
c
- argument typu int jest konwertowany do unsigned char i wynikowy znak jest
wypisywany. Jeżeli podano modyfikator rozmiaru l argument typu wint_t konwertowany
jest do wielobajtowej sekwencji i wypisywany.
•
s
- argument powinien być typu wskaźnik na char (lub wchar_t). Wszystkie znaki z
podanej tablicy, aż do i z wyłączeniem znaku null są wypisywane.
•
p
- argument powinien być typu wskaźnik na void. Jest to konwertowany na serię
drukowalnych znaków w sposób zależny od implementacji.
•
n
- argument powinien być wskaźnikiem na liczbę całkowitą ze znakiem, do którego
zapisana jest liczba zapisanych znaków.
Flagi
W sekwencji możliwe są następujące flagi:
•
-
(minus) oznacza, że pole ma być wyrównane do lewej, a nie do prawej.
•
+
(plus) oznacza, że dane liczbowe zawsze poprzedzone są znakiem (plusem dla liczb
nieujemnych lub minusem dla ujemnych).
•
spacja
oznacza, że liczby nieujemne poprzedzone są dodatkową spacją; jeżeli flaga plus i
spacja są użyte jednocześnie to spacja jest ignorowana.
•
#
(
hash
) powoduje, że wynik jest przedstawiony w alternatywnej postaci:
o
dla formatu
o
powoduje to zwiększenie precyzji, jeżeli jest to konieczne, aby na
początku wyniku było zero;
o
dla formatów
x
i
X
niezerowa liczba poprzedzona jest ciągiem
0x
lub
0X
;
o
dla formatów
a, A, e, E, f, F, g, G
wynik zawsze zawiera kropkę nawet jeżeli nie
ma za nią żadnych cyfr;
o
dla formatów
g
i
G
końcowe zera nie są usuwane.
•
0
(
zero
) dla formatów
d, i, o, u, x, X, a, A, e, E, f, F, g, G
do wyrównania pola
wykorzystywane są zera zamiast spacji za wyjątkiem wypisywania wartości
nieskończoność i NaN. Jeżeli obie flagi
0
i
-
są obecne to flaga zero jest ignorowana. Dla
formatów
d, i, o, u, x, X
jeżeli określona jest precyzja flaga ta jest ignorowana.
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
4
Szerokość pola i precyzja
Minimalna szerokość pola oznacza ile najmniej znaków ma zająć dane pole. Jeżeli wartość po
formatowaniu zajmuje mniej miejsca jest ona wyrównywana spacjami z lewej strony (chyba, że
podano flagi, które modyfikują to zachowanie). Domyślna wartość tego pola to 0.
Precyzja dla typów formatów określa:
•
d, i, o, u, x, X
- minimalną liczbę cyfr, które mają być wyświetlone i ma domyślną
wartość 1;
•
a, A, e, E, f , F
- liczbę cyfr, które mają być wyświetlone po kropce i ma domyślną
wartość 6;
•
g, G
- liczbę cyfr znaczących i ma domyślną wartość 1;
•
s
- maksymalną liczbę znaków, które mają być wypisane.
Szerokość pola może być albo dodatnią liczbą zaczynającą się od cyfry różnej od zera albo
gwiazdką. Podobnie precyzja z tą różnicą, że jest jeszcze poprzedzona kropką. Gwiazdka
oznacza, że brany jest kolejny z argumentów, który musi być typu int. Wartość ujemna przy
określeniu szerokości jest traktowana tak jakby podano flagę - (minus).
Rozmiar argumentów:
[{h | l | I | I32 | I64}]
Dla formatów
d
oraz
i
można użyć jednego z przykładowych modyfikatorów rozmiaru:
•
h
- oznacza, że format odnosi się do argumentu typu
short
,
•
l
(małe L) - oznacza, że format odnosi się do argumentu typu
long
,
•
I32
– oznacza, że format odnosi się do argumentu typu
_int32
•
I64
– oznacza, że format odnosi się do argumentu typu
_int32
•
I
- oznacza, że format odnosi się do argumentu typu
ptrdiff_t
, który jest
__int32
na
platformach 32-bitowych oraz
__int64
na 64-bitowych
Dla formatów typu
o, u, x, X
można użyć takich samych modyfikatorów rozmiaru jak dla
formatu
d
i oznaczają one, że format odnosi się do argumentu odpowiedniego typu bez znaku, a
dla rozmiaru I oznacza, że format odnosi się do argumentu typu
size_t
, który jest
unsigned
__int32
na platformach 32-bitowych oraz
unsigned __int64
na 64-bitowych
Dla formatu typu
n
można użyć takich samych modyfikatorów rozmiaru jak dla formatu
d
i
oznaczają one, że format odnosi się do argumentu będącego wskaźnikiem na dany typ.
Wartość zwracana
Jeżeli funkcje zakończą się sukcesem zwracają
liczbę znaków
w tekście
wypisanym na
standardowe wyjście
, do podanego strumienia lub tablicy znaków, nie wliczając kończącego '\0'.
W przeciwnym wypadku zwracana jest liczba ujemna.
Znaki specjalne
Aby wydrukować znaki specjalne należy je poprzedzić znakiem backslash:
printf("Procent: %% Backslash: \\");
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
5
Efekt:
Procent: % Backslash: \
Dygresja
:
Funkcja
wprintf
jest wersją z szerokimi znakami (ang.wide-character) funkcji printf; format jest
wtedy ciągiem wchar_t . Funkcje wprintf and printf zachowują się identycznie.
API (ang. Application Programming Interface) z „wąskimi” znakami nadaje się tylko do
kodowań, w których jeden znak zajmuje jeden bajt.
API z „szerokimi” znakami nadaje się do dowolnych kodowań obsługiwanych przez system, przy
czym programista musi podawać ciągi wchar_t (jeden cchar_t może zawierać kilka wchar_t, jeśli
to jest znak z osobno kodowanymi akcentami)
.
Przykład:
[msdn.microsoft.com]
// crt_printf.c
/* This program uses the printf function
* to produce formatted output.
*/
#include <stdio.h>
int main(){
char ch = 'h', *string = "computer";
int count = -9234;
double fp = 251.7366;
wchar_t wch = L'w', *wstring = L"Unicode";
/* Display integers. */
printf( "Integer formats:\n"
" Decimal: %d Justified: %.6d Unsigned: %u\n",
count, count, count );
printf( "Decimal %d as:\n Hex: %Xh C hex: 0x%x Octal: %o\n",
count, count, count, count );
/* Display in different radixes. */
printf( "Digits 10 equal:\n Hex: %i Octal: %i Decimal: %i\n",
0x10, 010, 10 );
/* Display characters. */
printf("Characters in field (1):\n%10c%5hc%5C%5lc\n", ch, ch, wch, wch);
wprintf(L"Characters in field (2):\n%10C%5hc%5c%5lc\n", ch, ch, wch, wch);
/* Display strings. */
printf("Strings in field (1):\n%25s\n%25.4hs\n %S%25.3ls\n",
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
6
string, string, wstring, wstring);
wprintf(L"Strings in field (2):\n%25S\n%25.4hs\n %s%25.3ls\n",
string, string, wstring, wstring);
/* Display real numbers. */
printf( "Real numbers:\n %f %.2f %e %E\n", fp, fp, fp, fp );
/* Display pointer. */
printf( "\nAddress as: %p\n", &count);
/* Count characters printed. */
printf( "\nDisplay to here:\n" );
printf( "1234567890123456%n78901234567890\n", &count );
printf( " Number displayed: %d\n\n", count );
}
Efekt:
Integer formats:
Decimal: -9234 Justified: -009234 Unsigned: 4294958062
Decimal -9234 as:
Hex: FFFFDBEEh C hex: 0xffffdbee Octal: 37777755756
Digits 10 equal:
Hex: 16 Octal: 8 Decimal: 10
Characters in field (1):
h h w w
Characters in field (2):
h h w w
Strings in field (1):
computer
comp
Unicode Uni
Strings in field (2):
computer
comp
Unicode Uni
Real numbers:
251.736600 251.74 2.517366e+002 2.517366E+002
Address as: 0022FF6C
Display to here:
123456789012345678901234567890
Number displayed: 16
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
7
Funkcja
puts
Identyczna w działaniu do wywołania: printf("%s\n", argument); może jednak działać szybciej.
Zawsze na końcu wysyłany jest znak przejścia do nowej linii.
Przykład:
#include <stdio.h>
int main(){
puts("Hello world!");
return 0;
}
Efekt:
Hello world!
Funkcja
fputs
Podobnie jak puts, z argumentem określającym strumień wyjścia, np. stdout lub pliku, który
został wcześniej otwarty do zapisu. Działa bez wypisania na końcu znaku przejścia do nowej
linii.
#include <stdio.h>
int main()
{
fputs("Hello world!\n", stdout); // lub stderr
return 0;
}
Efekt:
Hello world!
Funkcja
putchar
Służy do wypisywania pojedynczych znaków. Przykładowo jeżeli chcielibyśmy napisać program
wypisujący w prostej tabelce wszystkie liczby od 0 do 99 moglibyśmy to zrobić tak:
include <stdio.h>
int main() {
int i = 0;
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
8
for (; i<100; ++i) {
/* Nie jest to pierwsza liczba w wierszu */
if (i % 10) {
putchar(' ');
}
printf("%2d", i);
/* Jest to ostatnia liczba w wierszu */
if ((i % 10)==9) {
putchar('\n');
}
}
return 0;
}
INSTRUKCJE WEJŚCIA
Funkcja
scanf
Funkcja wczytuje dane ze standardowego strumienia wejścia stdin, które zwykle podłączone
jest do klawiatury, ale może również być dołączone w systemie operacyjnym do innego
strumienia wyjścia niż bufor obsługi klawiatury. Ważny jest nowy operator:
&
, gdyż bez niego
funkcja scanf() nie skopiuje odczytanej wartości liczby do odpowiedniej zmiennej. Operator
&
oznacza przekazanie do funkcji
adresu do zmiennej - wskaźnika
, aby funkcja mogła
zmodyfikować jej wartość. Dzięki temu funkcja scanf może zapamiętać odczytane z wejścia
wartości. Bez znajomości adresu (wskaźnika) do zmiennej funkcja otrzymałaby jedynie obecną
wartość zmiennej bez możliwości jej zmiany.
#include <stdio.h>
int main (){
int liczba = 0;
printf ("Podaj liczbę: ");
scanf ("%d",
&
liczba);
printf ("%d*%d=%d\n", liczba, liczba, liczba*liczba);
return 0;
}
Wczytanie ciągu znaków (tekstu) do tablicy znakowej.
#include <stdio.h>
int main()
{
char tablica[100]; /* 1 */
scanf("%s", tablica); /* 2 */
return 0;
}
Bezpieczna wersja programu z ograniczeniem na wczytywanie danych do 99 znaków
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
9
#include <stdio.h>
int main(){
char tablica[100];
scanf("%99s", tablica);
return 0;
}
Wczytanie liczby:
#include <stdio.h>
int main(){
int n;
while (scanf("%d", &n)==1) {
printf("%d\n", n*n*n);
}
return 0;
}
Wczytanie kolejno dwóch liczb:
#include <stdio.h>
int main(){
int a, b;
while (scanf("%d %d", &a, &b)==2) {
printf("%d\n", a+b);
}
return 0;
}
Trochę bardziej skomplikowany przykład. Podobnie jak poprzednio program będzie wypisywał 3
potęgę podanej liczby, ale tym razem musi ignorować błędne dane (tzn. pomijać ciągi znaków,
które nie są liczbami) i kończyć działanie w momencie, gdy nastąpi błąd odczytu lub koniec
pliku:
#include <stdio.h>
int main(){
int result, n;
do {
result = scanf("%d", &n);
if (result==1) {
printf("%d\n", n*n*n);
} else if (!result) { /* !result to to samo co result==0 */
result = scanf("%*s"); //ignorowanie zawartości bufora
}
} while (result!=EOF);
return 0;
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
10
}
Analiza:
Najpierw wywoływana jest funkcja scanf() i następuje próba odczytu liczby typu int. Jeżeli
funkcja zwróciła 1 to liczba została poprawnie odczytana i następuje wypisanie jej trzeciej
potęgi. Jeżeli funkcja zwróciła 0 to na wejściu były jakieś dane, które nie wyglądały jak liczba.
W tej sytuacji wywołujemy funkcję scanf() z formatem odczytującym dowolny ciąg znaków nie
będący białymi znakami z jednoczesnym określeniem, żeby nie zapisywała nigdzie wyniku. W
ten sposób niepoprawnie wpisana dana jest omijana. Pętla główna wykonuje się tak długo jak
długo funkcja scanf() nie zwróci wartości EOF.
Funkcja
gets -
nie należy jej używać !
Przyjmuje jeden argument - adres pierwszego elementu tablicy, do którego należy zapisać
odczytaną linię - i nic poza tym. Z tego powodu nie ma żadnej możliwości przekazania do tej
funkcji rozmiaru bufora podanego jako argument. Podobnie jak w przypadku scanf() może to
doprowadzić do przepełnienia bufora, co może mieć tragiczne skutki. Zamiast tej funkcji należy
używać funkcji fgets().
Funkcja
fgets
fgets
(
tablica_znaków, rozmiar_tablicy_znaków, stdin
);
Funkcja czyta tekst aż do napotkania znaku przejścia do nowej linii, który także zapisuje w
wynikowej tablicy (funkcja gets() tego nie robi). Jeżeli brakuje miejsca w tablicy to funkcja
przerywa czytanie, w ten sposób, aby sprawdzić czy została wczytana cała linia czy tylko jej
część należy sprawdzić czy ostatnim znakiem nie jest znak przejścia do nowej linii. Jeżeli
nastąpił jakiś błąd lub na wejściu nie ma już danych funkcja zwraca wartość NULL.
#include <stdio.h>
int main() {
char buffer[128], whole_line = 1, *ch;
while (fgets(buffer, sizeof buffer, stdin)) { /* 1 */
if (whole_line) { /* 2 */
putchar('>');
if (buffer[0]!='>') {
putchar(' ');
}
}
fputs(buffer, stdout); /* 3 */
for (ch = buffer; *ch && *ch!='\n'; ++ch); /* 4 */
whole_line = *ch == '\n';
}
if (!whole_line) {
putchar('\n');
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
11
}
return 0;
}
Analiza:
Powyższy kod wczytuje dane ze standardowego wejścia - linia po linii - i dodaje na początku
każdej linii znak większości, po którym dodaje spację jeżeli pierwszym znakiem na linii nie jest
znak większości. W linijce 1 następuje odczytywanie linii. Jeżeli nie ma już więcej danych lub
nastąpił błąd wejścia funkcja zwraca wartość NULL, która ma logiczną wartość 0 i wówczas
pętla kończy działanie. W przeciwnym wypadku funkcja zwraca po prostu pierwszy argument,
który ma wartość logiczną 1. W linijce 2 sprawdzamy, czy poprzednie wywołanie funkcji
wczytało całą linię, czy tylko jej część - jeżeli całą to teraz jesteśmy na początku linii i należy
dodać znak większości. W linii 3 najzwyczajniej w świecie wypisujemy linię. W linii 4
przeszukujemy tablicę znak po znaku, aż do momentu, gdy znajdziemy znak o kodzie 0
kończącym
ciąg znaków
albo znak przejścia do nowej linii. Ten drugi przypadek oznacza, że
funkcja fgets() wczytała całą linię.
Funkcja
getchar()
Jest to bardzo prosta funkcja, wczytująca 1 znak z klawiatury. W wielu przypadkach dane mogą
być buforowane przez co wysyłane są do programu dopiero, gdy bufor zostaje przepełniony lub
na wejściu jest znak przejścia do nowej linii. Z tego powodu wpisaniu danego należy nacisnąć
klawisz Enter, aczkolwiek trzeba pamiętać, że w następnym wywołaniu zostanie zwrócony znak
przejścia do nowej linii. Gdy nastąpił błąd lub nie ma już więcej danych funkcja zwraca wartość
EOF (która ma jednak wartość logiczną 1 toteż zwykła pętla
while (getchar())
nie da
oczekiwanego rezultatu):
#include <stdio.h>
int main(){
int c;
while ((c = getchar())!=EOF) {
//koniec gdy Ctrl-Z
if (c==' ') {
//zamiana spacji na podkreślenie
c = '_';
}
putchar(c);
}
return 0;
}
Ten prosty program wczytuje dane znak po znaku i zamienia wszystkie spacje na znaki
podkreślenia. Może wydać się dziwne, że zmienną c zdefiniowaliśmy jako trzymającą typ int, a
nie char. Właśnie taki typ (tj. int) zwraca funkcja getchar() i jest to konieczne ponieważ wartość
EOF wykracza poza zakres wartości typu char (gdyby tak nie było to nie byłoby możliwości
rozróżnienia wartości EOF od poprawnie wczytanego znaku).
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
12
STOSOWANIE FUNKCJI z rodziny
printf
i
scanf
:
Argumenty za:
są bardzo wygodne
cały szablon jest w jednym miejscu
umożliwiają łatwe dodawanie informacji nt. formatu
Przeciw:
trudno je rozszerzyć o nowe typy
kompilator nie zawsze może sprawdzić ich poprawność
mogą wystąpić problemy z bezpieczeństwem
konieczna znajomość typu każdego przetwarzanego wyrażenia
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
13
W języku
C++
w pliku nagłówkowym
<iostream>
predefiniowane są strumienie standardowe:
cout
– strumień standardowego wyjścia domyślnie skierowany na ekran;
cin
– strumień standardowego wejścia domyślnie dołączony do klawiatury
cerr
- strumień standardowego wyjścia komunikatów o błędach (errors) domyślnie skierowany na
ekran. Strumień niebuforowany – natychmiastowe powiadomienie o błędach.
clog
- strumień standardowego wyjścia komunikatów do dziennika (logu) domyślnie skierowany
na ekran. Strumień buforowany – może gromadzić komunikaty, a gdy zbierze się kilka wysłać do
dziennika co powoduje większą optymalizację pracy dziennika.
Do związania danych ze strumieniami służą operatory:
<<
i
>>
int k;
cin
>>
k;
//kierunek informacji ze strumienia cin do zmiennej k
cout
<<
”Wartość k: ”<<k; //kierunek informacji do strumienia cout ze zmiennej k
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
14
FLAGI STANU FORMATOWANIA
Są to zasady formatowania zapisane w słowie typu
long
enum {
skipws
= 0x0001
//ignoruje białe znaki (spacje, tabulacje)
left
= 0x0002
//justowanie lewe
right
= 0x0004
//justowanie prawe
internal
= 0x0008
//justowanie ”wewnętrzne”
dec
= 0x0010
//konwersja dziesiętna
oct
= 0x0020
//konwersja ósemkowa
hex
= 0x0040
//konwersja hexadecymalna
showbase
= 0x0080
//pokaż podstawę konwersji
showpoint
= 0x0100
//pokaż kropkę dziesiętną
uppercase
= 0x0200
//wielkie litery w liczbach
showpos
= 0x0400
//znak + w liczbach dodatnich
scientific
= 0x0800
//notacja wykładnicza (naukowa)
fixed
= 0x1000
//notacja zwykła
unitbuf
= 0x2000
//buforowanie
stdio
= 0x4000
//współpraca z stdio
};
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
15
Modyfikacje stanu flag formatowania mogą być wprowadzone przy użyciu trzech metod:
•
przy użyciu elementarnych funkcji składowych klasy ios o nazwach:
setf
,
unsetf
•
złożonych funkcji składowych klasy ios, których nazwy przypominają to co robią:
width
,
precision
i
fill
.
•
manipulatorów – wpuszczania do strumienia specjalnych kodów zmieniających
formatowanie
Przykład użycia elementarnych funkcji składowych klasy ios o nazwach:
setf
,
unsetf
#include <iostream>
#include <cstdlib>
using namespace std;
int main() {
using std::cout; using std::endl; using std::cin;
float x=1175;
_Ios_Fmtflags stare_flagi;
cout << x <<endl; //domniemane ustawienia formatowania
cout<<"Zapamietanie flag formatowania\n";
stare_flagi=cout.flags();
cout<<"Stare flagi to: "<<hex<<cout.flags()<<endl;
cout.setf(ios::showpoint);
cout<<x<<endl;
cout.setf(ios::scientific, ios::floatfield);
cout<<x<<endl;
cout.setf(ios::uppercase);
cout<<x<<endl;
cout.unsetf(ios::showpoint); //wyłączenie flagi showpoint
cout<<x<<endl;
cout<<"Powrot do starych flag formatowania\n";
cout.flags(stare_flagi);
cout<<x<<endl;
return 0;
}
Funkcja
int width(int)
ustala rozmiar pola, na którym będzie wydrukowana liczba.
float x=1175;
cout.width(8);
//liczba będzie wyświetlana na ośmiu polach
lub
char napis[7];
//tekst o rozmiarze 6 znaków
cin.width(sizeof(napis));
cin>>napis;
Funkcja
char fill(char)
wypełnienie dla pustych miejsc, jeśli przewidziane pole jest dłuższe niż
zawartość zmiennej.
int saldo = 2573;
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
16
cout<<”stan konta: ”;
cout.width(9);
cout.fill(‘*’);
//dopelnienie gwiazdami pustych miejsc
cout << saldo<<”,-\n”;
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
17
Funkcja
int precision(int)
ustala poziom dokładności.
float x=72.435672591143;
Y = 10.55;
Z = 2;
cout << ”Domyślna dokładność to: ”<<( cout.precision() )<<endl;
cout <<”x: ”<<x<<” y: ”<<y<<” z: ”<<z<<endl;
cout.precision(8);
cout << ”Teraz dokładność to: ”<<( cout.precision() )<<endl;
cout <<”x: ”<<x<<” y: ”<<y<<” z: ”<<z<<endl
;
Manipulatory
umożliwiają wysyłanie do strumieni informacji o formatowaniu.
Występuje zestaw manipulatorów predefiniowanych w klasie ios:
•
bezargumentowe:
dec
,
hex
,
oct, flush, endl, ends i ws
int i=30;
cout<<i<<endl;
//postać dziesiętna
cout<<i<<” ,”<<hex<<i<<endl;
•
parametryzowane zdefiniowane w bibliotece
<iomanip>
.
o
setw(int)
– ustawia szerokość
#include <iomanip>
cout<<setw(5)<<m<<setw(9)<<m<<setw(7)<<m<<endl;
char slowo[6];
//deklaracja na slowo o rozmiarze do 5 znaków
cin>>setw(sizeof(slowo))>>slowo;
o
setfill(int)
– ustawia wypełnienie wolnej przestrzeni
int kwota=3200;
cout<<”Stan konta: ”<<setfill(‘*’)<<setw(9)<<kwota<<”-,”<<endl;
o
setprecision(int)
– ustawia dokładność
double x=99.123456789;
cout<<x<<”, ”<<setprecision(2)<<x<<”, “<<setprecision(8)<<x<<endl;
o
setiosflags(long)
oraz
resetiosflags(long)
- ustawia i kasuje wskazane flagi
int liczba=242;
cout <<hex<<liczba<<”, ”<<setiosflags(ios::uppercase)<<liczba
<<resetiosflags(ios::uppercase)<<liczba<<endl;
o
setbase(int)
– określa podstawę dla konwersji liczb, czyli to samo co manipulatory
dec
,
hex
i
oct
int liczba=100;
cout<<dec<<liczba<<hex<<liczba<<oct<<liczba;
to samo co
cout<<setbase(10)<<liczba<<setbase(16)<<liczba<<setbase(8)<<liczba;
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
18
NIEFORMATOWANE OPERACJE WEJŚCIA / WYJŚCIA
W klasie
istream
zdefiniowane są funkcje:
•
get
– wczytanie jednego znaku (bajtu), Funkcja jest przeciążona i może wczytywać
zadaną liczbę znaków lub do chwili napotkania wzorca.
istream&
get
(
char &
);
int
get
();
//funkcja zwraca kod znaku w postaci int
istream&
get
(
char
);
istream&
get
(
char *, int
);
//drugi argument to długość wczytywanych znaków
istream&
get
(
char *, int , int =’\n’
);
//domyślny wzorzec to znak nowej linii
Przykłady:
char
znak
;
cin.get
(
znak
);
//wczytanie bajtu ze strumienia cin
//wczytywane są także białe znaki (spacje i tabulacje)
//- w odróżnieniu od
cin
>>
znak
;
//------------------- WCZYTANIE ZNAKU ------------------------
char
znak
;
cout<<”Wciskaj klawisze (^Z-konczy)”<<endl;
while((
cin.get
(
znak
))!=0)
cout<<”Znak : ”<<
znak
<<” ma kod ”<<int(
znak
)<<endl;
//------------ WCZYTANIE ZNAKU INACZEJ – A MOśE TAK SAMO ? --------
char
znak
;
cout<<”Wciskaj klawisze (^Z-konczy)”<<endl;
while((char(znak)=
cin.get
())!=EOF ) //EOF – znak końca pliku tekstowego
//funkcja zwraca kod znaku w postaci int
cout<<”Znak : ”<<
znak
<<” ma kod ”<<int(
znak
)<<endl;
//----------------------- WCZYTANIE ZNAKÓW DO TABLICY ---------------------
char
zdanie
[
80
];
cout<<”Wpisz zdanie:”<<endl;
cin.get
(
zdanie,
sizeof
(zdanie)
);
//
cin.get
(
zdanie,
80
);
cout<<”Twoje zdanie to: ”<<
zdanie
<<endl;
//----------- WCZYTANIE ZNAKÓW DO WYSTĄPIENIA WZORCA ------------
char
zdanie
[
80
];
cout<<”Wpisz zdanie:”<<endl;
cin.get
(
zdanie,
sizeof
(zdanie),’
,
’
);
// wczytuj do wystąpienia przecinka
cout<<”Twoje zdanie to : ”<<
zdanie
<<endl;
UWAGA:
Nie wczytane dane z powodu limitu długości lub napotkania wzorca pozostają
w buforze strumienia i mogą być czytane następnymi instrukcjami. Napotkany znak
wzorca zostaje zwrócony do strumienia.
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
19
•
getline
– wczytanie zadanej liczby bajtów lub do napotkania wskazanego znaku
istream&
getline
(
char *, int , int=’\n’
); //domyślny wzorzec to znak nowej linii
Działa podobnie do
get
(
char *, int , int=’\n’
) z tą różnicą, że zabiera i usuwa ze
strumienia napotkany wzorzec. Funkcja zwraca referencję do strumienia, na którym
pracuje. Umożliwia to kaskadowe łączenie takich funkcji, np. cin.getline(...).getline(...)
•
read
– wczytanie określonej liczby bajtów w trybie binarnym. Nie dołącza znaku ‘\0’ na
końcu jak to robią poprzednie funkcje
istream&
read
(
char *, int
);
Funkcja zwraca referencję do strumienia, na którym pracuje. Umożliwia to kaskadowe
łączenie takich funkcji, np. cin.read(...).read(...)
•
ignore
– wczytuje i ignoruje grupę bajtów ze strumienia
istream&
ignore
(
int , int
);
•
gcount
– pozwala dowiedzieć się ile znaków zostało wczytanych za pomocą
nieformatowanej operacji wejścia/ wyjścia
int
gcount
();
Przykład:
char
zdanie
[
80
];
cout<<”Wpisz zdanie:”<<endl;
cin.get
(
zdanie,
sizeof
(zdanie),’
,
’);
// wczytuj do wystąpienia przecinka
cout<<”Zdanie: ”<<
zdanie
<<”. ma : ”<<
cin.gcount()
<<” znakow”<<endl;
•
peek
– służy do sprawdzenia jaki bajt czeka w strumieniu, nie wyjmuje bajtu
int
peek
();
Przykład:
char
zdanie
[
80
];
float
liczba
;
int
test
;
cout<<”Wprowadz dane (tekst lub liczbe):”;
test =
cin.peek()
;
if(isdidgit(
test
)) {
cin>>liczba;
cout<<”Wprowadziłeś liczbę: “<<
liczba
<<endl;
} else {
cin.getline(
zdanie,
sizeof
(zdanie)
);
cout<<”Wprowadziłeś tekst: “<<
zdanie
<<endl;
}
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
20
•
putback
– umożliwia zwrócenie do strumienia wyjętego bajtu. Bajt ten wraca do
strumienia i może następnie być wyjęty operacją wyjmowania.
istream &
putback
(
char
);
W klasie
ostream
zdefiniowane są funkcje:
•
put
– wkłada do strumienia jeden bajt
ostream &
put
(char)
Przykład:
char
zdanie
[]={”I do Love You”};
int i=0;
do {
cout.put(zdanie[i]).put(”_”);
} while(zdanie[++i]);
Efekt: I_ _d_o_ _ L_o_v_e_ _Y_o_u.
•
write
– wkłada do strumienia zadaną liczbę bajtów
ostream &
write
(char *, int)
Przykład:
char zdanie[]={”I do Love You”};
cout.write(zdanie,2);
cout<<”hate”;
cout.write(zdanie+10,4);
Efekt:
I hate You.
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
21
OPERATORY ARYTMETYCZNE
Operator
Działanie
Postać matematyczna
=
instrukcja przypisania
z = x;
+
dodawanie
z = x + y;
-
odejmowanie
z = x – y;
*
mnożenie
z = x * y;
/
dzielenie
z = x / y;
%
dzielenie modulo = reszta z dzielenia
z = x % y;
--
zmniejszanie o jeden (dekrementacja)
z = z – 1;
++
zwiększanie o jeden (inkrementacja)
z = z + 1;
+=
skrót przypisania
z += x;
to samo co:
z = z + x;
-=
skrót odejmowania
z -= x;
to samo co:
z = z – x;
*=
skrót mnożenia
z *= x;
to samo co:
z = z * x;
/=
skrót dzielenia
z /= x;
to samo co:
z = z / x;
Ogólna postać instrukcji przypisania wygląda następująco:
zmienna = wyrażenie;
Można też stosować wielokrotnie takie instrukcje, np.:
z = x = y = 0;
Program realizujący operację dodawania dwóch liczb typu float.
#include <iostream>
#include <conio.h>
int main()
{
float x, y, z; // deklaracja zmiennych
using std::cout; using std::endl; using std::cin;
cout << endl << "Podaj dwie liczby " << endl;
cin >> x >> y;
z = x + y;
cout << x << " + " << y <<" = " << z<<endl;
return 0;
}
W przykładzie wykorzystano operatory
dodawania
+
oraz
przypisania
=
.
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
22
OPERATORY RELACYJNE i LOGICZNE
Każda różna od zera liczba, z którą spotykamy się w C, posiada wartość
TRUE
(prawda) ,
natomiast liczba 0 posiada wartość
FALSE
(nieprawda). Wyrażenia, w których występują
operatory relacyjne bądź logiczne, zwracają wartość
1
(
TRUE
) lub
0
(
FALSE
). W zależności
od potrzeb posługujemy się następującymi operatorami:
Operatory relacyjne
Operator
Działanie
>
większy
<
mniejszy
>=
większy lub równy
<=
mniejszy bądź równy
==
równy
!=
różny
Operatory logiczne
Operator
Działanie
&&
koniunkcja AND (i)
||
alternatywa OR (lub)
!
negacja NOT (nie)
Posługując się przedstawionymi operatorami należy zawsze pamiętać, że posiadają one różny
priorytet wykonywania kolejnych działań. Rozpatrzmy to na przykładzie wyrażenia
5-4 != 0
.
Jego wartość obliczana jest w taki sposób, że najpierw zostanie wykonana operacja odejmowania
liczb, a dopiero potem sprawdzony warunek, czy rezultat odejmowania jest różny od zera, tzn.:
(5-4) != 0
. Należy też pamiętać, że operator
()
ma największy priorytet. Jeżeli nie jesteśmy
pewni priorytetów stosowanych operatorów zawsze w wątpliwych sytuacjach możemy posłużyć
się właśnie operatorem
()
.
OPERATORY BITOWE
Operator
Działanie
&
bitowa koniunkcja AND (i)
|
bitowa alternatywa OR (lub)
~
bitowa negacja NOT (nie)
^
bitowa różnica symetryczna XOR
<<
przesuniecie bitów w lewo (mnożenie przez 2)
>>
przesuniecie bitów w prawo (dzielenie przez 2)
Możliwe jest w pewnych szczególnych zastosowaniach wykonywanie operacji na liczbach w
logice bitowej.
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
23
PRZEDROSTKI I PRZYROSTKI
Zarówno operator inkrementacji (
++
), jak i dekrementacji (
--
) występuje w dwóch odmianach:
•
przedrostkowej (np.
++x
) jest obliczany przed przypisaniem (zwiększ wartość zmiennej i
zapamiętaj ją)
x=5;
int y = ++x;
cout <<x<<”\t”<<y;
//po wykonaniu: x jest równe 6, y jest równe 6
•
przyrostkowej (np. x++) jest obliczany po przypisaniu (pamiętaj pierwotną wartość
zmiennej, po czym zwiększ wartość)
x=5;
int y = x++;
cout <<x<<”\t”<<y;
//po wykonaniu: x jest równe 6, y jest równe 5
Przykład działania operatora przedrostkowego i przyrostkowego
1: #include <iostream>
2: int main() {
3: using std::cout;
4: int mojWiek = 39; // inicjalizujemy dwie zmienne całkowite
5: int twojWiek = 39;
6: cout << "Ja mam: " << mojWiek << " lat.\n";
7: cout << "Ty masz: " << twojWiek << " lat\n";
8: mojWiek++; // inkrementacja przyrostkowa
9: ++twojWiek; // inkrementacja przedrostkowa
10: cout << "Minal rok...\n";
11: cout << "Ja mam: " << mojWiek << " lat.\n";
12: cout << "Ty masz: " << twojWiek << " lat\n";
13: cout << "Minal kolejny rok\n";
14: cout << "Ja mam: " << mojWiek++ << " lat.\n";
15: cout << "Ty masz: " << ++twojWiek << " lat\n";
16: cout << "Wypiszmy to jeszcze raz.\n";
17: cout << "Ja mam: " << mojWiek << " lat.\n";
18: cout << "Ty masz: " << twojWiek << " lat\n";
19: return 0;
20: }
Efekt:
Ja mam: 39 lat.
Ty masz: 39 lat
Minal rok...
Ja mam: 40 lat.
Ty masz: 40 lat
Minal kolejny rok
Ja mam: 40 lat.
Ty masz: 41 lat
Wypiszmy to jeszcze raz.
Ja mam: 41 lat.
Ty masz: 41 lat
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
24
INSTRUKCJA DECYZYJNA
if
Instrukcja
if
jest pytaniem czy jest spełniony
warunek
, który może być wyrażeniem logicznym
TRUE/ FALSE lub wprost wartością odpowiednio różną od zera (!=0) lub równą zero (==0).
if
(
warunek
) {
//jeśli warunek spełniony TRUE (!=0)
instrukcja;
instrukcja;
...
};
lub
if
(
warunek
)
instrukcja;
//jeśli jedna instrukcja to może być bez klamry
if (warunek)
TAK
NIE
instrukcja;
instrukcja;
...
Można też stosować wielobok zamiast rombu.
if (warunek)
TAK
NIE
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
25
INSTRUKCJA DECYZYJNA
if ... else
Instrukcja
if ... else
jest pytaniem czy jest spełniony
warunek
, który może być wyrażeniem
logicznym TRUE/ FALSE lub wprost wartością odpowiednio różną od zera (!=0) lub równą zero
(==0). W przypadku nie spełnienia
warunku
następuje wykonanie instrukcji występujących po
else
. Dopiero wtedy wykonywane są następne instrukcje w programie. W przypadku spełnienia
warunku
instrukcje po
else
są omijane.
if
(
warunek
) {
//jeśli warunek spełniony TRUE (!=0)
instrukcja;
instrukcja;
...
}
else
{
//jeśli warunek nie jest spełniony FALSE (0)
instrukcja;
instrukcja;
...
};
lub
if
(
warunek
)
instrukcja;
//jeśli jedna instrukcja to może być bez klamry
else
instrukcja;
if (warunek)
TAK
NIE
instrukcja;
instrukcja;
...
instrukcja;
instrukcja;
...
else
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
26
Program – ilustracja złożonych warunków logicznych dla
if ... else
#include <iostream>
int main()
{
using std::cout; using std::endl; using std::cerr;
enum KOLORY {bialy=1, czerwony, zielony, zolty, niebieski, fioletowy};
KOLORY kolor=zielony;
int liczba=2;
//liczba musi być w zakresie koloru i nieparzysta to kolor też musi być nieparzysty lub
// gdy parzysta to kolor też parzysty
if
(((liczba<=6)&&(liczba>=1))&&(((!(kolor%2))&&(!(liczba%2)))||((kolor%2)&&(liczba%2))))
cout<<"Liczba: "<<liczba<<"\tKolor: "<<kolor<<endl;
else
cerr<<"Bledna kombinacja liczb i kolorow"<<endl;
return 0;
}
UWAGA: Przy niespełnionym warunku przy
if
nie wiemy, które z kryteriów nie było spełnione
Program – Ten sam program i te same warunki logiczne rozbite na kaskadę wielu
if ... else
#include <iostream>
int main()
{
using std::cout; using std::endl; using std::cerr;
enum KOLORY {bialy=1, czerwony, zielony, zolty, niebieski, fioletowy};
KOLORY kolor=bialy;
int liczba=4;
//jesli liczba jest w zakresie koloru i parzysta to kolor tez parzysty
if
((liczba<=6)&&(liczba>=1))
{
//sprawdzanie zakresu liczby
if
((!(kolor%2))&&(!(liczba%2)))
//sprawdzanie parzystej liczby i koloru
cout<<"Liczba parzysta: "<<liczba<<"\tKolor parzysty: "<<kolor<<endl;
else
if
((kolor%2)&&(liczba%2))
//sprawdzanie nieparzystej liczby i koloru
cout<<"Liczba nieparzysta: "<<liczba<<"\tKolor nieparzysty: "<<kolor<<endl;
else
cerr<<"Liczba w zakresie, ale nie pasuje do koloru"<<endl;
}
else
cerr<<"Liczba poza zakresem"<<endl;
return 0;
}
Sprawdzane są poszczególne kryteria i wiadomo jakie występują kombinacje liczby i koloru. W
ten sposób użytkownik jest dokładnie informowany o szczegółach zarówno wyników
sprawdzania oraz występujących błędów.
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
27
INSTRUKCJA DECYZYJNA
switch
Instrukcja
switch
porównuje wyrażenie z wartością dla wybranych przypadków (
case
). Przy
zgodności wartości wykonane zostaną instrukcje zawarte po
case
oraz wszystkie instrukcje po
case
umieszczonych poniżej oraz po
default
, chyba że pojawi się rozkaz wyjścia z instrukcji:
break
. W przypadku braku zgodności ze wszystkimi wartościami po
case
może być wykonany
ciąg instrukcji dla przypadku domyślnego
default
.
switch
(
wyrażenie
) {
case
wartość_1:
instrukcja;
instrukcja;
...
break;
case
wartość_2:
instrukcja;
instrukcja;
...
break;
...
case
wartość_n:
instrukcja;
instrukcja;
...
break;
default
:
//fragment wykonywany domyślnie dla braku zgodności
//wartości wyrażenia w sprawdzanych przypadkach
case
instrukcja;
instrukcja;
};
lub
switch
(
wyrażenie
) {
case
wartość_1:
//można łączyć wartości, które posiadają wspólne instrukcje
case
wartość_2:
instrukcja;
instrukcja;
...
break;
...
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
28
if (wyra
ż
enie==warto
ść
_1)
TAK
NIE
instrukcja;
instrukcja;
...
switch (wyra
ż
enie)
case warto
ść
_1:
if (wyra
ż
enie==warto
ść
_2)
NIE
case warto
ść
_2:
TAK
instrukcja;
instrukcja;
...
if (wyra
ż
enie==warto
ść
_n)
NIE
case warto
ść
_n:
TAK
instrukcja;
instrukcja;
...
instrukcja;
instrukcja;
...
default:
. . .
. . .
break;
break;
break;
break;
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
29
Program – przykład użycia instrukcji
switch
#include <iostream>
int main()
{
using std::cout; using std::endl; using std::cerr;
enum OCENA {mierny=1,ndst,dst,db,bdb,celujacy};
OCENA zaliczenie=mierny;
switch
(
zaliczenie
) {
case
mierny
:
case
ndst
: cout <<"Ocena: "<<int(zaliczenie)<<"\tBrak zaliczenia."<<endl;
break;
case
dst
:
case
db
:
case
bdb
:
case
celujacy
: cout <<"Ocena: "<<int(zaliczenie)<<"\tZaliczenie."<<endl;
break;
default
: cerr<<"Błędna ocena"<<endl;
}; return 0;
}
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
30
INSTRUKCJA CYKLICZNA
while
Instrukcje cykliczne nazywane w żargonie pętlami.
Instrukcja
while
sprawdza czy zachodzi
warunek
i jeśłi tak (ang.
while
= podczas gdy
warunek
spełniony) wykonuje instrukcje zawarte w klamrze. Następnie ponownie sprawdza
warunek
. Po
pierwszym wejściu w cykl instrukcja
while
może zakończyć działanie jeśli instrukcje wewnątrz
while
lub zdarzenia wejścia/wyjścia wpłyną na dane w taki sposób, że
warunek
przestanie być
spełniany lub zostanie wykonana instrukcja
break
przerywająca pętlę
while
w dowolnym
momencie. W dowolnym momencie podczas wykonywania pętli może być wydana komenda
continue
, która spowoduje przejście do ponownego sprawdzenia
warunku
.
while
(
warunek
) {
instrukcja;
instrukcja;
...
};
lub
while
(
warunek
) instrukcja; //jeśli jedna instrukcja to może być bez klamry
if (warunek)
TAK
NIE
instrukcja;
instrukcja;
...
break;
continue;
continue;
while (warunek)
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
31
Program – demonstruje działanie
while
#include <iostream>
int main()
{
using std::cout; using std::cin;
long int iSuma=0,iIloczyn=1,iLiczba=0; //lepiej dać long int
cout<<"Podaj liczby (0 - konczy): ";
cin>>
iLiczba
;
while
(
iLiczba
) {
//zero kończy program – warunek: iLiczba!=0
iSuma+=
iLiczba
;
iIloczyn*=iLiczba;
cout<<"Suma liczb = "<<iSuma<<'\t';
cout<<"Iloczyn liczb = "<<iIloczyn<<endl;
cout<<"Nastepna liczba (0 - konczy): ";
cin>>
iLiczba
;
}
return 0;
}
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
32
INSTRUKCJA CYKLICZNA
do ... while
Instrukcja
do ... while
działa podobnie do
while
z tą różnicą, że najpierw są wykonywane
instrukcje a potem sprawdzany
warunek
. Powoduje to przynajmniej jednokrotne wykonanie
cyklu instrukcji w pętli. Po pierwszym wejściu w cykl instrukcja
do ... while
może zakończyć
działanie jeśli warunek nie jest spełniony. Przy kolejnych wykonaniach jedynie instrukcje
wewnątrz
do ...
while
lub zdarzenia wejścia/wyjścia wpłyną na dane w taki sposób, że
warunek
przestanie być spełniany lub zostanie wykonana instrukcja
break
przerywająca pętlę
do ... while
w dowolnym momencie. W dowolnym momencie podczas wykonywania pętli może być wydana
komenda
continue
, która spowoduje przejście do ponownego sprawdzenia
warunku
.
do
{
instrukcja;
instrukcja;
...
}
while
(
warunek
);
lub
do
instrukcja
while
(
warunek
);
//jeśli jedna instrukcja to może być bez klamry
if (warunek)
TAK
NIE
instrukcja;
instrukcja;
...
break;
continue;
while (warunek)
do
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
33
Program – demonstruje działanie
do ... while
#include <iostream>
int main()//Program oblicza dlugość szeregu wprowadzanych liczb
{
using std::cout; using std::cin;
int iDlugosc=0, iLiczba;
do
{
cout<<"Podaj liczbe z szeregu (0 - konczy): ";
cin >>iLiczba;
iDlugosc++;
}
while
(
liczba
);
iDlugosc--; //korekta dlugosci o liczbe 0, która jest sygnalem do wyjscia
cout<<"Dlugosc szeregu liczb wynosi " <<iDlugosc<<endl;
return 0;
}
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
34
INSTRUKCJA CYKLICZNA
for
Instrukcja cykliczna for służy do wykonywania zadań na skończonym zbiorze danych. Dzięki
wykorzystaniu dodawania (inkrementacji) lub odejmowania (dekrementacji) możliwe jest
iteracyjne (krokowe) ze zdefiniowanym krokiem przetwarzanie lub dostęp do danych, np.
operacje na tablicach, obliczenia dla ciągu argumentów itp.
for
(instrukcje_1;
warunek
;
iteracje
) {
instrukcja;
instrukcja;
...
};
lub
for
(instrukcje_1;
warunek
;
iteracje
)
//jeśli jedna instrukcja to może być bez klamry
instrukcja;
if (warunek)
TAK
NIE
instrukcja;
instrukcja;
...
break;
continue;
for(instrukcje_1; warunek; iteracje)
iteracje
instrukcje_1
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
35
Przykłady:
for(int i=1;i<11;i++)
//dziesięć cykli dla i=1,2,3,4,5,6,7,8,9,10
cout<<”Liczby rosnąco: ”<<i<<endl;
a teraz w odwrotnej kolejności:
for(int i=10;i>0;i--)
//dziesięć cykli dla i=10,9,8,7,6,5,4,3,2,1
cout<<”Liczby malejąco: ”<<i<<endl;
Jaka jest różnica ?
1
2
for
(int i=1;
i<11
;
i++
)
for
(int j=1;
j<11
;
j++
)
instrukcja1;
for
(int i=1;
i<11
;
i++
)
for
(int j=i;
j<11
;
j++
)
instrukcja2;
//Pytanie: ile cykli ?
//Pytanie: ile cykli ?
//Odpowiedź
: 100
<-
//
//Odpowiedź:
55
<-
// ?dlaczego: 10+9+8+7+6+5+4+3+2+1 <-
3
4
for
(int i=1,j=100;
i<11
;
i++,j++
)
instrukcja3;
for
(int i=1,j=100;
j<101
;
i++,j++
)
instrukcja4;
//Pytanie: ile cykli ?
//Pytanie: ile cykli ?
//Odpowiedź:
10
<-
//
//Odpowiedź:
1
<-
//
5
6
for
(int i=1,j=1;
(i<11)&&(j<101)
;
i++,j++
)
instrukcja5;
for
(int i=1,j=1;
(i<11)||(j<101)
;
i++,j++
)
instrukcja6;
//Pytanie: ile cykli ?
//Pytanie: ile cykli ?
//Odpowiedź:
10
<-
//
//Odpowiedź:
100
<-
//
Program – demonstruje działanie
for
#include <iostream>
int main() {
const unsigned int SZEROKOSC=4;
using std::cout; using std::endl;
for
(int i=1;
i<11
;
i++
) {
if (i>1) cout<<endl;
for
(int j=1;
j<11
;
j++
) {
cout.width(SZEROKOSC);
cout<<i*j;
}
}
cout<<endl;
return 0;
}
Jaki będzie rezultat wykonania programu ?
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
36
I
J
i*j
Ekran
1
1
1
1
1
2
2
1 2
1
3
3
1 2 3
...
...
...
...
2
1
2
...
...
...
10
10
100
1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 ...
...
...
...
...
10 20 30 40 50 60 70 80 90 100
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
37
INSRUKCJE CYKLICZNE – ZAGADKA
To samo ?
for
(instrukcje_1;
warunek
;
iteracje
) {
instrukcja;
instrukcja;
...
if (warunek) continue;
...
instrukcja;
instrukcja;
...
if (warunek) break;
...
instrukcja;
instrukcja;
};
instrukcje_1;
while
(
warunek
) {
instrukcja;
instrukcja;
...
if (warunek) {
iteracja;
continue;}
...
instrukcja;
instrukcja;
...
if (warunek) break;
...
instrukcja;
instrukcja;
iteracje;
};
A może są różnice ?:
sprawdź działanie break lub continue
<-
Odpowiedź –
różnica jest w działaniu: continue
<-
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
38
4.
F
UNKCJE
1.
Można podzielić program na fragmenty - podprogramy, które realizują ściśle określone zadania.
Dlaczego stosować funkcje ?
•
Powtarzające się fragmenty lub realizujące ściśle określone operacje mogą być wyodrębnione
i zapisane w postaci funkcji
•
Bezpośrednia odpowiedniość pomiędzy algorytmem i kodem programu Można pisać i
uruchamiać fragmenty programu niezależnie – struktura modułów funkcjonalnych –
„skrzyneczki”
•
Można je wywoływać z dowolnego punktu programu
•
Skrócenie programu powoduje jego większą czytelność
Forma zapisu:
typ
fnNazwa
(
typ argument1, typ argument2
) {
def. zmiennych lokalnych
instrukcja;
...
return
(
wartość zwracana
);
...
}
Funkcja może zwracać wartość (lub nie) poprzez instrukcję:
return(wartość);
lub samo:
return;
do miejsca w którym funkcja została wywołana
np. wynik =
fnSumaKwadratow
(
n
);
Przykłady: (ang. void - znaczy nieokreślona)
void
fnFunkcja1
(
int m
)
//nie zwraca wartości – argument: int m
int
fnFunkcja2
(
void
)
//zwraca wartość int – nie posiada argumentów
void
fnFunkcja3
(
void
)
//nie zwraca wartości – nie posiada argumentów
Typ wartości zwracanej
Parametry wywołania ze
wskazaniem typów
zmiennych
Zmienne tworzone dla
potrzeb wewnątrz funkcji
Powrót z funkcji wraz z
wartością zwracaną
Operacje funkcji
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
39
F
UNKCJE
2.
Kolejność definicji funkcji:
Definicje i deklaracje globalne
Definicja funkcji 1
Definicja funkcji 2
...
Definicja funkcji głównej main
lub
Definicje i deklaracje globalne
Prototyp funkcji 1
Prototyp funkcji 2
...
Definicja funkcji głównej main
Definicja funkcji 1
Definicja funkcji 2
Program liczy sumę kwadratów liczb n zadana z klawiatury
#include <iostream>
int
fnSumaKwadratow
(
int k
); //definicja prototypu
int main(){
using std::cout; using std::cin; using std::endl;
int n, iWynik;
cout<<"Podaj wartosc zmiennej n: ";cin>>n;
iWynik =
fnSumaKwadratow(
n
)
; //wywolanie funkcji
cout << "Suma kwadratow dla n = "<<n<<" wynosi "<<iWynik<<endl;
return 0;
}
/////////////////////////////////////// FUNKCJE /////////////////////////////////////////////////
int
fnSumaKwadratow
(
int k
) { // czy może być k zamiast n ?
int i, iSuma = 0;
for (i=1; i<=
k
;i++)
// czy może być k zamiast n ?
iSuma+=i*i;
return
(iSuma);
}
Co się stanie gdy zadeklaruje się jako parametr wywołania funkcji zmienną
k
zamiast
n
?
}
podobnie jak w plikach nagłówkowych
(ang. headers) , np. plik.h
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
40
iWynik=fnSumaKwadratow(n);
int n, iWynik;
...
int main(){
}
Adres
powrotu
n
STOS
iWynik
...
...
Adres
wyjścia z main()
WYWOŁANIE FUNKCJI
RAM
zrz
ut
skok
n
...
...
int fnSumaKwadratow(n){
int i, iSuma = 0;
}
return (iSuma);
for (i=1; i<=n;i++) iSuma+=i*i;
Podczas wywołania funkcji następuje zapamiętanie na stosie (zrzut na stos) adresu powrotu
(segment i offset) do miejsca następnej instrukcji następującej po wywoływanej funkcji.
Na stos zostaje odłożona wartość argumentu wywołania funkcji, która będzie funkcjonować jak
zmienna lokalna w obszarze funkcji.
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
41
Adres
powrotu
n
STOS
iWynik
...
Adres
wyjścia z main()
DZIAŁANIE FUNKCJI
ob
lic
ze
ni
a
i
iSuma
...
n
...
iWynik=fnSumaKwadratow(n);
int n, iWynik;
...
int main(){
}
RAM
...
int fnSumaKwadratow(n){
int i, iSuma = 0;
}
return (iSuma);
for (i=1; i<=n;i++) iSuma+=i*i;
Zostają powołane do życia nowe zmienne lokalne: i oraz iSuma w postaci komórek pamięci na
stosie. Na tych zmiennych odbywa się przetwarzanie, czyli obliczenia sumy kwadratów liczb od
1 do n. Zmienna i jest liczbą podnoszoną do kwadratu w pętli for.
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
42
Adres
powrotu
n
STOS
iWynik
...
Adres
wyjścia z main()
POWRÓT Z FUNKCJI
i
iSuma
...
n
...
przepisuje
wynik
pow
rót
iWynik
Adres
wyjścia z main()
...
n
...
STOS
zwalnia
stos
...
iWynik=fnSumaKwadratow(n);
int n, iWynik;
...
int main(){
}
RAM
...
int fnSumaKwadratow(n){
int i, iSuma = 0;
}
return (iSuma);
for (i=1; i<=n;i++) iSuma+=i*i;
Powrót z funkcji następuje po napotkaniu instrukcji return lub po dotarciu do klamry zamykającej
‘}’. W tym przypadku funkcja zwraca wartość typu int i przepisuje ją do zmiennej lokalnej
funkcji main o nazwie iWynik. Musi nastąpić zatem przepisanie wartości zmiennej iSuma pod
adres zmiennej iWynik na stosie. Wszystkie zmienne lokalne w funkcji fnSumaKwadratow, czyli
iSuma, i oraz n przestają być potrzebne i zostają usunięte (zciągnięte ze stosu). Wtedy na stosie
odkryty zostanie adres powrotu, który jest wczytywany do rejestru instrukcji procesora. To
spowoduje przekazanie sterowania pod ten adres, czyli wykonana będzie instrukcja następująca
po wywołaniu funkcji. W ten sposób poza zmienioną wartością iWynik zmiennej lokalnej funkcji
main nie pozostanie żaden ślad działalności funkcji fnSumaKwadratów. Obszar stosu używany
przez tą funkcję zostanie zwolniony i może być wykorzystany do innych celów.
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
43
F
UNKCJE
3.
ZASIĘG DOSTĘPNOŚCI ZMIENNYCH:
Zmienne:
•
Globalne
– mają zasięg ogólny – dostępne wszędzie w programie, posiadają rezerwację w
pamięci operacyjnej i swój stały adres
•
Lokalne
– powoływane do życia wewnątrz funkcji, znikają po zakończeniu funkcji
(tworzone tymczasowo na stosie w pamięci operacyjnej). Przesłaniają inne zmienne o tych
samych nazwach (patrz przykład)
•
Statyczne
– definiowane wewnątrz funkcji – zachowują wartość do następnego wywołania
tej samej funkcji
PRZESŁANIANIE ZMIENNYCH GLOBALNYCH:
Nazewnictwo zmiennych lokalnych nie jest dowolne, ponieważ zmienne globalne o tych
samych nazwach zostają
przesłonięte
przez nowe definicje, ale ich czas życia jest ograniczony
do wnętrza funkcji. Po zakończeniu funkcji zmienne lokalne znikają bezpowrotnie. Bierze się to
stąd, że są one tworzone tymczasowe na strukturze stosu w pamięci operacyjnej komputera.
Przykładowy program:
#include <iostream>
int
k
=50,
j
=10;
//zmienne globalne z nadaną wartością początkową
int
fnFunkcja
(
void
) {
return (
k
+100);
//obszar zasięgu zmiennej globalnej
k
=150
}
int main() {
using std::cout; using std::endl;
int
k
=3;
//zmienna lokalna
k
w funkcji main
cout<< ”Dla lokalnego k: ”<<
k
+100+
j
<<” Dla globalnego k: „<<
fnFunkcja()
+
j
<<endl;
}
Wynik : ? >
113, 160
<
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
44
Przykładowy program:
#include <iostream>
int
i
=10,
j
=200,
k
=300;
//zmienne globalne
void
fnFun
(
void
) {
using std::cout; using std::endl;
int
i
=4;
//przesłonięcie nazwy globalnej
static
int
j
=0;
//przesłonięcie nazwy globalnej – zmienna
static
i
++;
j
++;
k
++;
cout<<"W fnFun(): I= "<<
i
<<" J= "<<
j
<<" K= "<<
k
<<endl;
}
int main() {
int
i
,
k
=0;
for (
i
=0;
i
<3;
i
++)
fnFun
();
cout<<"W main(): I= "<<
i
<<" J= "<<
j
<<" K= "<<
k
<<endl;
return 0;
}
Wynik:
W fnFun(): I= 5 J= 1 K= 301
W fnFun(): I= 5 J= 2 K= 302
W fnFun(): I= 5 J= 3 K= 303
W main(): I= 3 J= 200 K= 0
int main() {
int i, k=0;
for (i=0; i<3; i++) fnFun();
cout<<"W main(): I= "<<i
<<" J= "<<j<<" K= "<<k<<endl;
return 0;
}
STOS
...
i
Adres
wyjścia z main()
RAM
powrót
k
...
i=10
j=200
k=300
zmienne
globalne
void fnFun(void) {
using std::cout; using std::endl;
int i =4;
static int j=0;
i++; j++;k++;
cout<<"W fnFun(): I= "<<i
<<" J= "<<j<<" K= "<<k<<endl;
}
Adres
powrotu z fnFun()
i
STOS
...
i
Adres
wyjścia z main()
k
...
j
j
i
i
k
skok
powrót
skok
zmienne
lokalne
zmienne
lokalne
static j=10
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
45
A
RGUMENTY
F
UNKCJI
1.
To co było do tej pory w przykładach to
przekazywanie argumentów przez wartość
Funkcja nie może wpływać na wartości zmiennych przekazywanych do funkcji jako
argumenty, ponieważ w trakcie wywołanie funkcji powoływane są do życia zmienna w sensie
lokalnym o typie i nazwie argumentu wywołania oraz z wartością początkową wywołania.
Po opuszczeniu funkcji nie pozostanie ślad po argumentach i operacjach na nich wykonanych.
Funkcja może zwrócić wartość w postaci jawnej (instrukcja return) do użycia w dalszej części
programu.
Przykładowy program:
#include <iostream>
void
fnZwieksz
(
int a, int b
) {
using std::cout; using std::endl;
a+=100;
b+=200;
cout << " a: "<<a<<" b: "<<b<<endl;
}
int main(){
using std::cout; using std::endl;
int
x
=10,
y
=10;
fnZwieksz
(
x,y
); //nie spowoduje zmiany wartości
x
i
y
w funkcji main()
cout << ” x: ”<<
x
<<” y: ”<<
y
;
return 0;
}
UWAGA
: Można to obejść używając wewnątrz funkcji zmiennych globalnych, ale nie podając
ich jako argument wywołania, lecz wykonując jedynie operacje na nich.
#include <iostream>
int
x
=10,
y
=10;
//zmienne globalne
void
fnZwieksz
(
int a, int b
) {
using std::cout; using std::endl;
x=a+100;
y=b+200;
cout << " a: "<<a<<" b: "<<b<<endl;
}
int main(){
using std::cout; using std::endl;
fnZwieksz
(
x,y
); //nie spowoduje zmiany wartości
x
i
y
w funkcji main()
cout << ” x: ”<<
x
<<” y: ”<<
y
;
return 0;
}
ODPOWIEDNIE METODY:
referencja
i
wskaźniki
– omawiane w dalszej części kursu
Wynik:
a: 110 b: 210
x: 10 y: 10
Wynik:
a: 10 b: 10
x: 110 y: 210
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
46
A
RGUMENTY
F
UNKCJI
2.
Argumenty funkcji
main()
są skojarzone z
parametrami
wywołania
programu
:
nazywanymi
linią komend
lub
wierszem polecenia
int
main
() lub int
main
(
void
) – bez parametrów,
int
main
(
int argc, char *argv[]
) – dwa parametry:
•
argc
– liczba argumentów wywołania programu (>=1)
•
argv
– tablica wskaźników do tekstów argumentów
//Program wypisze liczbę oraz zawartość wszystkich argumentów funkcji main()
#include <iostream>
int
main
(
int argc, char *argv[]
) {
using std::cout; using std::endl;
cout<<”Program ”<<
argv[0]
<<” ma ”<<
argc
-1<<” argumentów.”<<endl;
for(int i=1;i<
argc
;i++)
cout<<” Argument\t”<<i<<”\tto\t”<<
argv[i]
<<endl;
return 0;
}
Wywołanie programu: funkcje_5.exe argument1 argument2 argument3
Wynik:
Program ścieżka\ funkcje_5.exe ma 3 argumentów.
Argument 1 to argument1
Argument 2 to argument2
Argument 3 to argument3
Aby kontynuować, naciśnij dowolny klawisz . . .
UWAGA: Trzeba pamiętać, żeby dokonać ustawień dla linii komend w kompilatorze, np. w Dev-
C++ w okienku Compilation przycisk Parameters.
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
47
D
OMYŚLNE
A
RGUMENTY
F
UNKCJI
Podczas definicji funkcji można przypisać domyślne (początkowe) wartości argumentów funkcji
void
fnSuma
(
int a=10, int b=20, int c=30, int d=40
){
....
}
Wywołanie tej funkcji może się odbyć z niepełną liczbą parametrów:
fnSuma
(
1,2
); //a=1, b=2 i domyślnie c=30 i d=40
= równoważne =
fnSuma
(
1,2,30,40
);
UWAGA:
Możemy pominąć tylko
wszystkie
argumenty począwszy od tego,
którego pominiemy jako pierwszy.
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
48
5.
P
RZECIĄśANIE
F
UNKCJI
Nadajemy te same nazwy funkcjom, które realizują analogiczne operacje,
ale
różniące się typem lub liczbą parametrów
- dzięki tym różnicom kompilator
rozpoznaje, którą z funkcji ma być użyta w zależności od typu wywołania.
Przykład:
//prototypy funkcji
int
polePowierzchni
(
int bokX,int bokY
);
double
polePowierzchni
(
double bokX,double bokY
);
double
polePowierzchni
(
double promien
);
int main(){
int a=5,b=4;
double c=5.8,d=3.2, r=2.11;
cout<<"Pole powierzchni z argumentami całkowitymi "<<
polePowierzchni
(
a,b
)<<'\n';
cout<<"Pole powierzchni z argumentami rzeczywistymi "<<
polePowierzchni
(
c,d
)<<endl;
cout<<"Pole powierzchni koła z argumentem rzeczywistym "<<
polePowierzchni
(
r
)<<endl;
return 0;
}
//definicja funkcji
int
polePowierzchni
(
int bokX,int bokY
){
return (bokX*bokY);
}
double
polePowierzchni
(
double bokX,double bokY
){
return (bokX*bokY);
}
double
polePowierzchni
(
double promien
){ //promień kola
return (promien*promien*3.1415);
}
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
49
T
ABLICE
1.
Struktura danych zawierająca uporządkowany zbiór obiektów tego samego typu i
odpowiada matematycznemu pojęciu wektora, macierzy.
Mogą być tablice jednowymiarowe (wektory) lub wielowymiarowe (macierze).
Tablica jednowymiarowa o rozmiarze 16 elementów po zainicjowaniu wartościami
początkowymi:
int iMoto[
16
] ={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
Indeks
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15]
Wartość
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
RAM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[0]
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]
[11]
[12]
[13]
[14]
[15]
indeks
element
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
50
T
ABLICE
2.
Tablica dwuwymiarowa: iMoto o rozmiarze
4x4
po zainicjowaniu wartościami początkowymi:
int iMoto[
4
] [
4
] = {
{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
{13,14,15,16}
};
[0][0]
[0][1]
[0][2]
[0][3]
1
2
3
4
[1][0]
[1][1]
[1][2]
[1][3]
5
6
7
8
[2][0]
[2][1]
[2][2]
[2][3]
9
10
11
12
[3][0]
[3][1]
[3][2]
[3][3]
13
14
15
16
RAM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
indeks
element
[0]
[0]
[0]
[0]
[1]
[1]
[1]
[1]
[2]
[2]
[2]
[2]
[3]
[3]
[3]
[3]
[0]
[1]
[2]
[3]
[0]
[1]
[2]
[3]
[0]
[1]
[2]
[3]
[0]
[0]
[0]
[0]
Indeks
Wartość
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
51
T
ABLICE
3.
TABLICE SĄ INDEKSOWANE OD ZERA [0] !!!
Jeśli wpisujemy wszystkie wartości początkowe do tablicy to możemy nie podawać liczby
elementów - kompilator poradzi sobie z policzeniem elementów:
int iTab[] = {1,2,3,4,5,6};
≡
int iTab[6]= {1,2,3,4,5,6};
Przykład: Wpisujemy wartość 2 do wszystkich elementów pewnej tablicy iTab:
Czy jest różnica ?
int iTab[
10
][
5
];
for (int i=0;i<
10
;i++)
for(int j=0;j<
5
;j++)
iTab[i][j] = 2;
int iTab[
50
];
for (int i=0;i<
50
;i++)
iTab[i] = 2;
RAM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int iMoto[4][4] =
{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
int iMoto[4][4] = { {1,2,3,4},
{5,6,7,8},
{9,10,11,12},
{13,14,15,16} };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(tożsame)
[0]
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]
[11]
[12]
[13]
[14]
[15]
indeks
[0]
[0]
[0]
[0]
[1]
[1]
[1]
[1]
[2]
[2]
[2]
[2]
[3]
[3]
[3]
[3]
[0]
[1]
[2]
[3]
[0]
[1]
[2]
[3]
[0]
[1]
[2]
[3]
[0]
[0]
[0]
[0]
indeks
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
52
Przykładowy program
//Program wynajduje element min i max w tablicy jednowymiarowej (wektorze)
#include <iostream>
const int MAXTAB=100;
int main(){
using std::cout; using std::cin; using std::endl;
int n,i,iMin,iMinIndx,iMax,iMaxIndx; //n - aktualny rozmiar wektora
int
iTab
[MAXTAB];
cout<<"Podaj liczbe elementow: (w zakresie od 1 do "<<MAXTAB; cin>>n;
for(i=0;i<n;i++){
//wprowadzenie danych do tablicy
cout<<"Podaj "<<i<<" element: "; cin>>
iTab
[i];
}
iMin=iMax=
iTab
[0];
iMinIndx=iMaxIndx=0;
//inicjalizacja parametrów do poszukiwania
for(i=1; i<n; i++){
//wyszukaj min i max
if(
iTab
[i]<iMin) {
iMin=
iTab
[i]; iMinIndx=i;
}
if(
iTab
[i]>iMax) {
iMax=
iTab
[i]; iMaxIndx=i;
}
}
cout<<"Minimum to: "<<iMin<<" na pozycji "<<iMinIndx<<endl;
cout<<"Maximum to: "<<iMax<<" na pozycji "<<iMaxIndx<<endl;
cout<<"Index\t"<<"Wartosc"<<endl;
for(i=0;i<n;i++) { //wyswietl minima i maxima
cout<<i<<"\t"<<
iTab
[i];
if(
iTab
[i]==iMax) cout<<"\tMAXIMUM";
if(
iTab
[i]==iMin) cout<<"\tMINIMUM";
cout<<endl;
}
return 0;
}
Rezultat:
Podaj liczbe elementow: 7
Podaj 0 element: 2
Podaj 1 element: 5
Podaj 2 element: 6
Podaj 3 element: 4
Podaj 4 element: 2
Podaj 5 element: 6
Podaj 6 element: 3
Minimum to: 2 na pozycji 0
Maximum to: 6 na pozycji 2
Index Wartosc
0
2
MINIMUM
1
5
2
6
MAXIMUM
3
4
4
2
MINIMUM
5
6
MAXIMUM
6
3
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
53
T
ABLICE I
W
SKAŹNIKI
1.
WPROWADZENIE do WSKAŹNIKÓW
Informacja przechowywana jest w pamięci operacyjnej w sposób ciągły
podobnie jak w tablicy jednowymiarowej, z tą różnicą, że zamiast
indeksów występują adresy komórek pamięci. Korzystając z takiej
analogii można operować nazwą tablicy jednowymiarowej podobnie jak
zmienną wskaźnikową przechowującą adres początku tablicy.
Nazwa tablicy
podaje adres -
jest wskazaniem
na pierwszy element
tablicy – o indeksie
0
.
RAM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int iMoto[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
[0]
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]
[11]
[12]
[13]
[14]
[15]
indeks
tablica iMoto
=> wskaźnik iMoto
wskaźnik
iMoto + 3
wskaźnik
iMoto + 15
element iMoto[0]
element *(iMoto)
element iMoto[3]
element *(iMoto+3)
element iMoto[15]
element *(iMoto+15)
Wartość
Adres
iTab
[0] to jest to samo co: *
iTab
(wartość spod adresu
iTab
)
iTab
[i] to jest to samo co: *(
iTab
+i) (zwiększamy adres o i)
iTab
nie powinno
być zmieniany pod żadnym pozorem
Błąd:
iTab
+=i Błąd
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
54
T
ABLICE I
W
SKAŹNIKI
2.
Informacja przechowywana jest w pamięci operacyjnej w sposób ciągły
podobnie jak w tablicy jednowymiarowej, z tą różnicą, że zamiast
indeksów występują adresy komórek pamięci.
Tablice
wielowymiarowe
traktowane są specjalnie przez kompilatory – inaczej niż zwykłe
wskaźniki.
RAM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int iMoto[4][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};
indeks
[0]
[0]
[0]
[0]
[1]
[1]
[1]
[1]
[2]
[2]
[2]
[2]
[3]
[3]
[3]
[3]
[0]
[1]
[2]
[3]
[0]
[1]
[2]
[3]
[0]
[1]
[2]
[3]
[0]
[1]
[2]
[3]
iMoto[0][0]
**(iMoto)
*
wi
iMoto[0][3]
*(*iMoto+3)
*(
wi
+3)
tablica iMoto
=> wskaźnik
wi
wskaźnik
wi
+ 3
wskaźnik
wi
+ 3*4 +3
iMoto[2][2]
*(*(iMoto+2)+2)
*(
wi
+ 2*4 +2)
int *
wi
= iMoto[0][0];
wskaźnik
wi
+ 2*4 +2
Wartość
Adres
iMoto[3][3]
*(*(iMoto+3)+3)
*(
wi
+ 3*4 +3)
Istotna różnica polega na tym, że nazwa tablicy wielowymiarowej nie
jest traktowane jak zwykła zmienne wskaźnikowa. Dla przykładu
odwołanie do tablicy dwuwymiarowej iMoto[4][4] w następujące
sposoby: *(iMoto+2) oraz (iMoto +2) daje w efekcie dostęp do adresu
trzeciego wiersza. Gdyby iMoto była zwykłą zmienną wskaźnikową to
operator ‘*’ umożliwił by dostęp do wartości pod adresem iMoto. Dzieje
się tak jedynie w tablicy jednowymiarowej. Dla tablic
wielowymiarowych można używać podwójnego operatora ‘*’ i kolejno
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
55
dokonywać przesunięcia na szukany wiersz i szukaną kolumnę. Inną
metodą jest użycie dodatkowo zdefiniowanej zmiennej wskaźnikowej
zainicjowanej adresem pierwszego elementu tablicy.
Przykładowy program
include <iostream>
using namespace std;
int main()
{
int
tab
[10] = {1,2,3,4,5,6,7,8,9,10};
int
iMoto
[4][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};
//double dd=10.10;
int *wa;
cout << "Wskaznik wa ma wartosc poczatkowa = "<<wa<<endl;
int a=10,b=20;
cout << "Komorki pamieci zmiennej a maja adres = " << &a<<endl;
wa=&a;
cout << "Wskaznik wa ma wartosc = "<<wa<<endl;
cout << "Komorki pamieci wskazywane przez wa = " << *wa<<endl;
wa=&b;
cout << "Komorki pamieci wskazywane przez wa = " << *wa<<endl;
wa=tab+4;
cout << "Komorki pamieci wskazywane przez wa = " << *wa<<endl;
//wa=ⅆ
a=*wa;
cout<<"Element tablicy
tab
[4] = "<<
tab
[4]
<<" to jest to samo co *(
tab
+4) = "<<*(
tab
+4)<<endl;
// Tablica dwuwymiarowa
cout<<"Element iMoto to "<<
iMoto
<<endl;
cout<<"Element tablicy
iMoto
[0][0] = "<<
iMoto
[0][0]
<<" to jest to samo co **
iMoto
= "<<**
iMoto
<<endl;
cout<<"Element tablicy
iMoto
[0][3] = "<<
iMoto
[0][3]
<<" to jest to samo co *(*
iMoto
+3) = "<<*(*
iMoto
+3)<<endl;
cout<<"Element (
iMoto
+2) to "<<(
iMoto
+2)<<endl;
cout<<"Element *(
iMoto
+2) to "<<*(
iMoto
+2)<<endl;
cout<<"Element (*(
iMoto
+2)+2) to "<<(*(
iMoto
+2)+2)<<endl;
cout<<"Element *(*(
iMoto
+2)+2) to "<<*(*(
iMoto
+2)+2)<<endl;
cout<<"Element tablicy
iMoto
[2][2] = "<<
iMoto
[2][2]
<<" to jest to samo co *(*(
iMoto
+2)+2) = "<<*(*(
iMoto
+2)+2)<<endl;
cout<<"Element tablicy
iMoto
[3][3] = "<<
iMoto
[3][3]
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
56
<<" to jest to samo co *(*(
iMoto
+3)+3) = "<<*(*(
iMoto
+3)+3)<<endl;
// Tablica dwuwymiarowa z dodatkowym wskaźnikiem
int *wi=&iMoto[0][0];
cout<<"Wskaźnik wi to "<<wi<<endl;
cout<<"Wskaźnik (wi+2) to "<<(wi+2)<<endl;
cout<<"Wskaźnik *(wi+2) to "<<*(wi+2)<<endl;
cout<<"Wskaźnik *(wi+2*4) to "<<*(wi+2*4)<<endl;
cout<<"Element tablicy
iMoto
[2][2] = "<<
iMoto
[2][2]
<<" to jest to samo co *(wi+2*4+2) = "<<*(wi+2*4+2)<<endl;
// Wskaźnik na wskaźnik
int **ww=&wa;
cout << "Komorki pamieci wskazywane przez *ww = " << *ww<<endl;
cout << "Komorki pamieci wskazywane przez **ww = " << **ww<<endl;
system("PAUSE");
return 0;
}
Rezultat:
Wskaznik wa ma wartosc poczatkowa = 0x77c4fc80
Komorki pamieci zmiennej a maja adres = 0x22fef8
Wskaznik wa ma wartosc = 0x22fef8
Komorki pamieci wskazywane przez wa = 10
Komorki pamieci wskazywane przez wa = 20
Komorki pamieci wskazywane przez wa = 5
Element tablicy tab[4] = 5 to jest to samo co *(tab+4) = 5
Element iMoto to 0x22ff00
Element tablicy iMoto[0][0] = 1 to jest to samo co **iMoto = 1
Element tablicy iMoto[0][3] = 4 to jest to samo co *(*iMoto+3) = 4
Element (iMoto+2) to 0x22ff20
Element *(iMoto+2) to 0x22ff20
Element (*(iMoto+2)+2) to 0x22ff28
Element *(*(iMoto+2)+2) to 11
Element tablicy iMoto[2][2] = 11 to jest to samo co *(*(iMoto+2)+2) = 11
Element tablicy iMoto[3][3] = 16 to jest to samo co *(*(iMoto+3)+3) = 16
Wskačnik wi to 0x22ff00
Wskačnik (wi+2) to 0x22ff08
Wskačnik *(wi+2) to 3
Wskačnik *(wi+2*4) to 9
Element tablicy iMoto[2][2] = 11 to jest to samo co *(wi+2*4+2) = 11
Komorki pamieci wskazywane przez *ww = 0x22ff50
Komorki pamieci wskazywane przez **ww = 5
Aby kontynuować, naciśnij dowolny klawisz . . .
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
57
W
SKAŹNIKI
1.
Zmienne wskaźnikowe
– wartościami są adresy wskazujące na miejsce w
pamięci operacyjnej pewnego obiektu (segment + offset).
int *wa
; //deklaracja zmiennej wskaźnikowej wa na obiekt int
int *wa
, a=10, b=20;
wa=&a
; // wa zaczyna wskazywać na zmienna ‘a’
Dostęp do wartości zmiennej ‘a’:
cout<<a<<”,”;
lub
cout<<*wa;
//przez
operator
wyłuskania
//(nie mylić z wyłuzdaniem)
Efekt: 10,10
Można teraz
*wskaźnik
traktować jak zmienną na którą obecnie wskazuje (w przykładzie na a),
np.
*wa = 100
; zapisze wartość 100 do zmiennej ‘a’
b=*wa+1;
c=(*wa)++;
//operatory działają od prawej do lewej to trzeba nawiasy
c=++*wa;
//to samo
ale nie:
c=*wa++;
//spowoduje zwiększenie adresu wskazania wa o jeden i dopiero przypisanie do c
//zwykle NIEUśYWANE
Adres a składa się z adresu Segmentu i Offset fizycznego adresu RAM w komputerze.
Wskaźnik zatem zajmuje 2 bajty adresu segmentu i 2 bajty adresu offsetu = 4 bajty.
wa
a
b
int *wa
?
10
20
int a=10, b=20;
wa
wa=&a
wskazanie na a
(zaadresowanie a)
adres a
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
58
W
SKAŹNIKI
2.
PRZEKAZYWANIE PARAMETRÓW DO FUNKCJI:
Poprzednio poznaliśmy przekazywanie parametrów funkcji przez wartość. Były na to
przykłady, które pokazywały, że zmienne nawet o tej samej nazwie nie mają ze sobą nic
wspólnego – jedynie ich wartość przekazywana jest do wnętrza funkcji.
Przykład:
void
fnZwieksz
(
int a, int b
) {
a +=100;
b+=200;
}
Wywołanie:
int x = 10, y = 10;
fnZwieksz
(
x,y
);
//nie zmienia wartości x i y
Efekt: //bez zmian: x = 10, y=10
Jak to zmienić ?
1. Nie zmieniać –
używać zmiennych globalnych
,
2. Wykorzystać
wskaźniki
(C, C++) – przekazywanie przez wskaźniki
3. Wykorzystać
referencję
(C++) – przekazywanie adresu
Ad.2
Wskaźniki
– przekazanie przez wskaźniki
void
fnZwiekszInny
(
int *a, int *b
) {
*a+=100;
*b+=200;
}
Wywołanie:
int x=10, y =10;
fnZwiekszInny
(
&x,&y
); //zmienia wartość x i y
Efekt: x=110, y=210
Ad.3
Referencja
- przekazywanie adresu
void
fnZwiekszRef
(
int &a, int &b
){ // Zmienia się jedynie deklaracja funkcji
a+=100;
b+=200;
}
Wywołanie:
int x = 10, y = 10;
fnZwiekszRef
(
x,y
); // zmienia wartość x i y
Efekt: x=110, y=210
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
59
W
SKAŹNIKI
3.
Przykład - zamiana wartości argumentów:
void
fnZmien
(
int &a, int &b
){
int pom=b;
b=a;
a=pom;
}
Wywołanie:
int x=10, y=30;
fnZmien
(
x,y
);
Efekt: x=30, y=10
TABLICE i FUNKCJE
Nazwa tablicy jest wskaźnikiem na zerowy element tablicy
, zatem przekazanie
tablicy poprzez nazwę jest jednocześnie
przekazywaniem przez adres
.
UWAGA:
Rozmiar tablic może być różny, zatem należy pamiętać o przekazaniu do funkcji
rozmiaru tablicy przez wartość poprzez inny argument wywołania.
Przykłady:
//Program liczy sumę wszystkich elementów tablicy
int
fnSumaWekt(
int t[], int m
){ //m – rozmiar tablicy
int sum=0;
for(int i=0;i<m;i++)
sum+=t[i];
return sum;
}
Wywołanie:
int x[20],iWynik;
iWynik=
fnSumaWekt
(
x,20
);
lub: dla określonego wiersza tablicy dwuwymiarowej
int y[20][20],iWynik;
iWynik=
fnSumaWekt
(
y[1],20
); //obliczy sumę drugiego wiersza y[1] tablicy y
WYKŁADY cz.2 v.5 (2011) Podstawy programowania 1
(dr.inż Marcin Głowacki)
60
Przykład:
wyświetla wynik funkcji przeciążonych suma wektora i tablic dwuwymiarowych
#include <iostream>
#define MMAX 3
#define NMAX 3
using namespace std;
int
fnSuma
(
int iTab[][NMAX], int m, int n
){
int iSuma=0;
for (int i=0;i<m;i++){
cout<<endl;
for(int j=0;j<n;j++){
iSuma+=iTab[i][j];
cout<<'\t'<<iTab[i][j];
}
}
cout<<"\nSuma tablicy naturalnej: "<<iSuma<<endl;
return
iSuma;
}
double
fnSuma
(
double dTab[][NMAX], int m, int n
){
double dSuma=0;
for (int i=0;i<m;i++){
cout<<endl;
for(int j=0;j<n;j++){
dSuma+=dTab[i][j];
cout<<'\t'<<dTab[i][j];
}
}
cout<<"\nSuma tablicy zmiennoprzecinkowej: "<<dSuma<<endl;
return
dSuma;
}
int
fnSuma
(
int iTab[], int m
){
int iSuma=0;
for (int i=0;i<m;i++){
iSuma+=iTab[i];
cout<<'\t'<<iTab[i];
}
cout<<"\nSuma wektora naturalnego: "<<iSuma<<endl;
return
iSuma;
}
int
main
(){
int
iTablica
[MMAX][NMAX] = {1,2,3,4,5,6,7,8,9};
double
dTablica
[MMAX][NMAX] =
{ {1.1,2.2,3.3},{4.4,5.5,6.6},{7.7,8.8,9.9}};
fnSuma
(
iTablica,MMAX,NMAX
);
fnSuma
(
dTablica,MMAX,NMAX
);
fnSuma
(
iTablica[1],NMAX
);
return
0; }
Efekt:
1 2 3
4 5 6
7 8 9
Suma tablicy naturalnej: 45
1.1 2.2 3.3
4.4 5.5 6.6
7.7 8.8 9.9
Suma tablicy
zmiennoprzecinkowej: 49.5
4 5 6
Suma wektora naturalnego: 15