METODY I JĘZYKI PROGRAMOWANIA Programowanie w języku C
Transkrypt
METODY I JĘZYKI PROGRAMOWANIA Programowanie w języku C
METODY I JĘZYKI PROGRAMOWANIA Programowanie w języku C notatki z wykładów - Wskaźniki - Operację na łańcuchach znakowych - Struktury 1 Wskaźniki Wskaźnik → jest zmienną, która zawiera adres (wskazanie) początku dowolnego obszaru w pamięci komputera, (np. może być to adres obszaru danych lub adres kodu programu) Ogólna postać definicji wskaźnika: typ_danych ∗ identyfikator wskaźnika ; Najczęściej używane są wskaźniki „zdefiniowane” zawierające adres innej zmiennej. Taki wskaźnik zawiera informację o: • adresie zmiennej w pamięci komputera • typie danych przechowywanych w tej zmiennej Przykłady definicji: int double char char ∗ ∗ ∗ ∗ wskaznik; wsk_liczby; wsk_znaku; tekst; // wskaźnik na zmienną całkowitą // wskaźnik na zmienną rzeczywistą // wskaźnik na pojedynczy znak // wskaźnik na początek łańcucha znaków (na pierwszy znak tego łańcucha) Można również korzystać ze wskaźników „niezdefiniowanych” (anonimowych). Taki wskaźnik zawiera tylko informację o adresie obszaru pamięci (bez określenia typu wskazywanych danych). Definicja takiego wskaźnika ma postać: void ∗ identyfikator wskaźnika ; jest to wskaźnik na „dowolny” ciąg bajtów danych. Ze wskaźnikami i adresami związane są dwa operatory: • operator referencji tego operatora. & zwracający adres zmiennej podanej po prawej stronie • operator dereferencji ∗ identyfikujący obszar wskazywany przez wskaźnik podany po prawej stronie tego operatora. int liczba ; int ∗wskaźnik ; wskaznik = &liczba; ∗wskaźnik = 10; // przypisanie zmiennej wskaźnik // adresu zmiennej liczba // przypisanie 10 zawartości zmiennej // wskazywanej przez wskaźnik // tutaj równoważne liczba = 10 2 Arytmetyka wskaźników Na wskaźnikach mogą być wykonywane następujące operacje: • przypisania ( = ) wsk = wskaznik_zmiennej_lub_obszaru_pamięci ; (w przypadku niezgodności typów konieczne jest dokonanie konwersji typu) • operacje porównania ( ==, !=, <, >, <=, >= ): wsk_1 == wsk_2 wsk_1 < wsk_2 // sprawdzenie czy zmienne zawierają te same adresy // czy zmienna wsk_1 zawiera adres mniejszy // od adresu zawartego w zmiennej wsk_2 • operacje powiększania lub pomniejszania wskaźnika ( +, −, ++, −−, +=, −= ) o liczbę całkowitą (tylko dla wskaźników zdefiniowanych) → powiększenie (pomniejszenie) wskaźnika o wartość N powoduje wyznaczenie adresu przesuniętego o: N ∗ sizeof( typ_zmiennej_wskazywanej ) bajtów w kierunku rosnących (malejących) adresów. np. int ∗x; adresy → . . . 35 36 x x+3 ↓ ↓ 37 38 39 40 41 42 43 44 45 46 ... • operacje odejmowania wskaźników tego samego typu → wyznaczenie „odległości” pomiędzy dwoma adresami w pamięci. ( odległości w sensie N ∗ sizeof ( typ_elementu_wskazywanego ) ) Przykłady zmiennych wskaźnikowych: int ∗ wsk_liczby; // wskaźnik na liczbę typu int int tab_A[10]; // 10-cio elementowa tablica liczb int ( identyfikator tab_A jest stałą równą adresowi pierwszego elementu tablicy o tej samej nazwie tzn. tab_A == &( tab_A[0] ) int ∗ tab_B[10]; // 10-cio elementowa tablica wskaźników na liczby int int ∗ ( tab_C[10] ); // jak wyżej ( int ∗) tab_D[10]; // jak wyżej int (∗tab_E)[10]; // wskaźnik na 10-cio elementową tablicę liczb int 3 PRZYKŁADY: Dostęp do zmiennych za pomocą wskaźników #include <stdio.h> int a ; int b ; int c ; float x ; int∗ wsk_int ; // organizacja zajętości pamięci komputera przy w/w definicjach zmienne → a b c x 678 678 678 6447448 wsk_int 678 30 40 bajty pamięci adresy → . . . 31 32 33 34 35 36 37 38 39 41 ... void main( ) // Różne sposoby zapisu nowej wartości do zmiennej b { // za pomocą wskaźnika na b i sąsiadujące zmienne // Wyświeltlenie adresów zmiennych printf(" \n Adres zmiennej A = %u " , (unsigned) &a ) ; printf(" \n Adres zmiennej B = %u " , (unsigned) &b ) ; printf(" \n Adres zmiennej C = %u " , (unsigned) &c ) ; printf(" \n Adres zmiennej X = %u " , (unsigned) &x ) ; printf(" \n Adres zmiennej WSK_INT = %u " , (unsigned) &wsk_int ) ; a = b = c = 0; printf( " \n A=%d, B=%d, C=%d " , a, b, c ) ; b=10; printf( " \n A=%d, B=%d, C=%d " , a, b, c ) ; wsk_int = &b; ∗wsk_int = 20; printf( " \n A=%d, B=%d, C=%d " , a, b, c ) ; wsk_int = &a; ∗(wsk_int+1) = 30; printf( " \n A=%d, B=%d, C=%d " , a, b, c ) ; ∗(&a + 1) = 40; printf( " \n A=%d, B=%d, C=%d " , a, b, c ) ; ∗(&c − 1) = 50; printf( " \n A=%d, B=%d, C=%d " , a, b, c ) ; ∗( (int∗)&x − 2) = 60; printf( " \n A=%d, B=%d, C=%d " , a, b, c ) ; ∗(int∗)( &x − 1) = 70; printf( " \n A=%d, B=%d, C=%d " , a, b, c ) ; ∗((int∗)&wsk_int − 4) = 80; printf( " \n A=%d, B=%d, C=%d " , a, b, c ) ; ∗(int∗) (&wsk_int − 4) = 90; printf( " \n A=%d, B=%d, C=%d " , a, b, c ) ; } 4 PRZYKŁADY: Dostęp do tablic za pomocą indeksów i/lub wskaźników #include <stdio.h> // Część wspólna przykładów na tej stronie #define ROZMIAR 10 void main(void) { int tab[ ROZMIAR ]; ••• // wczytanie liczby do tablicy // przemnożenie elementu tablicy przez 2 // wyświetlenie elementu tablicy } a) int i; // dostęp za pomocą indeksu for( i = 0; i < ROZMIAR; i++ ) { scanf( ”%d”, &tab[ i ] ); tab[ i ] = 2 ∗ tab[ i ]; // tab[ i ] ∗= 2; printf( ”Tab[ %d ] = %d \n”, i+1 , tab[ i ] ); } b) int i; // dostęp za pomocą adresu i indeksu for( i = 0; i < ROZMIAR; i++ ) { scanf( ”%d”, tab + i ); // &∗(tab+i) == tab+i ∗(tab+i) = 2 ∗ ∗(tab+i); // ∗(tab+i) ∗= 2; printf( ”Tab[ %d ] = %d \n”, i+1 , ∗(tab+i) ); } c) int licznik, ∗wsk; // dostęp za pomocą wskaźnika i licznika for( licznik=0, wsk=tab; licznik < ROZMIAR; licznik++, wsk++ ) { scanf( ”%d”, wsk ); // ∗wsk ∗= 2; ∗wsk = 2∗∗wsk; printf( ”Tab[ %d ] = %d \n”, licznik+1 , ∗wsk ); } d) int ∗wsk; // dostęp za pomocą wskaźnika for( wsk=tab; wsk < tab + ROZMIAR; wsk++ ) { // wsk < &tab[ROZMIAR] ← adres ”końca tablicy” scanf( ”%d”, wsk ); ∗wsk ∗= 2; printf( ”Tab[ %d ] = %d \n”, wsk−tab+1 , ∗wsk ); } 5 Operacje na łańcuchach znakowych Stała tekstowa / łańcuchowa jest tablicą znaków zakończoną znakiem o kodzie: 0 np. stała łańcuchowa: ”Jestem tekstem” . . . 74 101 115 116 101 109 32 116 101 107 115 116 101 109 . . . ’J’ ’e’ ’s’ 8 9 10 11 char ∗ tekst; 0 ... ’t’ ’e’ ’m’ ’ ’ ’t’ ’e’ ’k’ ’s’ ’t’ ’e’ ’m’ ’\0’ . . . 12 13 16 17 20 21 14 15 18 19 22 23 24 // wskaźnik na znak == wskaźnik na początek łańcucha znaków tekst = ”Jestem tekstem” ; // przypisanie zmiennej tekst adresu // początku łańcucha znaków char tekst2[ 100]; // 100-elementowa tablica znakow tekst2 = ”Jestem tekstem” ; // błędne przypisanie !!! memcpy( tekst2, ”Jestem tekstem”, 15 ); // poprawne przypisanie Funkcje operujące na łańcuchach <string.h> // kopiowanie jednego łańcucha do drugiego → wersja tablicowa char ∗ strcpy( char tekst_wyj[ ], char tekst_wej[ ] ) { int i = 0; while( ( tekst_wyj[ i ] = tekst_wej[ i ] ) != ‘\0’ ) i++; return( tekst_wyj ); } // kopiowanie jednego łańcucha do drugiego → wersja wskaźnikowa 1 char ∗ strcpy( char ∗tekst_wyj, char ∗tekst_wej ) { char ∗pocz=tekst_wyj; while( ( ∗tekst_wyj = ∗tekst_wej ) != ‘\0’ ) { tekst_wyj++; tekst_wej++; } return( pocz ); } 6 // funkcja kopiująca łańcuchy − wersja wskaźnikowa 2 char ∗ strcpy( char ∗tekst_wyj, char *tekst_wej ) { char ∗pocz=tekst_wyj; while( ∗tekst_wyj++ = ∗tekst_wej++ ) ; return( pocz ); } Funkcja porównująca teksty: funkcja zwraca wartość: =0 >0 int strcmp ( char ∗tekst_1, char ∗tekst_2 ) ( ang. „string compare” ) <0 gdy gdy gdy tekst_1 < tekst_2 tekst_1 == tekst_2 tekst_1 > tekst_2 int strcmp( char tekst_1[ ], char tekst_2[ ] ) { int i = 0; while( tekst_1[ i ] == tekst_2[ i ] ) if( tekst_1[ i++ ] == ‘\0’ ) return( 0 ); return( tekst_1[ i ] − tekst_2[ i ] ); } // wersja tablicowa int strcmp( char ∗tekst_1, char ∗tekst_2 ) { while( ∗tekst_1 == ∗tekst_2 ) { if( ∗tekst_1 == ‘\0’ ) return( 0 ); tekst_1 = tekst_1 + 1; tekst_2 = tekst_2 + 1 ; } return( ∗tekst_1 − ∗tekst_2 ); } // wersja wskaźnikowa 1 int strcmp( char ∗tekst_1, char ∗tekst_2 ) { for( ; ∗tekst_1 == ∗tekst_2 ; tekst_2++ ) if( ! ∗tekst_1++ ) return( 0 ); return( ∗tekst_1 − ∗tekst_2 ); } // wersja wskaźnikowa 2 7 Inne wybrane funkcje biblioteki string → <string.h> size_t strlen( const char ∗s ) od ang. „ string length ” Funkcja wyznacza i zwraca długość (ilość znaków) łańcucha s (bez znaku ‘\0’) char ∗strcat( char ∗dest, const char ∗src ) od ang. „ string concatenate ” Funkcja dodaje łańcuch src (ang. source) do łańcucha dest (ang. destination) Zwraca wskaźnik na połączony łańcuch (dest) char ∗strchr( const char ∗s, int c ) od ang. „ string char ” Funkcja szuka pierwszego wystąpienia znaku c w podanym łańcuchu s Zwraca wskaźnik na znalezioną pozycję wystąpienia lub adres NULL. char ∗strrchr( char ∗s, int c ) od ang. „ string right char ” Funkcja szuka ostatniego wystąpienia znaku c w podanym łańcuchu s Zwraca wskaźnik na znalezioną pozycję wystąpienia lub adres NULL. char ∗strstr( char ∗s, const char ∗sub ) od ang. „ scans string for substring ” Funkcja szuka pierwszego wystąpienia łańcucha sub w podanym łańcuchu s Zwraca wskaźnik na znalezioną pozycję wystąpienia lub adres NULL. char∗ strupr( char ∗s ) od ang. „ string upper ” Funkcja zamienia zawartość łąńcucha s na duże litery char∗ strlwr( char ∗s ) od ang. „ string lower ” Funkcja zamienia zawartość łąńcucha s na małe litery 8 Przykłady operacji na łańcuchach znaków 1) #include <stdio.h> // przykład zamiany wszystkich liter na duże #include <ctype.h> // standardowe funkcje zamiany łańcuchów na małe lub duże litery // #include <string.h> → char *strlwr(char *s); char *strupr(char *s); char ∗Zamien_Na_Duze( char∗ tekst ) { char ∗wsk = tekst; do // zamiana pojedynczej litery na dużą ∗wsk = toupper(∗wsk ); while(∗wsk++ ); return( tekst ); } //------------------------------------------------------------------------ Zamien_Na_Duze void main( void ) { char ∗lancuch_testowy = "abcdefghijklmnopqrstuvwxyz"; printf( "%s\n" , Zamien_Na_Duze ( lancuch_testowy ) ); } 2) #include <stdio.h> // przykład zamiany pierwszych liter wyrazów #include <ctype.h> char ∗Slowa_Na_Duze( char∗ tekst ) { char ∗wsk = tekst; if( !∗wsk ) // jeżeli tekst pusty to zakończ działanie return(tekst); // zamiana pierwszej litery ∗wsk = toupper( ∗wsk ); while( ∗++wsk ) // jeżeli poprzedzający znak jest spacją if( ∗(wsk-1) == ' ' ) ∗wsk = toupper( ∗wsk ); // zamiana znaku na dużą literę return( tekst ); } //------------------------------------------------------------------------ Slowa_Na_Duze void main( void ) { char ∗lancuch = "to jest probka tekstu "; printf( "%s\n" , Slowa_Na_Duze( lancuch ) ); } 9 3) #include <stdio.h> // funkcja zamieniająca zadane fragmenty tekstu #include <string.h> void Zamien_Fragmenty( char∗ tekst, char∗ stary_wzorzec, char∗ nowy_wzorzec ) { char∗ wsk = tekst; int dlugosc_starego = strlen( stary_wzorzec ); int dlugosc_nowego = strlen( nowy_wzorzec ); do { wsk = strstr( tekst, stary_wzorzec ); if( wsk ) // if( wsk != null ) { // ewentualne zsunięcie lub rozsunięcie tekstu memmove( wsk + dlugosc_nowego , wsk + dlugosc_starego , strlen( wsk + dlugosc_starego ) +1 ); // wpisanie nowego wzorca w przygotowane miejsce memcpy( wsk, nowy_wzorzec, dlugosc_nowego); } } while( wsk ); } //---------------------------------------------------------------------- Zamien_Fragmenty void main( void ) { char tekst[200] = "Ala ma kota a Ola ma Asa"; printf( "Stary tekst: %s\n" , tekst ); Zamien_Fragmenty( tekst, "ma", "miala" ); printf( " Nowy tekst: %s\n" , tekst ); // "Ala miala kota a Ola miala Asa" } UWAGA ! • Zastosowanie w powyższym przykładzie funkcji strcpy zamiast memmove będzie generować błędy (gdy nowy_wzorzec będzie dłuższy od stary_wzorzec) np. strcpy( wsk+dlugosc_nowego, wsk+dlugosc_starego ); utworzy tekst: ” Ala ma ko ko ko ko ko ko ko k” Definicja: char∗ tekst = "Ala ma kota a Ola ma Asa"; jest równoważna: char tekst[24+1] = "Ala ma kota a Ola ma Asa"; Ponieważ podczas zamiany tekst może się wydłużyć (poprzez wstawienie dłuższych fragmentów), więc zmienna tekst powinna być tablicą większą niż długość inicjującego tekstu. } 10 Struktury → Struktury → → najbardziej elastyczny sposób reprezentowania danych w języku C (odpowiednik rekordów w języku Pascal), obiekt złożony z jednej lub kilku zmiennych, które mogą być różnego typu (w przeciwieństwie do tablic), budowa - układ i typy pól składowych - typu strukturalnego są definiowane przez programistę. Przykład: nazwisko imię rok_urodz płeć wzrost stypendium char [30] char [15] int char unsigned char double • Deklarowanie typu strukturalnego struct nazwa_typu ← nazwa tworzonego typu strukturalnego { typ_pola_1 nazwa_pola_1; typ_pola_2 nazwa_pola_2; ← typy i nazwy pól składowych • • • typ_pola_n nazwa_pola_n; }; Przykłady: struct dane_osobowe { char nazwisko[31]; char imie[16]; int rok_urodz; char plec; unsigned char wzrost; double stypendium; }; struct punkt { double x, y; } ; struct okrag { struct punkt srodek_okregu; double promien; }; // struktura zagnieżdżona 11 • Definiowanie (tworzenie) zmiennych strukturalnych struct nazwa_typu nazwa_tworzonej_zmiennej; Przykład: struct dane_osobowe student; struct okrag figura_1; • Można połączyć definicję zmiennej z inicjacją jej wartości. Np. // { Nazwisko, Imie, Rok_urodz, Plec,Wzrost, Stypendium } struct dane_osobowe student = { ”Kowalski”, ”Jan”, 1970,‘M’, 175, 320.0 }; // { { x , y } , promień } struct okrag figura_1 = { { 10.0, 15.75 } , 50.5 }; • Można połączyć deklarację typu strukturalnego z definicją zmiennych struct nazwa_typu { typ_pola_1 nazwa_pola_1; typ_pola_2 nazwa_pola_2; • • • typ_pola_n nazwa_pola_n; } nazwa_ zmiennej ; ← nazwę typu można pominąć ← typy i nazwy pól składowych ← nazwa definiowanej zmiennej Np. struct // pominięto nazwę typu { char nazwisko[31]; char imie[16]; int rok_urodz; char plec; unsigned char wzrost; double stypendium; } student_1, student_2; // definicja dwóch zmiennych strukturalnych // jednoczesna deklaracja typów i definicja zmiennych struct okrag { struct punkt // bezpośrednia definicja struktury zagnieżdżonej { double x, y; } srodek_okregu; double promien; } figura_1; 12 • Odwoływanie się do elementów struktury → za pomocą kropki Przykłady: student . wzrost = 180 ; student_1 . wzrost = student_2 . wzrost; figura_1 . promien = 50; figura_1 . srodek_okregu . x = 15; scanf( ”%lf” , &student . stypendium ); scanf( ”%s” , student . nazwisko ); strcpy( student . imie, ”Tomasz” ); // przypisanie wartości // wczytanie z klawiatury // wczytanie łańcucha znaków W pierwotnej wersji języka C (Kernigham, Ritchie) jedynymi dozwolonymi operacjami na strukturze były pobranie adresu (&) oraz działania na składowych. W wersji ANSI-C (Turbo C++) możliwe jest bezpośrednie przypisanie struktur, struktura może być również argumentem i wynikiem zwracanym przez funkcję. student_1 = student_2; // bezpośrednie przypisanie struktur memcpy( &student_1, &student_2, sizeof( student_1 ) ); student_1 . nazwisko = student_2 . nazwisko strcpy( student_1 . nazwisko, student_2 . nazwisko ); // funkcja zwracająca daną strukturalną struct dane_osobowe Wczytaj_Dane_Osobowe( void ) { struct dane_osobowe nowe_dane; printf( ”Podaj nazwisko: ” ); scanf( ”%s” , nowe_dane . nazwisko ); • • • return( nowe_dane ); } // funkcja której argumentem jest zmienna strukturalna void Wyswietl_Dane_Osobowe( struct dane_osobowe osoba ) { printf( ”Nazwisko: %s\n” , osoba . nazwisko ); printf( ” Imie: %s\n” , osoba . imie ); • • • } 13 • Struktura jako element tablicy struct dane_osobowe baza[ 100 ]; // definicja tablicy struktur struct dane_osobowe mala_baza[ 2 ] = { { ”Kowalski”, ”Jan”, 1970,‘M’, 175, 320.0 } , { ”Nowak”, ”Tomasz”, 1965,‘M’, 180, 50.0 } }; struct okrag figury[ 3 ] = { {{15, 10}, 50} , {{0, 0}, 10} , {{30, -70}, 8} }; baza[ 2 ] . wzrost = 150; // przykładowe operacje na tablicach struktur figury[ 0 ] . srodek_okregu . y = 50; for( int i = 0; i < 100; i++ ) { printf( ”Podaj nazwisko: ” ); scanf( ”%s” , baza[ i ] . nazwisko ); • • • printf( ”Podaj stypendium: ” ); scanf( ”%lf” , &(baza[ i ] . stypendium) ); } • Wskaźniki do struktur - dostęp do struktury za pośrednictwem adresu struct dane_osobowe student; struct dane_osobowe ∗wsk_os; student . wzrost = 180; wsk_os = &student; (∗wsk_os) . wzrost = 180; wsk_os −> wzrost = 180; // bezposrednie przypisanie do pola struktury // pośrednie przypisanie poprzez wskaźnik // j.w. z nowym operatorem strzałki // funkcja porządkująca roznąco zawartości dwu zmiennych void Uporzadkuj( struct dane_osobowe ∗wsk_os_1, struct dane_osobowe ∗wsk_os_2 ) { struct dane_osobowe bufor; if( wsk_os_1−>wiek > wsk_os_2−>wiek ) { bufor = ∗wsk_os_1; ∗wsk_os_1 = ∗wsk_os_2; wsk_os_2 = bufor } } 14