METODY I JĘZYKI PROGRAMOWANIA Programowanie w języku C

Transkrypt

METODY I JĘZYKI PROGRAMOWANIA Programowanie w języku C
Zmienne dynamiczne w języku C
Pamięć komputera, dostępna dla programu, dzieli się na cztery obszary:
• kod programu,
• dane statyczne ( np. stałe i zmienne globalne programu),
• dane automatyczne (zmienne lokalne funkcji - tworzone i usuwane
automatycznie przez kompilator na stosie (ang. stack))
• dane dynamiczne (zmienne, tworzone i usuwane w trakcie pracy programu w
obszarze tzw. STERTY (ang. heap))
Zmienne dynamiczne
METODY I JĘZYKI PROGRAMOWANIA
→
są to zmienne tworzone przez programistę w trakcie
pracy programu (na stercie)
→
dostęp do takiej zmiennej możliwy jest jedynie poprzez
jej adres w pamięci (przechowywany w zmiennej
wskaźnikowej).
W języku „C” do dynamicznego przydzielania pamięci (tworzenia zmiennych
dynamicznych) służyły specjalne funkcje z biblioteki < alloc.h >
void ∗malloc( size_t rozmiar );
// przydział bloku o zadanej wielkosci
void ∗calloc( size_t il_elementow, size_t rozmiar);
Programowanie w języku C
void free( void ∗wskaznik);
notatki z wykładów
unsigned coreleft( void );
- Zmienne dynamiczne w języku C
- Tablice wielowymiarowe
- Dostęp do dowolnego obszaru pamięci
operacyjne
- Wskaźniki funkcji
// przydział tablicy
// zwolnienie wskazywanego obszaru
// sprawdzenie wolnego miejsca na stercie
np. void main( void )
{
int ∗wsk;
// zmienna wskaźnikowa do zapamiętania adresu liczby int
• • •
wsk = (int∗) malloc( sizeof(int) );
// przydzielenie pamięci na liczbę int
if( wsk == NULL )
{ printf( ”Błąd przydziału pamięci” ); return;
• • •
// przykładowe operacje na dynamicznej liczbie int
∗wsk = 10;
∗wsk ∗= 2;
printf( ”%d”, ∗wsk );
scanf( ”%d”, wsk );
• • •
free( wsk );
// zwolnienie pamięci przed zakończeniem programu
}
1
2
Przykład operacji na strukturze utworzonej dynamicznie:
Przykład dynamicznej tablicy o ilości elementów wczytywanej z klawiatury:
int rozmiar_tablicy;
double ∗tablica_liczb;
printf( ”Ile liczb chcesz wprowadzić: ” );
scanf( ”%d”, &rozmiar_tablicy );
if( tablica_liczb = (double∗) calloc( rozmiar_tablicy, sizeof(double) ) )
{
for( int i = 0; i < rozmiar_tablicy, i++ );
∗( tablica_liczb+i ) = 100;
// tablica_liczb[ i ] = 100;
• • •
free( tablica_liczb );
}
// ( typ <struct dane_osobowe> był zdefniniowany na poprzednim wykładzie )
struct dane_osobowe ∗wsk_osoby;
wsk_osoby = (struct dane_osoby∗) malloc( sizeof(struct dane_osoby) );
if( wsk_osoby )
// if( wsk_osoby != NULL )
{
printf( ”Podaj nazwisko: ” );
scanf( ”%s” ,wsk_osoby −> nazwisko );
• • •
printf( ”Podaj stypendium: ” );
scanf( ”%lf” , &(wsk_osoby −> stypendium) );
• • •
free( wsk_osoby );
}
W języku „C++” do dynamicznego przydzielania pamięci wygodniej jest wykorzystywać
operatory new i delete :
Operacje na dynamicznej tablicy struktur::
<wskaźnik_na_obiekt> = new <typ_obiektu> [parametry_inicjacyjne] ;
delete <wskaźnik_na_obiekt> ;
int rozmiar_tablicy;
struct dane_osobowe ∗baza;
np.
int∗ wsk ;
// wskaźnik na zmienną typu całkowitego
wsk = new int ;
// utworzenie nowego obiektu (nowej zmiennej int)
if( wsk != NULL )
{
∗wsk = 10 ;
// przypisanie wartości (poprzez wskaźnik)
printf( ”%d” , ∗wsk );
// wydrukowanie zawartości zmiennej dynam.
• • •
delete wsk ;
}
Porównanie utworzenia zwykłej tablicy i tablicy dynamicznej:
const ROZMIAR_TABLICY = 100;
double zwykła_tablica[ ROZMIAR_TABLICY ];
int rozmiar_tablicy;
cout << ”Ile liczb chcesz wprowadzić: ” ;
cin >> rozmiar_tablicy ;
printf( ”Ile liczb chcesz wprowadzić: ” );
scanf( ”%d”, &rozmiar_tablicy );
baza = (dane_osobowe∗) calloc( rozmiar_tablicy, sizeof(dane_osobowe) );
if( baza == NULL )
{
printf( ”Błąd przydziału pamięci” );
exit;
}
• • •
// usunięcie zmiennej dynam. (zwolnienie pamięci)
// wczytanie danych kilku osób do utworzonej dynamicznej tablicy
for( int i = 0; i < rozmiar_tablicy, i++ );
{
printf( ”Podaj nazwisko: ” );
scanf( ”%s” , ( baza+i ) −> nazwisko );
printf( ”Podaj stypendium: ” );
scanf( ”%lf” , &( ( baza+i ) −> stypendium) );
• • •
}
• • •
double ∗tablica_dynamiczna;
tablica_dynamiczna = new double[ rozmiar_tablicy ];
• • •
if( baza != NULL )
free( baza );
// zwolnienie pamięci przed zakończeniem programu
delete [ ] tablica_dynamiczna;
3
4
int tab[ 3 ][ 5 ] ;
int i, j ;
TABLICE WIELOWYMIAROWE
Zapis indeksowy
int tab[ 3 ][ 5 ] ;
int i, j ;
for( i=0 ; i<3 ; i++ )
for( j=0 ; j<5 ; j++ )
{
printf( ” TAB[ %d , %d ]= ”, i, j );
scanf( ”%d” , &tab[ i ] [ j ] );
}
tab[ i ][ j ] == ∗( ∗(tab + i) + j)
np. tab[ 0][ 0] == ∗(∗(tab+0)+0) == ∗∗tab
Dlaczego tab jest typu int (∗)[5] a nie typu int ∗∗ ?
int ∗∗tab2 → Wskaźnik na wskaźnik zmiennej:
// cout << ” TAB[ ” << i << ”,” << j << ”] =”
// cin >> tab[ i ] [ j ]
Reprezentacja tablicy int tab[3][5] w pamięci komputera:
int ∗∗tab2 → Wskaźnik na tablicę wskaźników tablice:
Operacje na tablicy dwuwymiarowej w zapisie wskaźnikowym
int tab[ 3 ][ 5 ] ;
int i, j ;
for( i=0 ; i<3 ; i++ )
for( j=0 ; j<5 ; j++ )
{
printf( ” TAB[ %d , %d ]= ”, i, j );
scanf( ”%d” , ∗(tab + i) + j );
}
Operacje na tablicy dwuwymiarowej bez wykorzystywania indeksów
liczbowych:
int tab[ 3 ][ 5 ] ;
int (∗wsk_w) [ 5 ] ;
// wskaźnik na wiersz tzn. na 5-cio elementową tablicę
int
int∗ wsk_k ;
// wskaźnik na kolumnę tzn. na liczbę int
for( wsk_w = tab ; wsk_w < tab + 3 ; wsk_w++ )
for( wsk_k = ∗wsk_w ; wsk_k < ∗wsk_w + 5 ; wsk_k++ )
{
printf( ” TAB[ %d , %d ]= ”, wsk_w − tab, wsk_k − ∗wsk_w );
scanf( ”%d” , wsk_k );
}
// przykładowy program tworzący strukturę danych j.w.
int tab_k_0 [ 5 ] ;
int tab_k_1 [ 5 ] ;
int tab_k_2 [ 5 ] ;
int∗ tab2 [ 5 ] = { tab_k_0, tab_k_1, tab_k_2 } ;
// lub inaczej:
int∗ tab_w [ 5 ] = { tab_k_0, tab_k_1, tab_k_2 } ; int∗∗ tab2 = tab_w ;
// zapis liczby 111 do wybranego elementu tablicy tab2
∗(∗(tab2+2) + 3) = 111 ;
tab2 [ 2 ][ 3 ] = 111 ;
// zamiana miejscami wierszy o indeksach 0 i 2
int∗ wsk_pom ;
wsk_pom = ∗tab2 ;
∗tab2 = ∗(tab2 + 2) ;
∗(tab2 + 2) = wsk_pom ;
5
// wsk_pom = ∗(tab2 + 0) ;
// ∗(tab2 + 0) = ∗(tab2 + 2) ;
6
DOSTĘP DO DOWOLNEGO OBSZARU PAMIĘCI OPERACYJNEJ
Dostęp do zmiennej poprzez nazwę lub wskaźnik:
Obszar pamięci widziany jako tablice o różnej strukturze:
double wzrost ;
double ∗wysokość ;
wysokość = &wzrost ;
wzrost = 170 ;
// równoważne
∗wysokość = 170 ;
Dostęp do zmiennej dynamicznej poprzez wskaźnik:
double ∗wysokość ;
wysokość = new double ;
sizeof(double) ) ;
int i ;
double A[ 2 ];
for( i=0 ; i < 2; i++ )
A[ i ] = 0 ;
// wysokość = (double∗) malloc(
long∗ B = (long∗) A ;
for( i=0 ; i < 4; i++ )
B[ i ] = 0 ;
∗wysokość = 170 ;
Dostęp do dowolnych bajtów pamięci:
int∗ C = (int∗) A ;
for( i=0 ; i < 8; i++ )
C[ i ] = 0 ;
double far ∗wysokość ;
wysokość = (double far∗) adres_początku_8_bajtów_w_pamięci ;
char∗ D = (char∗) A ;
for( i=0 ; i < 16; i++ )
D[ i ] = 0 ;
// na komputerach typu IBM PC:
wysokość = (double far∗) MK_FP( segment_adresu , offset_adresu ) ;
∗wysokość = 170 ;
Bezpośredni zapis do pamięci ekranu (wypełnianie spacjami, biały znak, czarne
tło):
Np. bezpośredni zapis znaku do pamięci obrazu:
// 4000-elementowa tablica znaków (25 ∗ 80 ∗ 2)
char ekran_A = (char∗) MK_FP( 0xB800 , 0x0000 ) ;
for( i=0 ; i < 4000; i+=2 )
{ ekran_A[ i ] = 32 ; ekran_A[ i+1 ] = 15; }
char far ∗pierwszy_znak ;
pierwszy_znak = (char far∗) MK_FP( 0xB800 , 0x0000 ) ;
∗pierwszy_znak = ‘A’ ;
// równoważne:
gotoxy( 1, 1 );
cprintf( ”A” );
// 2000-elementowa tablica word (25 ∗ 80 ∗ 1)
unsigned int ekran_B = (unsigned int∗) MK_FP( 0xB800 , 0x0000 ) ;
for( i=0 ; i < 2000; i++ )
ekran_B[ i ] = 32 + 15∗256 ;
Wyświetlanie bajtów kodujących zmienne:
double zmienna = 111;
unsigned char ∗wsk_bajtu ;
wsk_bajtu = (unsigned char ∗) &zmienna ;
for( int i=0 ; i < sizeof(double) ; i++)
cout << ∗wsk_bajtu++ ;
// dwuwymiarowa tablica 25-wierszy i 80-kolumn
unsigned int (∗ekran_C) [ 80 ] ;
ekran_C = (unsigned int (∗) [ 80 ]) MK_FP( 0xB800 , 0x0000 ) ;
for( int w=0 ; w < 25; w++ )
for( int k=0 ; k < 80; k++ )
ekran_C[ w ][ k ] = 32 + 15∗256 ;
7
8
funkcja qsort
WSKAŹNIKI DO FUNKCJI
→ nazwa funkcji jest stałą równą adresowi kodu funkcji w pamięci komputera
(analogicznie jak nazwa tablicy jest stałą równą adresowi tablicy),
#include <conio.h>
...
clrscr ;
// podanie samej nazwy funkcji jest równoważne podaniu adresu
// i nie powoduje żadnej akcji (podobnie jak polecenie 10 ; )
clrscr() ;
<stdlib.h>
→ implementacja algorytmu sortowania szybkiego (ang. quick sort)
pozwalająca sortować tablice obiektów dowolnego typu według zadanego kryterium
(funkcji definiującej relację porządku)
prototyp funkcji:
// nazwa funkcji z nawiasami () jest traktowana jako „wywolanie
// funkcji” tzn. polecenie wykonania fragmentu kodu umieszczo// nego pod podanym adresem
void qsort(
void ∗base, // adres poczatku sortowanego obszaru
size_t nelem, // rozmiar pojedynczego elementu
size_t width, // ilosc sortowanych elementów
int (∗fcmp)( void ∗, void ∗) //funkcja porównująca
→ możliwość pośredniego dostępu do funkcji (poprzez zmienną zawierającą adres /
wskazanie na funkcję). Ogólna postać definicji wskaźnika funkcji:
);
typ_zwracany_przez_funkcję ( ∗nazwa_zmiennej ) ( parametry_funkcji );
Przykład sortowania tablicy liczb całkowitych
void clrscr( void );
// prototyp funkcji clrscr()
void (∗nowy_clrscr) ( void );
// definicja zmiennej wskazującej na funkcję
...
nowy_clrscr = clrscr;
// przypisanie zmiennej nowy_clrscr adresu clrscr
...
clrscr();
// bezposrednie wywołaniefunkcji clrscr()
nowy_clrscr();
// wywolanie funkcji wskazywanej przez zmienną
→ możliwość napisania programów wywołujących funkcje, których adresy zostaną
podane dopiero w trakcie wykonywania programu.
// np. uniwersalna funkcja licząca sume N elementów dowolnego ciągu
double Suma_Ciagu( double (∗Element)( int ) , int ilosc )
{
double s = 0;
for( int i = 0; i < ilosc; i++ )
s += Element( i );
}
double Nty_Element ( int n )
{ return( 1.0/(n+1) ); }
// 1, 1/2, 1/3, 1/4, 1/5, ...
#include <stdlib.h>
#include <stdio.h>
int liczby_rosnaco( const void∗ , const void∗ );
void wyswietl( int [ ], int );
void main( void )
{
int tab[10] = { 12, −1, 3, 0, 10, 1, 2, 6, 4, 9 };
wyswietl( tab, 10 );
qsort( tab, 10, sizeof(int), liczby_rosnaco );
wyswietl( tab, 10 );
}
int liczby_rosnaco( const void∗ wsk_1, const void∗ wsk_2)
{
int ∗wsk_liczby_1, ∗wsk_liczby_2;
wsk_liczby_1 = (int∗)wsk_1;
wsk_liczby_2 = (int∗)wsk_2;
return( ∗wsk_liczby_1 − ∗wsk_liczby_2 );
}
void wyswietl( int tab[ ], int ilosc )
{
int i;
for( i = 0; i<ilosc; i++ )
printf( "tab[%d]=%d\n" , i , tab[i] );
}
...
printf( ”Suma elementów = %lf”, Suma_Ciagu( Nty_Element , 100 );
...
9
10
Przykład sortowania tablicy tekstów
PRZYKŁADY RÓŻNYCH KOMBINACJI WSKAŹNIKÓW
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Przykładowe elementy:
float LICZBA;
// liczba rzeczywista float
int TAB_INT [ 5 ]; // 5-cio elementowa tablica liczb int
int teksty_rosnaco( const void ∗wsk_1, const void ∗wsk_2)
{
return( strcmp( (char ∗) wsk_1, (char ∗) wsk_2) );
}
double FUNKCJA ( int x )
// funkcja z parametrem int zwracająca
{
// wartość double
return x+0.1;
}
void main( void )
{
char tab_tekstow[5][10] = { "Opel", "Audi", "Ford", "Trabant", "Fiat" };
qsort( tab_tekstow, 5, sizeof( tab_tekstow[0] ) , teksty_rosnaco );
}
Wskaźniki na w/w elementy:
float∗ wsk_liczby ; // wskaźnik na liczbę float
wsk_liczby = & LICZBA ;
Przykład sortowania bazy danych (tablicy struktur)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int ( ∗wsk_tab ) [ 5 ] ;
wsk_tab = & TAB_INT ;
// wskaźnik na 5-cio elementowa tablicę
double ( ∗wsk_fun ) ( int ) ;
wsk_fun = FUNKCJA ;
// wskaźnik na funkcję
Tablice elementów:
struct student
{
char nazwisko[31];
char imie[16];
int wiek;
char plec;
float stypendium;
};
float tab_liczb [ 10 ] ;
// tablica liczb float
int tab_tab [ 10 ] [ 5 ] ;
// tablica tablic
// − − − − − − − − − −
// nie ma tablicy funkcji
Tablice wskaźników na elementy:
int wedlug_nazwisk( const void ∗wsk_1, const void ∗wsk_2)
{
struct student ∗osoba_1 = (struct student ∗) wsk_1;
struct student ∗osoba_2 = (struct student ∗) wsk_2;
return( strcmp( osoba_1−>nazwisko, osoba_2−>nazwisko );
}
void main( void )
{
#define MAX_IL 100
struct student baza[ MAX_IL ];
...
qsort( baza, MAX_IL, sizeof( struct student ), wedlug_nazwisk );
}
float∗ tab_wsk_liczb [ 10 ] ;
tab_wsk_liczb [ 2 ] = & LICZBA ;
// tablica wskaźników na liczby
int (∗ tab_wsk_tab [ 10 ] ) [ 5 ] ;
tab_wsk_tab [ 2 ] = & TAB_INT ;
// tablica wskaźników na tablice
double (∗ tab_wsk_fun [ 10 ] ) ( int ) ;
tab_wsk_fun [ 2 ] = FUNKCJA ;
// tablica wskaźników na funkcje
Funkcje zwracające elementy:
float FUNKCJA_E1 ( void )
{ return 0.1; }
// funkcja zwracająca liczbę float
// − − − − − − − − − − // nie ma funkcji zwracającej tablice
// − − − − − − − − − − // nie ma funkcji zwracającej funkcje
11
12
Funkcje zwracające wskaźniki elementów:
float∗ FUNKCJA_W1( void )
// funkcja (void) zwracająca
{
// wskaźnik na liczbę float
float∗ wsk_liczby ;
wsk_liczby = &LICZBA ;
return wsk_liczby ;
}
int (∗ FUNKCJA_W2( void ) ) [ 5 ]
{
// wskaźnik na tablicę
int (∗wsk_tab)[ 5 ] ;
wsk_tab = &TAB_INT ;
return wsk_tab ;
}
// funkcja (void) zwracająca
double (∗ FUNKCJA_W3( void ) ) ( int )
{
// wskaźnik na funkcję
double (∗wsk_fun)( int );
wsk_fun = FUNKCJA ;
return wsk_fun ;
}
// funkcja (void) zwracająca
// pięciu liczb int
// double (int)
TABLICA WSKŹNIKÓW NA FUNKCJE
Tablica wskaźników na funkcje double ( int )
double (∗ tab_wsk_fun[ 10 ] ) ( int ) ;
Wskaźnik tablicy wskaźników na funkcje double ( int )
double (∗ (∗wsk_tab_wsk_fun) [ 10 ] ) ( int ) ;
Funkcja (void) zwracająca wskaźnik tablicy wskaźników na funkcje double(int)
double (∗ (∗ fun_wsk_tab_wsk_fun( void ) ) [ 10 ] ) ( int )
{
return wsk_tab_wsk_fun ;
}
13

Podobne dokumenty