Podstawy programowania w języku C++

Podobne dokumenty
Podstawy programowania w języku C++

Podstawy programowania w języku C++

Języki programowania obiektowego Nieobiektowe elementy języka C++

Wprowadzenie do programowania w języku C

Podstawy programowania w języku C++

Podstawy programowania

Języki programowania. Przetwarzanie tablic znaków. Część druga. Autorzy Tomasz Xięski Roman Simiński

Języki programowania obiektowego Nieobiektowe elementy języka C++

Programowanie w języku C++

Podstawy programowania w języku C++

Podstawy programowania w języku C++

Tablice deklaracja, reprezentacja wewnętrzna

Podstawy programowania w języku C++

Wprowadzenie do programowania w języku C

Lab 9 Podstawy Programowania

Wskaźniki w C. Anna Gogolińska

Podstawy programowania w języku C++

Wskaźniki. Przemysław Gawroński D-10, p marca Wykład 2. (Wykład 2) Wskaźniki 8 marca / 17

Języki programowania. Przetwarzanie plików amorficznych Konwencja języka C. Część siódma. Autorzy Tomasz Xięski Roman Simiński

Podstawy programowania w języku C i C++

Temat: Dynamiczne przydzielanie i zwalnianie pamięci. Struktura listy operacje wstawiania, wyszukiwania oraz usuwania danych.

KURS C/C++ WYKŁAD 6. Wskaźniki

DYNAMICZNE PRZYDZIELANIE PAMIECI

Wskaźniki i dynamiczna alokacja pamięci. Spotkanie 4. Wskaźniki. Dynamiczna alokacja pamięci. Przykłady

Podstawy programowania w języku C++

ZASADY PROGRAMOWANIA KOMPUTERÓW

Programowanie w języku C++

Co to jest sterta? Sterta (ang. heap) to obszar pamięci udostępniany przez system operacyjny wszystkim działającym programom (procesom).

Wskaźniki. nie są konieczne, ale dają językowi siłę i elastyczność są języki w których nie używa się wskaźników typ wskaźnikowy typ pochodny:

Wskaźniki. Informatyka

// Liczy srednie w wierszach i kolumnach tablicy "dwuwymiarowej" // Elementy tablicy są generowane losowo #include <stdio.h> #include <stdlib.

Podstawy programowania w języku C++

Wskaźniki. Programowanie Proceduralne 1

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 6

Zmienne i struktury dynamiczne

Wykład 1: Wskaźniki i zmienne dynamiczne

Powyższe wyrażenie alokuje 200 lub 400 w zależności od rozmiaru int w danym systemie. Wskaźnik wskazuje na adres pierwszego bajtu pamięci.

Podstawy programowania

Podstawy programowania komputerów

Programowanie w C++ Wykład 5. Katarzyna Grzelak. 16 kwietnia K.Grzelak (Wykład 1) Programowanie w C++ 1 / 27

Stałe, tablice dynamiczne i wielowymiarowe

Wprowadzenie do programowania w języku C

Struktury czyli rekordy w C/C++

Wprowadzenie do programowanie obiektowego w języku C++

Tablice, funkcje - wprowadzenie

Języki programowania. Tablice struktur, pliki struktur. Część ósma. Autorzy Tomasz Xięski Roman Simiński

Języki i metodyka programowania. Wskaźniki i tablice.

Podstawy programowania w języku C i C++

Języki programowania obiektowego Nieobiektowe elementy języka C++

Laboratorium nr 9. Temat: Wskaźniki, referencje, dynamiczny przydział pamięci, tablice dynamiczne. Zakres laboratorium:

Uzupełnienie dot. przekazywania argumentów

Podstawy programowania 1

Tablice, funkcje, wskaźniki - wprowadzenie

Podstawy programowania. Wykład 6 Wskaźniki. Krzysztof Banaś Podstawy programowania 1

Wstęp do programowania INP001213Wcl rok akademicki 2018/19 semestr zimowy. Wykład 4. Karol Tarnowski A-1 p.

Wskaźnik może wskazywać na jakąś zmienną, strukturę, tablicę a nawet funkcję. Oto podstawowe operatory niezbędne do operowania wskaźnikami:

WYKŁAD 10. Zmienne o złożonej budowie Statyczne i dynamiczne struktury danych: lista, kolejka, stos, drzewo. Programy: c5_1.c, c5_2, c5_3, c5_4, c5_5

tablica: dane_liczbowe

int tab[5]; tab[1]; ciągły obszar pamięci, w którym umieszczone są elementy tego samego typu macierz [ ] - dwuargumentowy operator indeksowania

Struktury, unie, formatowanie, wskaźniki

Techniki Programowania wskaźniki

Wykład II. Programowanie II - semestr II Kierunek Informatyka. dr inż. Janusz Słupik. Wydział Matematyki Stosowanej Politechniki Śląskiej

PARADYGMATY PROGRAMOWANIA Wykład 4

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

METODY I JĘZYKI PROGRAMOWANIA PROGRAMOWANIE STRUKTURALNE. Wykład 02

Algorytmy i złożoności. Wykład 3. Listy jednokierunkowe

Tablice i struktury. czyli złożone typy danych. Programowanie Proceduralne 1

1 Wskaźniki i zmienne dynamiczne, instrukcja przed zajęciami

Programowanie komputerowe. Zajęcia 4

Programowanie w C++ Wykład 4. Katarzyna Grzelak. 19 marca K.Grzelak (Wykład 1) Programowanie w C++ 1 / 37

Wstęp do wskaźników w języku ANSI C

Język C zajęcia nr 11. Funkcje

Podstawy programowania skrót z wykładów:

Konwersje napis <-> liczba Struktury, unie Scanf / printf Wskaźniki

wykład III uzupełnienie notatek: dr Jerzy Białkowski Programowanie C/C++ Język C - zarządzanie pamięcią, struktury,

Ćwiczenie nr 6. Poprawne deklaracje takich zmiennych tekstowych mogą wyglądać tak:

Podstawy informatyki. Elektrotechnika I rok. Język C++ Operacje na danych - wskaźniki Instrukcja do ćwiczenia

Typy złożone. Struktury, pola bitowe i unie. Programowanie Proceduralne 1

Tablice. Monika Wrzosek (IM UG) Podstawy Programowania 96 / 119

Programowanie i struktury danych

ŁAŃCUCHY W JĘZYKU C/C++

1 Przetwarzanie tablic znakowych

TABLICE W JĘZYKU C/C++ typ_elementu nazwa_tablicy [wymiar_1][wymiar_2]... [wymiar_n] ;

DANE TEKSTOWE W JĘZYKU C/C++ - TABLICE ZNAKOWE

wykład II uzupełnienie notatek: dr Jerzy Białkowski Programowanie C/C++ Język C - funkcje, tablice i wskaźniki wykład II dr Jarosław Mederski Spis

Wykład nr 3. Temat: Wskaźniki i referencje. Edward Morgan Forster

Podstawy programowania. Wykład 6 Złożone typy danych: struktury, unie. Krzysztof Banaś Podstawy programowania 1

Strona główna. Strona tytułowa. Programowanie. Spis treści. Sobera Jolanta Strona 1 z 26. Powrót. Full Screen. Zamknij.

Podstawy programowania w języku C++

Programowanie w C++ Wykład 5. Katarzyna Grzelak. 26 marca kwietnia K.Grzelak (Wykład 1) Programowanie w C++ 1 / 40

Tablice (jedno i wielowymiarowe), łańcuchy znaków

> C++ dynamiczna alokacja/rezerwacja/przydział pamięci. Dane: Iwona Polak. Uniwersytet Śląski Instytut Informatyki

Typy wyliczeniowe Konwersje napis <-> liczba Struktury, unie Scanf / printf Wskaźniki

Instytut Mechaniki i Inżynierii Obliczeniowej Wydział Mechaniczny Technologiczny Politechnika Śląska

Programowanie obiektowe W3

1 Wskaźniki. 1.1 Główne zastosowania wskaźników

Języki i metodyka programowania. Wprowadzenie do języka C

Stałe i zmienne znakowe. Stała znakowa: znak

Wykład 4: Klasy i Metody

Dla każdej operacji łącznie tworzenia danych i zapisu ich do pliku przeprowadzić pomiar czasu wykonania polecenia. Wyniki przedstawić w tabelce.

Transkrypt:

Podstawy programowania w języku C++ Część dziewiąta Tablice a zmienne wskaźnikowe Autor Roman Simiński Kontakt roman.siminski@us.edu.pl www.us.edu.pl/~siminski Niniejsze opracowanie zawiera skrót treści wykładu, lektura tych materiałów nie zastąpi uważnego w nim uczestnictwa. Opracowanie to jest chronione prawem autorskim. Wykorzystywanie jakiegokolwiek fragmentu w celach innych niż nauka własna jest nielegalne. Dystrybuowanie tego opracowania lub jakiejkolwiek jego części oraz wykorzystywanie zarobkowe bez zgody autora jest zabronione.

Nazwa tablicy jako wskaźnik na jej początek Nazwa tablicy jest interpretowana jako ustalony wskaźnik na jej początek (pierwszy element). int tab[ 10 ]; 10 elementów 0 1 2 3 4 5 6 7 8 9 tab Pierwszy element: tab[ 0 ] Copyright Roman Simiński Strona : 2

Nazwa tablicy jako wskaźnik na jej początek, cd.... int tab[ 10 ]; int * p;... p = tab; 10 elementów 0 1 2 3 4 5 6 7 8 9 p tab Pierwszy element: tab[ 0 ] Przypisanie: p = tab; Jest równoznaczne z: p = &tab[ 0 ]; Copyright Roman Simiński Strona : 3

Nazwa tablicy jako wskaźnik na jej początek, cd.... tab[ 0 ] = 5; tab[ 1 ] = 1; tab[ 2 ] = 10;... tab[ i ] = 22; Odwołania równoważne *p = 5 *( p + 1 ) = 1 *( p + 2 ) = 10... *( p + i ) = 22 0 1 2 3 i 8 9 p tab p + 0 p + 1 p + 2 p + 3 p + i Copyright Roman Simiński Strona : 4

Nazwa tablicy jako wskaźnik na jej początek, cd.... Nazwa tablicy z indeksem Wskaźnik z przesunięciem Odwołania równoważne 0 1 2 3 i 8 9 p tab Odwołanie: *( p + i ) Odwołanie: tab[ i ] Copyright Roman Simiński Strona : 5

Nazwa tablicy jako wskaźnik na jej początek, cd.... Wyrażenie p + i jest wyrażeniem wskaźnikowym, wskazuje ono na obiekt oddalony o i obiektów danego typu od p. Wartość dodawana do wskaźnika jest skalowana rozmiarem typu obiektu wskazywanego. Każde odwołanie: tab[ i ]; można zapisać tak: *( tab + i ) Oraz Każde odwołanie: *( p + i ) można zapisać tak: p[ i ]; Copyright Roman Simiński Strona : 6

Uwaga, wskaźnik to nie tablica! int tab[ 10 ]; int * p = tab; Czy to jest to samo? Nie! int tab[ 10 ] obszar danych + wskaźnika na jego początek Nazwa tablicy 10 elementów tab 0 1 2 3 4 5 6 7 8 9 p int * p = tab wskaźnik zakotwiczony o początek tablicy Copyright Roman Simiński Strona : 7

Nazwa tablicy to ustalony wskaźnik na jej początek Nazwa tablicy jest ustalonym (niemodyfikowalnym) wskaźnikiem na pierwszy jej element. Nazw tablic nie wolno modyfikować! Zwykłe wskaźniki można. int tab[ 10 ]; int * p = tab; tab = p; tab++; Źle p = tab + 8; p++; OK Copyright Roman Simiński Strona : 8

Ciekawostka Wiemy, że odwołanie: tab[ i ] można zapisać tak: *( tab + i ) Oraz, że odwołanie *( tab + i ) można zapisać tak: tab[ i ] Wiemy również, że dodawanie jest przemienne, zatem każde odwołanie: *( tab + i ) można zapisać tak: *( i + tab ) Czy zatem odwołanie: *( i + tab ) można zapisać tak: i[ tab ]? Copyright Roman Simiński Strona : 9

Ciekawostka, cd.... Tak, można, dla kompilatora nie ma to większego znaczenia. char napis[] = "język c";... cout << napis << endl; 0[ napis ] = 'J'; // Zamiast napis[ 0 ] 6[ napis ] = 'C'; // Zamiast napis[ 6 ] język c Język C cout << napis << endl; Copyright Roman Simiński Strona : 10

Dlaczego nie wolno przypisywać tablic, posługując się ich nazwami? int a[ 10 ]; int b[ 10 ]; b = a; // Nie wolno przypisywać do siebie tablic! Gdyby przypisywanie było możliwe... a b 0 1 2 3 4 5 0 1 2 3 4 5 6 7 8 9 6 7 8 9 To po wykonaniu tej linii: b = a; gubimy obszar danych tablicy b! 0 1 2 3 4 5 a 6 7 8 9 b 0 1 2 3 4 5 6 7 8 9 Copyright Roman Simiński Strona : 11

Arytmetyka na wskaźnikach podsumowanie Dozwolone operacje wskaźnikowe to: przypisywanie wskaźników do obiektów tego samego typu, przypisywanie wskaźników do obiektów innego typu po konwersji, dodawanie lub odejmowanie wskaźnika i liczby całkowitej, odejmowanie lub porównanie dwóch wskaźników związanych z ta samą tablicą, przypisanie wskaźnikowi wartości zero (lub wskazania puste NULL) lub porównanie ze wskazaniem pustym. Copyright Roman Simiński Strona : 12

Wskaźniki w akcji metamorfoza funkcji put_string char napis[] = "Język C i C++"; put_string( napis ); void put_string( char s[] ) int i; for( i = 0; s[ i ]!= '\0'; i++ ) putchar( s[ i ] ); Wersja pierwotna Copyright Roman Simiński Strona : 13

Wskaźniki w akcji metamorfoza funkcji put_string char napis[ 80 ] = "C++"; put_string( napis ); void put_string2( char * s ) for( ; *s!= '\0'; s++ ) putchar( *s ); Eliminujemy zmienną i Jak to działa...? Copyright Roman Simiński Strona : 14

Wywołanie funkcji put_string char napis[ 80 ] = "C++"; put_string( napis ); void put_string2( char * s ) for( ; *s!= '\0'; s++ ) putchar( *s ); Kopiowanie parametru aktualnego napis do parametru s napis C + + \0 s Copyright Roman Simiński Strona : 15

Parametr s jest kopią wskaźnika napis char napis[ 80 ] = "C++"; put_string( napis ); void put_string2( char * s ) for( ; *s!= '\0'; s++ ) putchar( *s ); Kopiowanie parametru aktualnego napis do parametru s napis C + + \0 s Copyright Roman Simiński Strona : 16

Czy obiekt wskazywany przez s jest znacznikiem końca napisu? char napis[ 80 ] = "C++"; put_string( napis ); void put_string2( char * s ) for( ; *s!= '\0'; s++ ) putchar( *s ); Parametr s wskazuje na pierwszy element tablicy napis napis C + + \0 s Copyright Roman Simiński Strona : 17

Znak wskazywany przez s wyprowadzamy jest do stdout char napis[ 80 ] = "C++"; put_string( napis ); void put_string2( char * s ) for( ; *s!= '\0'; s++ ) putchar( *s ); Parametr s wskazuje na pierwszy element tablicy napis C napis C + + \0 s Copyright Roman Simiński Strona : 18

Wskaźnik s przesuwamy na następny znak char napis[ 80 ] = "C++"; put_string( napis ); void put_string2( char * s ) for( ; *s!= '\0'; s++ ) putchar( *s ); Parametr s wskazuje na kolejny element tablicy napis C napis C + + \0 s Copyright Roman Simiński Strona : 19

Czy obiekt wskazywany przez s jest znacznikiem końca napisu? char napis[ 80 ] = "C++"; put_string( napis ); void put_string2( char * s ) for( ; *s!= '\0'; s++ ) putchar( *s ); Parametr s wskazuje na kolejny element tablicy napis C napis C + + \0 s Copyright Roman Simiński Strona : 20

Znak wskazywany przez s wyprowadzamy jest do stdout char napis[ 80 ] = "C++"; put_string( napis ); void put_string2( char * s ) for( ; *s!= '\0'; s++ ) putchar( *s ); Parametr s wskazuje na kolejny element tablicy napis C+ napis C + + \0 s Copyright Roman Simiński Strona : 21

Wskaźnik s przesuwamy na następny znak char napis[ 80 ] = "C++"; put_string( napis ); void put_string2( char * s ) for( ; *s!= '\0'; s++ ) putchar( *s ); Parametr s wskazuje na kolejny element tablicy napis C+ napis C + + \0 s Copyright Roman Simiński Strona : 22

Czy obiekt wskazywany przez s jest znacznikiem końca napisu? char napis[ 80 ] = "C++"; put_string( napis ); void put_string2( char * s ) for( ; *s!= '\0'; s++ ) putchar( *s ); Parametr s wskazuje na kolejny element tablicy napis C+ napis C + + \0 s Copyright Roman Simiński Strona : 23

Znak wskazywany przez s wyprowadzamy jest do stdout char napis[ 80 ] = "C++"; put_string( napis ); void put_string2( char * s ) for( ; *s!= '\0'; s++ ) putchar( *s ); Parametr s wskazuje na kolejny element tablicy napis C++ napis C + + \0 s Copyright Roman Simiński Strona : 24

Wskaźnik s przesuwamy na następny znak char napis[ 80 ] = "C++"; put_string( napis ); void put_string2( char * s ) for( ; *s!= '\0'; s++ ) putchar( *s ); Parametr s wskazuje na kolejny element tablicy napis C++ napis C + + \0 s Copyright Roman Simiński Strona : 25

Czy obiekt wskazywany przez s jest znacznikiem końca napisu? char napis[ 80 ] = "C++"; put_string( napis ); void put_string2( char * s ) for( ; *s!= '\0'; s++ ) putchar( *s ); Parametr s wskazuje na kolejny element tablicy napis C++ napis C + + \0 s Copyright Roman Simiński Strona : 26

Wskaźniki w akcji metamorfoza funkcji put_string, wersja 3 i 4 void put_string3( char * s ) for( ; *s!= '\0' ; putchar( *s++ ) ) ; Kompresja iteracji for Najpierw pobierz znak wskazywany przez s, użyj *s ++ Potem zwiększ o jeden wartość wskaźnika s będzie on wtedy go. wskazywał na następny element tablicy. void put_string4( char * s ) while( *s ) putchar( *s++ ); Iteracja while nie jest taka zła... Znak '\0' to bajt o wartości 0 Copyright Roman Simiński Strona : 27

Wskaźniki pod lupą metamorfoza funkcji strcpy char s1[ 80 ] = "Język C"; char s2[ 20 ];... strcpy( s2, s1 ); Wersja początkowa Przypomnienie jak to działa s i++ s1... J ę z y k C \ 0... 0 1 2 3 4 5 6 79 d s2 J ę z y k C \ 0... 0 1 2 3 4 5 void strcpy( char d[], char s[] ) int i; for( i = 0; s[ i ]!= '\0'; i++ ) d[ i ] = s[ i ]; d[ i ] = '\0'; 6 19 Copyright Roman Simiński Strona : 28

Wskaźniki pod lupą metamorfoza funkcji strcpy void strcpy1( char * d, char * s ) while( *s!= '\0' ) *d = *s; d++; s++; *d = '\0'; Odwołania wskaźnikowe To właściwie nie wiele zmienia, poza wyeliminowaniem zmiennej i void strcpy2( char * d, char * s ) while( *s!= '\0' ) *d++ = *s++; *d = '\0'; Kompresja krok pierwszy Copyright Roman Simiński Strona : 29

Wskaźniki pod lupą metamorfoza funkcji strcpy void strcpy3( char * d, char * s ) while( ( *d++ = *s++ )!= '\0' ) ; Kompresja krok drugi Wartością tego wyrażenia jest znak (bajt) przepisany z obszaru wskazywanego przez s do obszaru wskazywanego przez d. ( *d++ = *s++ )!= '\0' Pobierz znak wskazywany, wykorzystaj go, zwiększ wskaźnik tak, by pokazywał na następny element tablicy. Copyright Roman Simiński Strona : 30

Wskaźniki pod lupą metamorfoza funkcji strcpy void strcpy4( char * d, char * s ) while( *d++ = *s++ ) ; Kompresja krok trzeci Znak '\0' to bajt o wartości 0 Często spotykaną praktyką w funkcjach bibliotecznych jest udostępnianie wskaźnika do tablicy (jednej z tablic) będącej parametrem: char * strcpy5( char * d, char * s ) while( *d++ = *s++ ) ; return d; Tablica d jako rezultat funkcji Copyright Roman Simiński Strona : 31

Wskaźniki pod lupą metamorfoza funkcji strcpy Pozwala to na skrócenie kodu, załóżmy następujące definicje tablic s1, s2, s3: char s1[ 80 ] = "C i C++"; char s2[ 80 ]; char s3[ 80 ]; Następujący fragment kodu: strcpy5( s2, s1 ); strcpy5( s3, s2 ); puts( s3 ); Można zapisać krócej: puts( strcpy5( s3, strcpy5( s2, s1 ) ) ); Copyright Roman Simiński Strona : 32

Wskaźniki pod lupą metamorfoza funkcji strcpy W dotychczasowych realizacjach funkcji strcpyx, funkcja może modyfikować zawartość tablicy źródłowej: char * strcpy5( char * d, char * s ) *s = 'A';... Modyfikacja tablicy źródłowej dozwolona, choć merytorycznie niepoprawna Aby temu zaradzić, można zadeklarować parametr reprezentujący tablicę źródłową w specyficzny sposób: char * strcpy6( char * d, const char * s ) *s = 'A';... Tablica źródłowa jest chroniona Copyright Roman Simiński Strona : 33

Wskaźniki pod lupą zastosowanie modyfikatora const Aby funkcja nie mogła zmodyfikować parametru przekazanego za pośrednictwem wskaźnika, należy w deklaracji użyć słowa const. Deklaracja: const char * s; oznacza, że s jest wskaźnikiem na stały (niemodyfikowalny) obiekt typu char. Można wyróżnić następujące kombinacje definicji wskaźnika z/bez const: const int * const p; // Ustalony wskaźnika na niemodyfikowalny obiekt int * const p; // Ustalony wskaź nika na modyfikowalny obiekt const int * p; // Zwykły wskaź nika na niemodyfikowalny obiekt int * p; // Zwykły wskaźnik na zwykły obiekt Copyright Roman Simiński Strona : 34

Wskaźniki pod lupą zastosowanie modyfikatora const Wersja najbardziej restrykcyjna pod lupą: const int * const p; To się nie uda, ustalony wskaźnik należy zainicjować! Posługiwanie się ustalonym wskaźnikiem do stałego obiektu: int i = 10; const int * const p = &i;... j = *p + 10;... *p = 20;... p = &j; To jest OK, odwołanie nie modyfikujące obiektu Niedozwolone, odwołanie modyfikujące obiekt Niedozwolone, odwołanie modyfikujące wskaźnik Copyright Roman Simiński Strona : 35

Wyznaczanie długości napisu funkcja strlen klasycznie Realizacja w wykorzystaniem iteracji while: int strlen( char s[] ) int len = 0; while( s[ len ]!= '\0' ) len++; return len; Realizacja w wykorzystaniem iteracji for: int strlen( char s[] ) int len; for( len = 0; s[ len ]!= '\0'; len++ ) ; return len; Copyright Roman Simiński Strona : 36

Wyznaczanie długości napisu funkcja strlen wskaźnikowo Realizacja w wykorzystaniem iteracji while: int strlen( char * s ) char * ptr = s; while( *ptr!= '\0' ) ptr++; return ( int )( ptr s ); Realizacja w wykorzystaniem iteracji for: int strlen( char * s ) char * ptr; for( ptr = s; *ptr!= '\0'; ptr++ ) ; return ( int )( ptr s ); Copyright Roman Simiński Strona : 37

Odwracanie kolejności znaków w napisie strrev klasycznie char * strrev( char s[] ) int begin, end; // Szukanie konca napisu for( end = 0; s[ end ]!= '\0'; end++ ) ; // Zamiana znakow miejscami for( begin = 0, end--; begin < end; begin++, end-- ) char c = s[ begin ]; s[ begin ] = s[ end ]; s[ end ] = c; return s; Copyright Roman Simiński Strona : 38

Odwracanie kolejności znaków w napisie strrev wskaźnikowo char * strrev( char * s ) char * begin, * end; // Szukanie znacznika konca for( end = s; *end ; end++ ) ; // Zamiana znakow miejscami for( begin = s, end--; begin < end; begin++, end-- ) char c = *begin; *begin = *end; *end = c; return s; Copyright Roman Simiński Strona : 39

Dynamiczna alokacja tablic konwencja języka C Na tablicach alokowanych dynamicznie na stercie, można wykonywać takie same operacje, jak na tablicach statycznych. Należy tylko uważnie przydzielać i zwalniać pamięć. char * s = NULL; int n; /* Tu ustalenie liczby potrzebnych elementów i zapamiętanie w zmiennej n */ s = malloc( n * sizeof( char ) ); if( s!= NULL ) strcpy( s, "Język C " ); strcat( s, "fajny jest!" ); puts( s );... free( s ); Zobaczmy, jak wyglądają kolejne etapy definiowania i wykorzystania takiej tablicy... Copyright Roman Simiński Strona : 40

Dynamiczna alokacja tablic konwencja języka C, etap 1-szy Definicja wskaźnika typ obiektu wskazywanego taki, jak typ elementów tablicy jakich potrzebujemy. Zerowanie wskaźnika to dobra praktyka. char * s = NULL; int n; /* Tu ustalenie liczby potrzebnych elementów i zapamiętanie w zmiennej n */ s = malloc( n * sizeof( char ) ); if( s!= NULL ) strcpy( s, "Język C " ); strcat( s, "fajny jest!" ); puts( s );... free( s ); s Pamięć operacyjna Sterta Copyright Roman Simiński Strona : 41

Dynamiczna alokacja tablic konwencja języka C, etap 2-gi Zwykle korzystamy ze zmiennej, która pozwoli zapamiętać ilu elementową tablicę potrzebujemy. char * s = NULL; int n; /* Tu ustalenie liczby potrzebnych elementów i zapamiętanie w zmiennej n */ s = malloc( n * sizeof( char ) ); if( s!= NULL ) strcpy( s, "Język C " ); strcat( s, "fajny jest!" ); puts( s );... free( s ); s Pamięć operacyjna Sterta Copyright Roman Simiński Strona : 42

Dynamiczna alokacja tablic konwencja języka C, etap 3-ci Przed utworzeniem tablicy musimy ustalić konkretną liczbę elementów tablicy. Jak ustalimy tę liczbę zależy od konkretnego zastosowania. char * s = NULL; int n; /* Tu ustalenie liczby potrzebnych elementów i zapamiętanie w zmiennej n */ s = malloc( n * sizeof( char ) ); if( s!= NULL ) strcpy( s, "Język C " ); strcat( s, "fajny jest!" ); puts( s );... free( s ); s Pamięć operacyjna Sterta Copyright Roman Simiński Strona : 43

Dynamiczna alokacja tablic konwencja języka C, etap 4-ty Przydział pamięci dla tablicy funkcja malloc otrzymuje liczbę bajtów potrzebnych do przechowania ustalonej liczby elementów tablicy. char * s = NULL; int n; /* Tu ustalenie liczby potrzebnych elementów i zapamiętanie w zmiennej n */ s = malloc( n * sizeof( char ) ); if( s!= NULL ) strcpy( s, "Język C " ); strcat( s, "fajny jest!" ); puts( s );... free( s ); s Pamięć operacyjna Sterta?...? Pamięć operacyjna Copyright Roman Simiński Strona : 44

Dynamiczna alokacja tablic konwencja języka C, etap 5-ty Kontrola poprawności przydziału pamięci. Uwaga to koniecznie niezbędny etap! Przydzielony obszar pamięci ma przypadkową zawartość. char * s = NULL; int n; /* Tu ustalenie liczby potrzebnych elementów i zapamiętanie w zmiennej n */ s = malloc( n * sizeof( char ) ); if( s!= NULL ) strcpy( s, "Język C " ); strcat( s, "fajny jest!" ); puts( s );... free( s ); s Pamięć operacyjna Sterta???...? Pamięć operacyjna Copyright Roman Simiński Strona : 45

Dynamiczna alokacja tablic konwencja języka C, etap 6-ty Tak utworzona tablicę można używać tak samo, jak każdą inną tablicę w języku C. Wszystkie funkcje do manipulowania np. napisami działają bez problemu. char * s = NULL; int n; /* Tu ustalenie liczby potrzebnych elementów i zapamiętanie w zmiennej n */ s = malloc( n * sizeof( char ) ); if( s!= NULL ) strcpy( s, "Język C " ); strcat( s, "fajny jest!" ); puts( s );... free( s ); s Pamięć operacyjna Sterta Język C fajny Pamięć jest! operacyjna Copyright Roman Simiński Strona : 46

Dynamiczna alokacja tablic konwencja języka C, etap 6-ty Gdy tablica nie jest już potrzebna, zwalniamy przydzieloną pamięć i oddajemy do puli wolnych bloków. Uwaga, wskaźnik pokazuje dalej na zwolniony obszar pamięci! char * s = NULL; int n; /* Tu ustalenie liczby potrzebnych elementów i zapamiętanie w zmiennej n */ s = malloc( n * sizeof( char ) ); if( s!= NULL ) strcpy( s, "Język C " ); strcat( s, "fajny jest!" ); puts( s );... free( s ); s Pamięć operacyjna Sterta Język C fajny Pamięć jest! operacyjna Copyright Roman Simiński Strona : 47

Dynamiczna alokacja tablic konwencja języka C, etap 6-ty Zerowanie wskaźnika po zwolnieniu pamięci jest dobrą praktyką. char * s = NULL; int n; /* Tu ustalenie liczby potrzebnych elementów i zapamiętanie w zmiennej n */ s = malloc( n * sizeof( char ) ); if( s!= NULL ) strcpy( s, "Język C " ); strcat( s, "fajny jest!" ); puts( s );... free( s ); s = NULL; s Pamięć operacyjna Sterta Copyright Roman Simiński Strona : 48

Dynamiczna alokacja tablic konwencja języka C++ W języku C++ wykorzystujemy operatory new i delete. Stara wersja, zakładająca, że operator new oddaje wskaźnik zerowy w przypadku braku wolnej pamięci: char * s = 0; int n; // Tu ustalenie liczby potrzebnych elementów i zapamiętanie w zmiennej n s = new char [ n ]; if( s!= 0 ) strcpy( s, "Język C " ); strcat( s, "fajny jest!" ); puts( s );... delete [] s; Copyright Roman Simiński Strona : 49

Dynamiczna alokacja tablic konwencja języka C++ Nowa wersja, zakładająca, że operator new generuje wyjątek w przypadku braku wolnej pamięci: char * s = 0; int n; // Tu ustalenie liczby potrzebnych elementów i zapamiętanie w zmiennej n try s = new char [ n ]; strcpy( s, "Język C " ); strcat( s, "fajny jest!" ); puts( s );... delete [] s; catch(... ) cout << "Brak pamięci dla wykonania tej operacji"; Copyright Roman Simiński Strona : 50

Dynamiczna alokacja tablic konwencja języka C++ Wykorzystanie operatora new (nothrow) nie generującego wyjątków, obsługa jak w starej wersji: char * s = 0; int n; // Tu ustalenie liczby potrzebnych elementów i zapamiętanie w zmiennej n s = new (nothrow) char [ n ]; if( s!= 0 ) strcpy( s, "Język C " ); strcat( s, "fajny jest!" ); puts( s );... delete [] s; Copyright Roman Simiński Strona : 51

Dynamiczna alokacja tablic konwencja języka C++ Zerowanie wskaźnika po zwolnieniu pamięci jest dobrą praktyką. char * s = 0; int n; // Tu ustalenie liczby potrzebnych elementów i zapamiętanie w zmiennej n s = new (nothrow) char [ n ]; if( s!= 0 ) strcpy( s, "Język C " ); strcat( s, "fajny jest!" ); puts( s );... delete [] s; s = 0; Copyright Roman Simiński Strona : 52

Dynamiczna alokacja tablic konwencja języka C++ Zerowanie wskaźnika po zwolnieniu pamięci jest dobrą praktyką. char * s = 0; int n; // Tu ustalenie liczby potrzebnych elementów i zapamiętanie w zmiennej n try s = new char [ n ]; strcpy( s, "Język C " ); strcat( s, "fajny jest!" ); puts( s );... delete [] s; s = 0; catch(... ) cout << "Brak pamięci dla wykonania tej operacji"; Copyright Roman Simiński Strona : 53

Ważna sprawa ostrożnie z parametrami wskaźnikowymi! W funkcjach bibliotecznych języka C i C++ stałą praktyką jest deklarowanie parametrów tablicowych z wykorzystaniem wskaźników, np: int strlen( char * s ); zamiast int strlen( char s[] ); Wymaga to dokładnego przeczytania dokumentacji, bowiem programiści często się mylą. Rozważmy następujący przykład (fragment systemu pomocy firmy Borland): Prototype char *gets(char *s); Description Gets a string from stdin. gets collects a string of characters terminated by a new line from the standard input stream stdin and puts it into s. The new line is replaced by a null character (\0) in s. gets allows input strings to contain certain whitespace characters (spaces, tabs). gets returns when it encounters a new line; everything up to the new line is copied into s. Copyright Roman Simiński Strona : 54

Ważna sprawa ostrożnie z parametrami wskaźnikowymi! Niedokładna lektura dokumentacji może sugerować, że funkcji należy użyć tak: char * imie; printf( "Podaj imie: " ); gets( imie ); Pamięć operacyjna imie??? Aga Pamięć operacyjna gets( imie ) Gdyby wskaźnik był wyzerowany, kompilator czasem pomoże: char * imie = NULL; imie Pamięć operacyjna printf( "Podaj imie: " ); gets( imie ); gets( imie ) Pamięć operacyjna Stare kompilatory firmy Borland: Null pointer assignment Copyright Roman Simiński Strona : 55

Ważna sprawa ostrożnie z parametrami wskaźnikowymi! A trzeba np. tak: char imie[ 80 ]; printf( "Podaj imie: " ); gets( imie ); Pamięć operacyjna imie Aga Pamięć operacyjna gets( imie ) Lub tak: char * imie = NULL; imie = new (nothrow) char[ 80 ]; if( imie!= 0 ) printf( "Podaj imie: " ); gets( imie );... delete [] imie; Pamięć operacyjna imie Aga Pamięć operacyjna gets( imie ) Copyright Roman Simiński Strona : 56

Można tworzyć dynamicznie tablice dowolnych typów double * dochody = 0; int liczbamiesiecy; // Tu ustalenie liczby miesiecy okresu rozrachunkowego dochody = new (nothrow) double [ liczbamiesiecy ]; if( dochody!= 0 ) for( int miesiac = 0; miesiac < liczbamiesiecy; miesiac++ ) dochody[ miesiac ] = 0;... delete [] dochody; Copyright Roman Simiński Strona : 57

Można tworzyć dynamicznie tablice dowolnych typów typedef unsigned char byte; byte * bitmapa = 0; int rozmiarrysunku; // Tu ustalenie liczby bajtów rysunku bitmapowego bitmapa = new (nothrow) byte [ rozmiarrysunku ]; if( bitmapa!= 0 ) // Zaladuj bitmape // Zrob z nia co trzeba // Gdy juz niepotrzebna delete [] bitmapa; Copyright Roman Simiński Strona : 58

Tablice a problem ich początkowego rozmiaru Według standardu C89 i C++: tablica zawsze składa się z ustalonej, i znanej na etapie kompilacji liczby elementów, liczba elementów tablicy nie ulega zmianie w trakcie działania programu tablice są statyczne. W standardzie C99 istnieją tablice VLA (ang. variable length array): liczba elementów tablicy może być zdefiniowany w trakcie wykonania programu może być określona wartością zmiennej, ta wartość nie musi być znana na etapie kompilacji, liczba elementów tablicy nie ulega zmianie w trakcie działania programu raz stworzona tablica zachowuje swój rozmiar. Copyright Roman Simiński Strona : 59

Tablice a problem ich początkowego rozmiaru przykład Problem Należy napisać program pozwalający na ewidencjonowanie czasów osiągniętych przez zawodników maratonu. Liczba zawodników nie jest dokładnie znana, zakłada się jednak, że startowa pula numerów jest ograniczona do 300. Należy ewidencjonować dokładnie tyle czasów ile to potrzebne, program będzie być może uruchamiany na starym komputerze przenośnym. Copyright Roman Simiński Strona : 60

C89 i tablica czasów dla liczby zawodników nie większej niż 300 int lb_zawodnikow; printf( "\npodaj liczbe zawodnikow (maks. 300): " ); scanf( "%d", &lb_zawodnikow ); if( lb_zawodnikow > 0 && lb_zawodnikow <= 300 ) float * czasy; int nr_zawodnika; Ustalenie liczby zawodników, których czasy będą ewidencjonowane if( ( czasy = malloc( lb_zawodnikow * sizeof( float ) ) )!= NULL ) printf( "\npodaj czasy kolejnych zawodnikow:\n" ); for( nr_zawodnika = 0; nr_zawodnika < lb_zawodnikow; nr_zawodnika++ ) printf( "%d>", nr_zawodnika + 1 ); scanf( "%f", &czasy[ nr_zawodnika ] ); /* Jakies operacje na tablicy czasy */ printf( "\n\nzawodnik Czas" ); for( nr_zawodnika = 0; nr_zawodnika < lb_zawodnikow; nr_zawodnika++ ) printf( "\n%-10d%-.2f", nr_zawodnika + 1, czasy[ nr_zawodnika ] ); free( czasy ); else printf( "\nbrak pamieci!" ); else printf( "Zbyt duza liczba zawodnikow" ); Copyright Roman Simiński Strona : 61

C89 i tablica czasów dla liczby zawodników nie większej niż 300 int lb_zawodnikow; printf( "\npodaj liczbe zawodnikow (maks. 300): " ); scanf( "%d", &lb_zawodnikow ); if( lb_zawodnikow > 0 && lb_zawodnikow <= 300 ) float * czasy; int nr_zawodnika; Czy liczba zawodników nie przekracza zadanego maksimum? if( ( czasy = malloc( lb_zawodnikow * sizeof( float ) ) )!= NULL ) printf( "\npodaj czasy kolejnych zawodnikow:\n" ); for( nr_zawodnika = 0; nr_zawodnika < lb_zawodnikow; nr_zawodnika++ ) printf( "%d>", nr_zawodnika + 1 ); scanf( "%f", &czasy[ nr_zawodnika ] ); /* Jakies operacje na tablicy czasy */ printf( "\n\nzawodnik Czas" ); for( nr_zawodnika = 0; nr_zawodnika < lb_zawodnikow; nr_zawodnika++ ) printf( "\n%-10d%-.2f", nr_zawodnika + 1, czasy[ nr_zawodnika ] ); free( czasy ); else printf( "\nbrak pamieci!" ); else printf( "Zbyt duza liczba zawodnikow" ); Copyright Roman Simiński Strona : 62

C89 i tablica czasów dla liczby zawodników nie większej niż 300 int lb_zawodnikow; printf( "\npodaj liczbe zawodnikow (maks. 300): " ); scanf( "%d", &lb_zawodnikow ); if( lb_zawodnikow > 0 && lb_zawodnikow <= 300 ) float * czasy; int nr_zawodnika; Tablica ewidencjonująca czasy zostanie przydzielona dynamicznie if( ( czasy = malloc( lb_zawodnikow * sizeof( float ) ) )!= NULL ) printf( "\npodaj czasy kolejnych zawodnikow:\n" ); for( nr_zawodnika = 0; nr_zawodnika < lb_zawodnikow; nr_zawodnika++ ) printf( "%d>", nr_zawodnika + 1 ); scanf( "%f", &czasy[ nr_zawodnika ] ); /* Jakies operacje na tablicy czasy */ printf( "\n\nzawodnik Czas" ); for( nr_zawodnika = 0; nr_zawodnika < lb_zawodnikow; nr_zawodnika++ ) printf( "\n%-10d%-.2f", nr_zawodnika + 1, czasy[ nr_zawodnika ] ); free( czasy ); else printf( "\nbrak pamieci!" ); else printf( "Zbyt duza liczba zawodnikow" ); Copyright Roman Simiński Strona : 63

C89 i tablica czasów dla liczby zawodników nie większej niż 300 int lb_zawodnikow; printf( "\npodaj liczbe zawodnikow (maks. 300): " ); scanf( "%d", &lb_zawodnikow ); if( lb_zawodnikow > 0 && lb_zawodnikow <= 300 ) float * czasy; int nr_zawodnika; Przydział pamięci dla tablicy czasów i kontrola poprawności jego wykonania if( ( czasy = malloc( lb_zawodnikow * sizeof( float ) ) )!= NULL ) printf( "\npodaj czasy kolejnych zawodnikow:\n" ); for( nr_zawodnika = 0; nr_zawodnika < lb_zawodnikow; nr_zawodnika++ ) printf( "%d>", nr_zawodnika + 1 ); scanf( "%f", &czasy[ nr_zawodnika ] ); /* Jakies operacje na tablicy czasy */ printf( "\n\nzawodnik Czas" ); for( nr_zawodnika = 0; nr_zawodnika < lb_zawodnikow; nr_zawodnika++ ) printf( "\n%-10d%-.2f", nr_zawodnika + 1, czasy[ nr_zawodnika ] ); free( czasy ); else printf( "\nbrak pamieci!" ); else printf( "Zbyt duza liczba zawodnikow" ); Copyright Roman Simiński Strona : 64

C89 i tablica czasów dla liczby zawodników nie większej niż 300 int lb_zawodnikow; printf( "\npodaj liczbe zawodnikow (maks. 300): " ); scanf( "%d", &lb_zawodnikow ); if( lb_zawodnikow > 0 && lb_zawodnikow <= 300 ) float * czasy; int nr_zawodnika; Operacje na tablicy przetwarzana jest rzeczywista liczba elementów if( ( czasy = malloc( lb_zawodnikow * sizeof( float ) ) )!= NULL ) printf( "\npodaj czasy kolejnych zawodnikow:\n" ); for( nr_zawodnika = 0; nr_zawodnika < lb_zawodnikow; nr_zawodnika++ ) printf( "%d>", nr_zawodnika + 1 ); scanf( "%f", &czasy[ nr_zawodnika ] ); /* Jakies operacje na tablicy czasy */ printf( "\n\nzawodnik Czas" ); for( nr_zawodnika = 0; nr_zawodnika < lb_zawodnikow; nr_zawodnika++ ) printf( "\n%-10d%-.2f", nr_zawodnika + 1, czasy[ nr_zawodnika ] ); free( czasy ); else printf( "\nbrak pamieci!" ); else printf( "Zbyt duza liczba zawodnikow" ); Copyright Roman Simiński Strona : 65

C89 i tablica czasów dla liczby zawodników nie większej niż 300 int lb_zawodnikow; printf( "\npodaj liczbe zawodnikow (maks. 300): " ); scanf( "%d", &lb_zawodnikow ); if( lb_zawodnikow > 0 && lb_zawodnikow <= 300 ) float * czasy; int nr_zawodnika; Tablica nie jest już dłużej potrzebna, zwolnienie pamięci jej przydzielonej if( ( czasy = malloc( lb_zawodnikow * sizeof( float ) ) )!= NULL ) printf( "\npodaj czasy kolejnych zawodnikow:\n" ); for( nr_zawodnika = 0; nr_zawodnika < lb_zawodnikow; nr_zawodnika++ ) printf( "%d>", nr_zawodnika + 1 ); scanf( "%f", &czasy[ nr_zawodnika ] ); /* Jakies operacje na tablicy czasy */ printf( "\n\nzawodnik Czas" ); for( nr_zawodnika = 0; nr_zawodnika < lb_zawodnikow; nr_zawodnika++ ) printf( "\n%-10d%-.2f", nr_zawodnika + 1, czasy[ nr_zawodnika ] ); free( czasy ); else printf( "\nbrak pamieci!" ); else printf( "Zbyt duza liczba zawodnikow" ); Copyright Roman Simiński Strona : 66

C99 i tablica czasów dla liczby zawodników nie większej niż 300 #include <stdlib.h> #include <stdio.h> int main() int lb_zawodnikow; Tablica VLA o rozmiarze początkowym definiowanym w trakcie działania programu printf( "\npodaj liczbe zawodnikow (maks. 300): " ); scanf( "%d", &lb_zawodnikow ); if( lb_zawodnikow > 0 && lb_zawodnikow <= 300 ) float czasy[ lb_zawodnikow ]; int nr_zawodnika; printf( "\npodaj czasy kolejnych zawodnikow:\n" ); for( nr_zawodnika = 0; nr_zawodnika < lb_zawodnikow; nr_zawodnika++ ) printf( "%d>", nr_zawodnika + 1 ); scanf( "%f", &czasy[ nr_zawodnika ] ); /* Jakies operacje na tablicy czasy */ printf( "\n\nzawodnik Czas" ); for( nr_zawodnika = 0; nr_zawodnika < lb_zawodnikow; nr_zawodnika++ ) printf( "\n%-10d%-.2f", nr_zawodnika + 1, czasy[ nr_zawodnika ] ); else printf( "Zbyt duza liczba zawodnikow" ); return EXIT_SUCCESS; Copyright Roman Simiński Strona : 67