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

Podobne dokumenty