Wprowadzenie do programowania w języku C

prac.us.edu.pl

Wprowadzenie do programowania w języku C

Podstawy i języki programowaniaJęzyk CZanim poznamy zmienne wskaźnikowe...Zmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikiMotywacjaW języku C intensywnie wykorzystuje się l-wartości oparte na zmiennychwskaźnikowych oraz na wyrażeniach te zmienne zawierających.Dokładne opanowanie zasad posługiwania się wskaźnikami jest niezbędne doefektywnego i sprawnego programowania w C i C++. Tej umiejętności nie możnapominąć, przeskoczyć lub zostawić na później.Nie oszukujmy się — ten, kto nie opanuje zasad posługiwania się wskaźnikami nigdy niebędzie prawdziwym, profesjonalnym programistą wykorzystującym język C lub C++.Copyright © Roman Simiński Strona : 3


Podstawy i języki programowaniaJęzyk CZmienne wskaźnikowe ― koncepcjaZmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikiTrzy stany zmiennej wskaźnikowejZmienna wskaźnikowa wskazuje na konkretny obiekt w pamięciPamięć operacyjnaZmienna wskaźnikowaObiekt wskazywany OKZmienna wskaźnikowa nie wskazuje na żaden obiektPamięć operacyjnaZmienna wskaźnikowa OKZmienna wskaźnikowa wskazuje na nie wiadomo coPamięć operacyjnaZmienna wskaźnikowa? KiepskoCopyright © Roman Simiński Strona : 5


Podstawy i języki programowaniaJęzyk CZmienne wskaźnikowe ― koncepcjaZmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikiCo zawiera zmienna wskaźnikowa?Zwykle przyjmuje się, że zmienna wskaźnikowa zawiera w sobie adres obiektuwskazywanego.Pamięć operacyjnaZmienna wskaźnikowa345fa012hObiekt wskazywanyAdres: 345fa012hJednak zmienna wskaźnikowa nie musi w sobie zawierać adresu bezpośredniego, jejzawartość może, w pewnej realizacji kompilatora, zawierać inną informację, pozwalającąna precyzyjne i jednoznaczne zidentyfikowanie położenia obiektu w pamięci.PrzykładW 16-to bitowych realizacjach kompilatorów firmy Borland:zmienna wskaźnikowa zawiera przesunięcie (ang. offset) obiektu względem początkusegmentu gdy wskaźniki są „krótkie” (odwołania wewnątrz segmentu),zmienna wskaźnikowa zawiera adres segmentu i przesunięcie obiektu gdy wskaźnikisą „długie” (odwołania międzysegmentowe).Copyright © Roman Simiński Strona : 6


Podstawy i języki programowaniaJęzyk CZmienne wskaźnikowe ― podstawowe operacjeZmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikiPrzypisywanie wartości zmiennym wskaźnikowymint i = 10;int * pi = NULL;. . .pi = &i;iPamięćoperacyjna. . .10O biektwskazywanypiPrzypisanie pod lupą. . .pi = & i ;Od momentu tegoprzypisania, pi wskazujezmienną i, umożliwiającrealizację dowolnych operacjina tej zmiennej.Jednoargumentowy operator & budujewyrażenie wskaźnikowe lokalizującezmienną w pamięci operacyjnej. Argumentmusi być l-wartością nie odnoszącą się doobiektu register ani pola bitowego.Wyrażeniewskaźnikowelokalizującezmienną i wpamięci.Copyright © Roman Simiński Strona : 9


Podstawy i języki programowaniaJęzyk CZmienne wskaźnikowe ― podstawowe operacjeZmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikiOdwoływanie sie do obiektu wskazywanegoint i = 10;int * pi = NULL;. . .pi = &i;*pi = 20;ipiPamięćoperacyjna. . .20*piOdwołanie pod lupą. . .* pi = 20 ;Ten zapis oznacza obiekt wskazywanyprzez pi. Zapis *pi może wystąpićwszędzie tam, gdzie może wystąpić i.Zmienna pi jest aliasem, linikiem doobiektu i.Jednoargumentowy operatoradresowania pośredniego *daje w wyniku obiektwskazywany przez argumentpi.Dowolne wyrażenietypu zgodnego ztypem obiektuwskazywanego.Copyright © Roman Simiński Strona : 10


Podstawy i języki programowaniaJęzyk CZmienne wskaźnikowe ― zastosowaniaZmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikiRealizacja przekazywania parametrów przez zmienną z użyciem wskaźnikówvoid inc( int * pi ){*pi = *pi + 1;}. . .int i = 10;inc( &i );ipiPamięćoperacyjna. . .1110. . .*pi=*pi+1void getint( int * ptr ){char s[ 80 ];gets( s );*ptr = atoi( s );}. . .int liczba;getint( &liczba );liczbaptrPamięćoperacyjna. . .123. . .W języku C wykorzystuje się parametry będące wskaźnikami do realizacji przekazywaniaparametró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 5b 10s 5int a = 5, b = 10;. . .printf( "a=%d b=%d", a, b );zamien( &a, &b );printf( "a=%d b=%d", a, b );a=5 b=10a=10 b=5Copyright © Roman Simiński Strona : 12


Podstawy i języki programowaniaJęzyk CZmienne wskaźnikowe ― zastosowaniaZmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikiDynamiczny przydział pamięciDynamiczny przydział pamięci polegana zarezerwowaniu fragmentu pamięciw obszarze pamięci wolnej zwanejstertą, dla obiektu pamięciowegozwanego dynamicznym.pPamięć operacyjnaStertaaint * p = NULL;p = malloc( sizeof( int ) );. . .*p = 10;. . .free( p );pp10Pamięć operacyjnaStertaPamięć operacyjnaaStertaaPrzydziela się fragmento określonym rozmiarze, jedynysposób na odwoływanie się do takiegoobiektu to wykorzystanie wskaźnika.pPamięć operacyjnaStertaaStrona :Copyright © Roman Simiński 13


Podstawy i języki programowaniaJęzyk CZmienne wskaźnikowe ― zastosowania w przetwarzaniu tablicTablice a wskaźnikiZmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikiNazwa tablicy jest interpretowana jako ustalony wskaźnik na jej początek (pierwszyelement).tabint tab[ 10 ];10 elementów0 1 2 3 4 5 6 7 8 9int tab[ 10 ];int * p;. . .p = tab; /* lub p = &tab[ 0 ]*/10 elementówtabp0 1 2 3 4 5 6 7 8 9Copyright © Roman Simiński Strona : 15


Podstawy i języki programowaniaJęzyk CZmienne wskaźnikowe ― zastosowania w przetwarzaniu tablicTablice a wskaźniki, cd. ...Zmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikitab[ 0 ] = 5tab[ 1 ] = 1tab[ 2 ] = 10. . .tab[ i ] = 22Odwołania równoważne*p = 5*( p + 1 ) = 1*( p + 2 ) = 10. . .*( p + i ) = 22tabp p + 0 p + 1 p + 2 p + i0 1 2 i 7 8 9tablica + indeksOdwołania równoważnewskaźnik + przesunięcieWyrażenie p + i jest wyrażeniem wskaźnikowym, wskazuje ono na obiektoddalony o i obiektów od p.Wartość dodawana do wskaźnika jest skalowana rozmiarem typu obiektuwskazywanego.Copyright © Roman Simiński Strona : 16


Podstawy i języki programowaniaJęzyk CZmienne wskaźnikowe ― zastosowania w przetwarzaniu tablicTablice a wskaźniki, cd. ...Każde odwołanie:Zmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikitab[ 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 tabliceint tab[ 10 ];int * p = tab;tab = p;tab++;tabp Źle0tab[*1 210tab3→→4wskaźnikzakotwiczony5 6+ obszar7wskaźnik8danych9int ]int p =p = tab + 8;p++; OKNazwa tablicy jest ustalonym (niemodyfikowalnym)wskaźnikiem na pierwszy jej element. Nazw tablicnie wolno modyfikować! Wskaźniki można.Copyright © Roman Simiński Strona : 17


Podstawy i języki programowaniaJęzyk CZmienne wskaźnikowe ― zastosowania w przetwarzaniu tablicDlaczego nie wolno przypisywać tablic?int a[ 10 ];int b[ 10 ];b = a; /* Nie wolno przypisywać do siebie tablic */Zmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikiGdyby przypisywanie było możliwe...abPo wykonaniu tej linii:0 1 2 3 4 5 6 7 8 90 1 2 3 4 5 6 7 8 9b = a;gubimy obszar danych tablicy b!a0 1 2 3 4 5 6 7 8 9b0 1 2 3 4 5 6 7 8 9Copyright © Roman Simiński Strona : 19


Podstawy i języki programowaniaJęzyk CZmienne wskaźnikowe ― zastosowania w przetwarzaniu tablicArytmetyka na wskaźnikach — nie tylko dla typu charZmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikiZałóżmy, że zmienne wskaźnikowezawierają bezpośrednio adresy komórekpamięci.char s[ 6 ] = "Napis";char * p1 = s;char * p2 = &s[ 4 ]int n = 2;Adresy. . .200341010200341011200341012200341013200341014200341015200341016200341017Pamięć operacyjna. . .Napis\0200341011 s200341011 p1p2 - 2200341015 p2. . .. . .p2 - p1 + 1 = 200341015 - 200341011 + 1 = 5Wskaźnik i wartość całkowita mogą być dodawane i odejmowane, wyrażenie:s + noznacza adres n-tego elementu od miejsca na które wskazuje sWskaźniki związane z jedną tablicą można odejmować, jeżeli p1 < p2 to wyrażenie:p2 – p1 + 1określa liczbę elementów pomiędzy p1 a p2 wliczając w to p2Copyright © Roman Simiński Strona : 20


Podstawy i języki programowaniaJęzyk CZmienne wskaźnikowe ― zastosowania w przetwarzaniu tablicArytmetyka na wskaźnikach — podsumowanieDozwolone operacje wskaźnikowe to:przypisywanie wskaźników do obiektów tego samego typu,dodawanie lub odejmowanie wskaźnika i liczby całkowitej,Zmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikiodejmowanie 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 zewskazaniem pustym.Copyright © Roman Simiński Strona : 21


Podstawy i języki programowaniaJęzyk CZmienne wskaźnikowe ― zastosowania w przetwarzaniu tablicWskaźniki w akcji — metamorfoza funkcji put_stringZmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikivoid put_string( char s[] ){int i;for( i = 0; s[ i ] != '\0'; i++ )putchar( s[ i ] );}Wersja początkowavoid put_string2( char * s ){for( ; *s != '\0'; s++ )putchar( *s );}Eliminujemy zmienną ivoid put_string3( char * s ){for( ; *s != '\0' ; putchar( *s++ ) );}„Kompresja” iteracji forNajpierw pobierz znakwskazywany przez s, użyjgo.*s++Potem zwiększ o jeden wartośćwskaźnika s — będzie on wtedywskazywał na następny element tablicy.Copyright © Roman Simiński Strona : 22


Podstawy i języki programowaniaJęzyk CZmienne wskaźnikowe ― zastosowania w przetwarzaniu tablicWskaźniki pod lupą — jak działa funkcja put_string4Zmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikivoid put_string4( char * s ){while( *s )putchar( *s++ );}Iteracja while nie jest taka zła...Znak '\0' to bajt o wartości 0Najpierw pobierz znakwskazywany przez s, użyjgo.*s++Potem zwiększ o jeden wartośćwskaźnika s — będzie on wtedywskazywał na następny element tablicy.sNumer przebiegu iteracji while1 2 3s++ s++ s++ s++char imie[ 80 ] = ”Aga” ;. . .put_string4( imie );imieA g a \0. . .*s *s *s *sAga_Koniec iteracjiwhileCopyright © Roman Simiński Strona : 23


Podstawy i języki programowaniaJęzyk CZmienne wskaźnikowe ― zastosowania w przetwarzaniu tablicWskaźniki pod lupą — metamorfoza funkcji strcpyZmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikichar s1[ 80 ] = "Język C";char s2[ 20 ];. . .strcpy( s2, s1 );Wersja początkowaPrzypomnienie jak to działasi++s1. . .J ę z y k C \0 . . .0 1 2 3 4 5 679ds2J ę z y k C \0 . . .0 1 2 3 4 5void strcpy( char d[], char s[] ){int i;for( i = 0; s[ i ] != '\0'; i++ )d[ i ] = s[ i ];d[ i ] = '\0';}619Copyright © Roman Simiński Strona : 24


Podstawy i języki programowaniaJęzyk CZmienne wskaźnikowe ― zastosowania w przetwarzaniu tablicWskaźniki pod lupą — metamorfoza funkcji strcpyZmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikivoid strcpy1( char * d, char * s ){while( *s != '\0' ){*d = *s;d++;s++;}*d = '\0';}Odwołania wskaźnikoweTo właściwie nie wiele zmienia,poza wyeliminowaniem zmiennej ivoid strcpy2( char * d, char * s ){while( *s != '\0' )*d++ = *s++;*d = '\0';}„Kompresja” — krok pierwszyCopyright © Roman Simiński Strona : 25


Podstawy i języki programowaniaJęzyk CZmienne wskaźnikowe ― zastosowania w przetwarzaniu tablicWskaźniki pod lupą — metamorfoza funkcji strcpyZmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikivoid strcpy3( char * d, char * s ){while( ( *d++ = *s++ ) != '\0' );}„Kompresja” — krok drugiWartością tego wyrażenia jest znak(bajt) przepisany z obszaruwskazywanego przez s do obszaruwskazywanego przez d.Operator = jest lewostronnie łączny( *d++ = *s++ )Pobierz znak wskazywany,wykorzystaj go, zwiększ wskaźniktak, by pokazywał na następnyelement tablicy.!= '\0'void strcpy4( char * d, char * s ){while( *d++ = *s++ );}„Kompresja” — krok trzeciZnak '\0' to bajt o wartości 0Copyright © Roman Simiński Strona : 26


Podstawy i języki programowaniaJęzyk CZmienne wskaźnikowe ― zastosowania w przetwarzaniu tablicWskaźniki pod lupą — metamorfoza funkcji strcpyZmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikiCzęsto spotykaną praktyką w funkcjach bibliotecznych jest udostępnianie wskaźnika dotablicy (jednej z tablic) będącej parametrem:char * strcpy5( char * d, char * s ){while( *d++ = *s++ );return d;}Tablica d jako rezultat funkcjiPozwala to na skrócenie kodu, załóżmy następujące definicje tablic s1, s2, s3:char s1[ 80 ] = "Język 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 : 27


Podstawy i języki programowaniaJęzyk CZmienne wskaźnikowe ― zastosowania w przetwarzaniu tablicWskaźniki pod lupą — metamorfoza funkcji strcpyZmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikiW 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łowejdozwolona, choć merytorycznieniepoprawnaAby temu zaradzić, można zadeklarować parametr reprezentujący tablicę źródłową wspecyficzny sposób:char * strcpy6( char * d, const char * s ){*s = 'A';. . .}Aby funkcja nie mogła zmodyfikować parametru przekazanego za pośrednictwemwskaź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


Podstawy i języki programowaniaZmienne wskaźnikowe ― uwagiJęzyk CZmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikiWskaźniki a kwalifikator constMoż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 obiektuNiedozwolone, odwołanie modyfikujące obiektNiedozwolone, odwołanie modyfikujące wskaźnikCopyright © Roman Simiński Strona : 29


Podstawy i języki programowaniaZmienne wskaźnikowe ― uwagiJęzyk CZmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikiWażna sprawa — ostrożnie z parametrami wskaźnikowymi!W funkcjach bibliotecznych języka C stałą praktyką jest deklarowanie parametrówtablicowych z wykorzystaniem wskaźników, np:int strlen( char * s );zamiastint 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):Prototypechar *gets(char *s);DescriptionGets a string from stdin.gets collects a string of characters terminated by a new line from the standard input stream stdin andputs 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 itencounters a new line; everything up to the new line is copied into s.Copyright © Roman Simiński Strona : 30


Podstawy i języki programowaniaZmienne wskaźnikowe ― uwagiJęzyk CZmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikiWaż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 );Pamięć operacyjnaimie??? AgaPamięć operacyjnagets( imie )A trzeba np. tak:char imie[ 80 ];printf( "Podaj imie: " );gets( imie );Pamięć operacyjnaimieAgaPamięć operacyjnagets( imie )Copyright © Roman Simiński Strona : 31


Podstawy i języki programowaniaDynamiczny przydział pamięciJęzyk CZmienne wskaźnikowe — koncepcja, zastosowania, sztuczki i trikiDynamiczna alokacja tablicDynamiczny przydział pamięci polegana zarezerwowaniu fragmentu pamięciw obszarze pamięci wolnej zwanejstertą, dla obiektu pamięciowegozwanego dynamicznym.char * s = NULL;int n = 30;s = malloc( n * sizeof( char ) );. . .strcpy( s, "Język C " );strcat( s, "fajny jest!" );puts( s );. . .free( s );Na tablicach alokowanych dynamiczniena stercie, można wykonywać takiesame operacje, jak na tablicachstatycznych. Należy tylko uważnieprzydzielać i zwalniać pamięć.ssssPamięć operacyjnaStertaaPamięć operacyjnaStertaaPamięć operacyjnaPamięć operacyjnaStertaaJęzyk C fajny Pamięć jest! operacyjnaPamięć operacyjnaStertaaJęzyk C fajny Pamięć jest! operacyjnaCopyright © Roman Simiński Strona : 32

More magazines by this user
Similar magazines