Wprowadzenie do programowania w języku C

Podobne dokumenty
Podstawy programowania w języku C++

Podstawy programowania w języku C++

Podstawy programowania w języku C++

Podstawy programowania w języku C++

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

Podstawy programowania w języku C++

Podstawy programowania w języku C++

Programowanie w języku C++

Podstawy programowania

Tablice deklaracja, reprezentacja wewnętrzna

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

Lab 9 Podstawy Programowania

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

Wprowadzenie do programowania w języku C

Wskaźniki. Informatyka

Podstawy programowania w języku C i 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 obiektowego Nieobiektowe elementy języka C++

Podstawy programowania w języku C++

ZASADY PROGRAMOWANIA KOMPUTERÓW

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

Podstawy programowania w języku C++

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

Podstawy programowania w języku C++

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

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:

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

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

Wykład 1: Wskaźniki i zmienne dynamiczne

Wskaźniki. Programowanie Proceduralne 1

Wskaźniki w C. Anna Gogolińska

Programowanie w języku C++

Podstawy programowania komputerów

Techniki Programowania wskaźniki

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

Podstawy programowania w języku C++

Wprowadzenie do programowanie obiektowego w języku C++

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

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

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

Wprowadzenie do programowania w języku C

Podstawy programowania. Wykład PASCAL. Zmienne wskaźnikowe i dynamiczne. dr Artur Bartoszewski - Podstawy prograowania, sem.

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

Tablice, funkcje, wskaźniki - wprowadzenie

Struktury, unie, formatowanie, wskaźniki

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

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

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

część 8 wskaźniki - podstawy Jarosław Gramacki Instytut Informatyki i Elektroniki Podstawowe pojęcia

Tablice, funkcje - wprowadzenie

DYNAMICZNE PRZYDZIELANIE PAMIECI

Podstawy programowania

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

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

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

Wykład 4: Klasy i Metody

Struktury czyli rekordy w C/C++

Stałe, tablice dynamiczne i wielowymiarowe

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy INNE SPOSOBY INICJALIZACJI SKŁADOWYCH OBIEKTU

Podstawy programowania 1

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

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

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

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

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

Wprowadzenie do programowania w języku C

PARADYGMATY PROGRAMOWANIA Wykład 4

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

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

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

Języki programowania C i C++ Wykład: Typy zmiennych c.d. Operatory Funkcje. dr Artur Bartoszewski - Języki C i C++, sem.

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy KONSTRUKTORY

Podstawy programowania. Wykład 7 Tablice wielowymiarowe, SOA, AOS, itp. Krzysztof Banaś Podstawy programowania 1

Język C zajęcia nr 11. Funkcje

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

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

Wykład 8: klasy cz. 4

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy PRAWA PRZYJACIÓŁ KLASY. Dostęp z zewnątrz: Dostęp z wewnątrz:

Podstawy programowania. Wykład: 8. Wskaźniki. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

XV. Wskaźniki Odczytywanie adresu pamięci istniejących zmiennych Wskaźniki pierwsze spojrzenie.

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

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

Java - tablice, konstruktory, dziedziczenie i hermetyzacja

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy INNE SPOSOBY INICJALIZACJI SKŁADOWYCH OBIEKTU

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

Operatory. Operatory bitowe i uzupełnienie informacji o pozostałych operatorach. Programowanie Proceduralne 1

Szablony klas, zastosowanie szablonów w programach

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

Materiał Typy zmiennych Instrukcje warunkowe Pętle Tablice statyczne Wskaźniki Tablice dynamiczne Referencje Funkcje

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

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.

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

KURS C/C++ WYKŁAD 7. struct Punkt { int x, y; int kolor; };

> C++ wskaźniki. Dane: Iwona Polak. Uniwersytet Śląski Instytut Informatyki 26 kwietnia 2017

IX. Wskaźniki.(3 godz.)

Wstęp do programowania INP003203L rok akademicki 2018/19 semestr zimowy. Laboratorium 2. Karol Tarnowski A-1 p.

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

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

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

Dynamiczny przydział pamięci w języku C. Dynamiczne struktury danych. dr inż. Jarosław Forenc. Metoda 1 (wektor N M-elementowy)

Transkrypt:

Wprowadzenie do programowania w języku C Część szósta Zmienne wskaźnikowe koncepcja, zastosowania, sztuczki i triki Autor Roman Simiński Kontakt 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.

Zanim poznamy zmienne wskaźnikowe... Co to jest zmienna? Zmienna jest obiektem w programie przeznaczonym do przechowywania wartości. int i; i = 10; Pamięć operacyjna i Dziwne pojęcia l-wartość i r-wartość 10 Każda zmienna ma swoją nazwę, oraz typ wartości. Zmienne są przechowywane w pamięci operacyjnej, liczba zajętych bajtów zależy od typu zmiennej. Nazwa zmiennej identyfikuje zmienną w programie zwalniając programistę od zastanawiania się, pod jakim adresem w pamięci zmienna jest zlokalizowana. int i; int j; j = 5; i = j; 5 = i; j to l-wartość 5 to r-wartość i to l-wartość j to r-wartość Zmienna może występować po lewej stronie operatora przypisania, mówi się, że jest wtedy l-wartością. Wszystko co może występować po prawej stronie operatora przypisania jest r-wartością. Nie każda r-wartość to l-wartość Obiekt jest nazwanym obszarem pamięci. l-wartość jest wyrażeniem lokalizującym ten obiekt w pamięci Copyright Roman Simiński Strona : 2

Zanim poznamy zmienne wskaźnikowe... Motywacja W języku C intensywnie wykorzystuje się l-wartości oparte na zmiennych wskaźnikowych oraz na wyrażeniach te zmienne zawierających. Dokładne opanowanie zasad posługiwania się wskaźnikami jest niezbędne do efektywnego i sprawnego programowania w C i C++. Tej umiejętności nie można pominąć, przeskoczyć lub zostawić na później. Nie oszukujmy się ten, kto nie opanuje zasad posługiwania się wskaźnikami nigdy nie będzie prawdziwym, profesjonalnym programistą wykorzystującym język C lub C++. Copyright Roman Simiński Strona : 3

Zmienne wskaźnikowe koncepcja Po co są zmienne wskaźnikowe? Zmienna wskaźnikowa przeznaczona jest do lokalizowania (wskazywania) obiektów w pamięci operacyjnej. Jedyną rolą zmiennej wskaźnikowej jest umożliwienie odwoływania się do obiektów wskazywanych. Naiwna interpretacja graficzna Zmienna wskaźnikowa Obiekt wskazywany Zmienne wskaźnikowe też żyją w pamięci operacyjnej Zmienna wskaźnikowa Obiekt wskazywany Copyright Roman Simiński Strona : 4

Zmienne wskaźnikowe koncepcja Trzy stany zmiennej wskaźnikowej Zmienna wskaźnikowa wskazuje na konkretny obiekt w pamięci Zmienna wskaźnikowa Obiekt wskazywany OK Zmienna wskaźnikowa nie wskazuje na żaden obiekt Zmienna wskaźnikowa OK Zmienna wskaźnikowa wskazuje na nie wiadomo co Zmienna wskaźnikowa? Kiepsko Copyright Roman Simiński Strona : 5

Zmienne wskaźnikowe koncepcja Co zawiera zmienna wskaźnikowa? Zwykle przyjmuje się, że zmienna wskaźnikowa zawiera w sobie adres obiektu wskazywanego. Zmienna wskaźnikowa 345fa012h Obiekt wskazywany Adres: 345fa012h Jednak zmienna wskaźnikowa nie musi w sobie zawierać adresu bezpośredniego, jej zawartość może, w pewnej realizacji kompilatora, zawierać inną informację, pozwalającą na precyzyjne i jednoznaczne zidentyfikowanie położenia obiektu w pamięci. Przykład W 16-to bitowych realizacjach kompilatorów firmy Borland: zmienna wskaźnikowa zawiera przesunięcie (ang. offset) obiektu względem początku segmentu gdy wskaźniki są krótkie (odwołania wewnątrz segmentu), zmienna wskaźnikowa zawiera adres segmentu i przesunięcie obiektu gdy wskaźniki są długie (odwołania międzysegmentowe). Copyright Roman Simiński Strona : 6

Zmienne wskaźnikowe deklaracje W deklaracjach zmiennych wskaźnikowych występuje * int i = 10; int * pi; i Pamięć operacyjna 10 int i = 10; int * pi = NULL; i Pamięć operacyjna 10 pi? pi Deklaracja pod lupą Nieznany obiekt wskazywany Wskaźnik pusty oznacza brak obiektu wskazywanego int * pi ; To oznacza, że deklarowana zmienna wskaźnikowa będzie przeznaczona do lokalizowania w pamięci obiektów typu int. To oznacza, że deklarowana zmienna będzie wskaźnikiem, kompilator wie, ile dla niej zarezerwować pamięci. Nazwa deklarowanej zmiennej wskaźnikowej. Podlega takim samym regułom jak nazwy innych zmiennych. Często zawiera p lub ptr od pointer. Copyright Roman Simiński Strona : 7

Zmienne wskaźnikowe deklaracje Rola wskaźnika pustego NULL Tak zdefiniowana zmienna wskaźnikowa: int * pi; ma wartość początkową zależną od kontekstu deklaracji. Jeżeli ta zmienna jest klasy auto, to jej wartość jest przypadkowa zmienna wskazuje zatem na bliżej nieznany obiekt w pamięci. W pliku nagłówkowym stddef.h zdefiniowana stałą NULL, reprezentującą wskaźnik pusty, niezależny od platformy i implementacji. Tak zdefiniowana zmienna: int * pi = NULL; jest wskaźnikiem pustym, a więc nie wskazuje żadnego obiektu w pamięci. To, czy zmienna wskaźnikowa jest wskaźnikiem pustym można sprawdzić: if( pi!= NULL ) if( pi == NULL ) W języku C jawnie inicjowanie zmiennych wskaźnikowych oraz posługiwanie się wartością NULL (a nie wartością 0) jest dobrą praktyką programistyczną. Copyright Roman Simiński Strona : 8

Zmienne wskaźnikowe podstawowe operacje Przypisywanie wartości zmiennym wskaźnikowym int i = 10; int * pi = NULL; pi = &i; i Pamięć operacyjna 10 O biekt ws kazywany pi Przypisanie pod lupą pi = & i ; Od momentu tego przypisania, pi wskazuje zmienną i, umożliwiając realizację dowolnych operacji na tej zmiennej. Jednoargumentowy operator & buduje wyrażenie wskaźnikowe lokalizujące zmienną w pamięci operacyjnej. Argument musi być l-wartością nie odnoszącą się do obiektu register ani pola bitowego. Wyrażenie wskaźnikowe lokalizujące zmienną i w pamięci. Copyright Roman Simiński Strona : 9

Zmienne wskaźnikowe podstawowe operacje Odwoływanie sie do obiektu wskazywanego int i = 10; int * pi = NULL; pi = &i; *pi = 20; i pi Pamięć operacyjna 20 *pi Odwołanie pod lupą * pi = 20 ; Ten zapis oznacza obiekt wskazywany przez pi. Zapis *pi może wystąpić wszędzie tam, gdzie może wystąpić i. Zmienna pi jest aliasem, linikiem do obiektu i. Jednoargumentowy operator adresowania pośredniego * daje w wyniku obiekt wskazywany przez argument pi. Dowolne wyrażenie typu zgodnego z typem obiektu wskazywanego. Copyright Roman Simiński Strona : 10

Zmienne wskaźnikowe podstawowe operacje Odwoływanie sie do obiektu wskazywanego Po przypisaniu: pi = &i; te fragmenty są równoważne: char s[] = 123 ; *pi = atoi( s ); *pi += 10; printf( %d, *pi ); char s[] = 123 ; i = atoi( s ); i += 10; printf( %d, i ); Jeżeli wskaźnik pi wskazuje na zmienną i, to *pi może wystąpić wszędzie tam, gdzie może wystąpić i. Zmienna pi jest aliasem, linikiem do i. Copyright Roman Simiński Strona : 11

Zmienne wskaźnikowe zastosowania Realizacja przekazywania parametrów przez zmienną z użyciem wskaźników void inc( int * pi ) *pi = *pi + 1; } int i = 10; inc( &i ); i pi Pamięć operacyjna 11 10 *pi=*pi+1 void getint( int * ptr ) char s[ 80 ]; gets( s ); *ptr = atoi( s ); } int liczba; getint( &liczba ); liczba ptr Pamięć operacyjna 123 W języku C wykorzystuje się parametry będące wskaźnikami do realizacji przekazywania parametrów działającego podobnie do przekazywania przez zmienną. void zamien( int * pierwszy, int * drugi ) int s; /* Schowek */ } s = *pierwszy; *pierwszy = *drugi; *drugi = s; a 5 b 10 s 5 int a = 5, b = 10; printf( "a=%d b=%d", a, b ); zamien( &a, &b ); printf( "a=%d b=%d", a, b ); a=5 b=10 a=10 b=5 Copyright Roman Simiński Strona : 12

Zmienne wskaźnikowe zastosowania Dynamiczny przydział pamięci Dynamiczny przydział pamięci polega na zarezerwowaniu fragmentu pamięci w obszarze pamięci wolnej zwanej stertą, dla obiektu pamięciowego zwanego dynamicznym. p Sterta a int * p = NULL; p = malloc( sizeof( int ) ); *p = 10; free( p ); p p 10 Sterta a Sterta a Przydziela się fragment o określonym rozmiarze, jedyny sposób na odwoływanie się do takiego obiektu to wykorzystanie wskaźnika. p Sterta a Strona : Copyright Roman Simiński 13

Zmienne wskaźnikowe zastosowania Dynamiczny przydział pamięci double * pole; pole = malloc( sizeof( double ) ); if( pole!= NULL ) *pole = PI * r * r; printf( "Pole kola: %f", *pole ); free( pole ); } Rezultatem funkcji malloc jest wskaźnik do przydzielonego dynamicznie obszaru pamięci przeznaczonego dla obiektu o rozmiarze sizeof(double). Wskaźnik będzie miał wartość NULL jeżeli pamięć nie została przydzielona. Zwalnia obszar pamięci wskazywany przez wskaźnik pole. W tym przykładzie przydział pamięci wydaje sie bezsensowny. Nie lepiej posłużyć się zwykłą zmienną typu double? Tak, dynamiczny przydział sprawdza się w innych sytuacjach, ale o tym później.... Copyright Roman Simiński Strona : 14

Zmienne wskaźnikowe zastosowania w przetwarzaniu tablic Tablice a wskaźniki Nazwa tablicy jest interpretowana jako ustalony wskaźnik na jej początek (pierwszy element). tab int tab[ 10 ]; 10 elementów 0 1 2 3 4 5 6 7 8 9 int tab[ 10 ]; int * p; p = tab; /* lub p = &tab[ 0 ]*/ 10 elementów tab p 0 1 2 3 4 5 6 7 8 9 Copyright Roman Simiński Strona : 15

Zmienne wskaźnikowe zastosowania w przetwarzaniu tablic Tablice a wskaźniki, 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 tab p p + 0 p + 1 p + 2 p + i 0 1 2 i 7 8 9 tablica + indeks Odwołania równoważne wskaźnik + przesunięcie Wyrażenie p + i jest wyrażeniem wskaźnikowym, wskazuje ono na obiekt oddalony o i obiektów od p. Wartość dodawana do wskaźnika jest skalowana rozmiarem typu obiektu wskazywanego. Copyright Roman Simiński Strona : 16

Zmienne wskaźnikowe zastosowania w przetwarzaniu tablic Tablice a wskaźniki, cd.... 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 ] Wskaźniki to nie to samo, co tablice int tab[ 10 ]; int * p = tab; tab = p; tab++; p = tab + 8; p++; tab p Źle OK 0 1 2 3 4 5 6 7 8 9 int tab[ 10 ] wskaźnik + obszar danych int * p = tab zakotwiczony wskaźnik Nazwa tablicy jest ustalonym (niemodyfikowalnym) wskaźnikiem na pierwszy jej element. Nazw tablic nie wolno modyfikować! Wskaźniki można. Copyright Roman Simiński Strona : 17

Zmienne wskaźnikowe zastosowania w przetwarzaniu tablic Ciekawostka Wiemy, że: tab[ i ] można zapisać tak: *( tab + i ) Skoro dodawanie jest przemienne: *( i + tab ) czy można zapisać tak?: i[ tab ] Przykład: char napis[] = "język c"; puts( napis ); 0[ napis ] = 'J'; /* Zamiast napis[ 0 ] */ 6[ napis ] = 'C'; /* Zamiast napis[ 6 ] */ język c puts( napis ); Copyright Roman Simiński Strona : 18

Zmienne wskaźnikowe zastosowania w przetwarzaniu tablic Dlaczego nie wolno przypisywać tablic? int a[ 10 ]; int b[ 10 ]; b = a; /* Nie wolno przypisywać do siebie tablic */ Gdyby przypisywanie było możliwe... a b Po wykonaniu tej linii: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 b = a; gubimy obszar danych tablicy b! a 0 1 2 3 4 5 6 7 8 9 b 0 1 2 3 4 5 6 7 8 9 Copyright Roman Simiński Strona : 19

Zmienne wskaźnikowe zastosowania w przetwarzaniu tablic Arytmetyka na wskaźnikach nie tylko dla typu char Załóżmy, że zmienne wskaźnikowe zawierają bezpośrednio adresy komórek pamięci. char s[ 6 ] = "Napis"; char * p1 = s; char * p2 = &s[ 4 ] int n = 2; Adresy 200341010 200341011 200341012 200341013 200341014 200341015 200341016 200341017 N a p i s \0 200341011 s 200341011 p1 p2-2 200341015 p2 p2 - p1 + 1 = 200341015-200341011 + 1 = 5 Wskaźnik i wartość całkowita mogą być dodawane i odejmowane, wyrażenie: s + n oznacza adres n-tego elementu od miejsca na które wskazuje s Wskaźniki związane z jedną tablicą można odejmować, jeżeli p1 < p2 to wyrażenie: p2 p1 + 1 określa liczbę elementów pomiędzy p1 a p2 wliczając w to p2 Copyright Roman Simiński Strona : 20

Zmienne wskaźnikowe zastosowania w przetwarzaniu tablic Arytmetyka na wskaźnikach podsumowanie Dozwolone operacje wskaźnikowe to: przypisywanie wskaźników do obiektów tego samego typu, 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 (wskazanie puste NULL) lub porównanie ze wskazaniem pustym. Copyright Roman Simiński Strona : 21

Zmienne wskaźnikowe zastosowania w przetwarzaniu tablic Wskaźniki w akcji metamorfoza funkcji put_string void put_string( char s[] ) int i; for( i = 0; s[ i ]!= '\0'; i++ ) putchar( s[ i ] ); } Wersja początkowa void put_string2( char * s ) for( ; *s!= '\0'; s++ ) putchar( *s ); } Eliminujemy zmienną i void put_string3( char * s ) for( ; *s!= '\0' ; putchar( *s++ ) ) ; } Kompresja iteracji for Najpierw pobierz znak wskazywany przez s, użyj go. *s ++ Potem zwiększ o jeden wartość wskaźnika s będzie on wtedy wskazywał na następny element tablicy. Copyright Roman Simiński Strona : 22

Zmienne wskaźnikowe zastosowania w przetwarzaniu tablic Wskaźniki pod lupą jak działa funkcja put_string4 void put_string4( char * s ) while( *s ) putchar( *s++ ); } Iteracja while nie jest taka zła... Znak '\0' to bajt o wartości 0 Najpierw pobierz znak wskazywany przez s, użyj go. *s ++ Potem zwiększ o jeden wartość wskaźnika s będzie on wtedy wskazywał na następny element tablicy. s Numer przebiegu iteracji while 1 2 3 s++ s++ s++ s++ char imie[ 80 ] = Aga ; put_string4( imie ); imie A g a \0 *s *s *s *s Aga_ Koniec iteracji while Copyright Roman Simiński Strona : 23

Zmienne wskaźnikowe zastosowania w przetwarzaniu tablic Wskaźniki pod lupą metamorfoza funkcji strcpy char s1[ 80 ] = ""; 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 : 24

Zmienne wskaźnikowe zastosowania w przetwarzaniu tablic 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 : 25

Zmienne wskaźnikowe zastosowania w przetwarzaniu tablic 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. Operator = jest lewostronnie łączny ( *d++ = *s++ ) Pobierz znak wskazywany, wykorzystaj go, zwiększ wskaźnik tak, by pokazywał na następny element tablicy.!= '\0' void strcpy4( char * d, char * s ) while( *d++ = *s++ ) ; } Kompresja krok trzeci Znak '\0' to bajt o wartości 0 Copyright Roman Simiński Strona : 26

Zmienne wskaźnikowe zastosowania w przetwarzaniu tablic Wskaźniki pod lupą metamorfoza funkcji strcpy 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 Pozwala to na skrócenie kodu, załóżmy następujące definicje tablic s1, s2, s3: char s1[ 80 ] = ""; 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 : 27

Zmienne wskaźnikowe zastosowania w przetwarzaniu tablic 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'; } 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. Copyright Roman Simiński Strona : 28

Zmienne wskaźnikowe uwagi Wskaźniki a kwalifikator const 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 */ Wersja najbardziej restrykcyjna pod lupą const int * const p; To się nie uda, ustalony wskaźnik należy zainicjować! 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 : 29

Zmienne wskaźnikowe uwagi Ważna sprawa ostrożnie z parametrami wskaźnikowymi! W funkcjach bibliotecznych języka 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 : 30

Zmienne wskaźnikowe uwagi Ważna sprawa ostrożnie z parametrami wskźnikowymi, cd... Niedokładna lektura dokumentacji może sugerować, że funkcji należy użyć tak: char * imie; printf( "Podaj imie: " ); gets( imie ); imie??? Aga gets( imie ) A trzeba np. tak: char imie[ 80 ]; printf( "Podaj imie: " ); gets( imie ); imie Aga gets( imie ) Copyright Roman Simiński Strona : 31

Dynamiczny przydział pamięci Dynamiczna alokacja tablic Dynamiczny przydział pamięci polega na zarezerwowaniu fragmentu pamięci w obszarze pamięci wolnej zwanej stertą, dla obiektu pamięciowego zwanego dynamicznym. char * s = NULL; int n = 30; s = malloc( n * sizeof( char ) ); strcpy( s, " " ); strcat( s, "fajny jest!" ); puts( s ); free( s ); 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ęć. s s s s Sterta a Sterta a Sterta a fajny Pamięć jest! operacyjna Sterta a fajny Pamięć jest! operacyjna Copyright Roman Simiński Strona : 32