Šta je novo?

strukture i C jezik

milos_r

Čuven
Učlanjen(a)
31.10.2004
Poruke
488
Poena
619
Pozdrav,
Interesuje me moze li se inicijalizovati niz struktura putem funkcije malloc. Tj. da pokazem primerom da bi bilo jasnije sta me interesuje:
Kod:
struct imenik{
char ime[30];
char prezime[30];
char adresa[50];
unsigned telefon;
};
int main(){
struct imenik *korisnik;
int i=0,alocirano=10,brojac=0;

[B]korisnik=(int*) malloc (alocirano*sizeof(int));[/B]
Mislim vidim i sam da nemoze jel mi prevodioc prijavljuje gresku ali me interesuje kako to resiti. Pre svega jel bih kasnije hteo da sa realloc povecavam taj niz dinamicki.
Mozda postoji neko drugo resenje za to?
Hvala unapred
 
Koliko vidim ovde si alocirao niz adresa a ne prostor za strukture. Mislim da bi trebalo nešto tipa malloc (alocirano * sizeof(imenik)) ili tako nešto, nemam sad kompajler pri ruci. Ili možda u tom alociranom nizu adresa for petlju pa malloc svake strukture ponaosob.
 
Evo da jos malo pojasnim. Ovako mogu da uradim recimo
Kod:
struct imenik{
char ime[30];
char prezime[30];
char adresa[50];
unsigned telefon;
};
int main(){
struct imenik korisnik[10];
int i=0,alocirano=10,brojac=0;

for(i=0; i<alocirano; i++){
    printf("Unesi podatke za %d osobu \n",i+1);
    ucitaj(&korisnik[i]);
ali time sam ogranicen na unosenje 10 osoba u imenik(ili vec nekog broja unapred definisanog), a ja bi zeleo da se taj broj dinamicki povecava sa unosom novih korisnika.
E da probao sam i sa tvojim predlogom sizeof(imenik) ali kompajler mi prijavljuje istu gresku da nemoze da prevede int u imenik?
 
Pa da sama greska u kompajleru mi je lepo ukazivala na gresku.

Resenje izgleda ovako
Kod:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct imenik{
char ime[30];
char prezime[30];
char adresa[50];
char telefon[20];
};

void ucitaj(struct imenik *o){

printf("Unesite ime osobe: ");
gets(o->ime);
printf("Unesite prezime osobe: ");
gets(o->prezime);
printf("Unesite adresu osobe: ");
gets(o->adresa);
printf("Unesite telefon osobe: ");
scanf("%u",&o->telefon);
fflush(stdin);
}

int main(){
    struct imenik *korisnik,*pok;
    int i=0,alocirano=2,brojac=0,n;

    korisnik=(imenik*) malloc (alocirano*sizeof(imenik));
    if(korisnik == NULL )
        printf("Nema slobodne memorije za malloc.\n") ;
    printf("Za unos korisnika u imenik pritisnite 1, za listanje imenika 2.\n") ;
    scanf("%d",&n);
    fflush(stdin);
    if(n==1){
        for(i=0; ; i++,brojac++){
            printf("Unesi podatke za %d osobu \n",i+1);
            ucitaj(&korisnik[i]);

            if(brojac>=alocirano){
            alocirano+=2;
            korisnik = (imenik*)realloc( korisnik, alocirano * sizeof(imenik));
                if(korisnik== NULL)
                printf("Nema slobodne memorije za realloc.\n") ;}}}
    else if(n==2){
        for(pok=korisnik;pok<korisnik+brojac;pok++)
            printf("%s %s %s %d\n",pok->ime,pok->prezime,pok->adresa,pok->telefon);}

    return 0;
}
Naravno program nikako nije dobar ali sam makar resio taj broblem sa dinamickim povecanjem niza struktura, sad mi ostaje da nadjem resenje kako da prestanem sa unosom korisnika posto ovo radi i kad krenes sa unosom korisnika kraj se nenazire 😀
 
Slazem se sa postom iznad, nauci liste. Olaksaces sebi posao a i resenje ce biti elegantije..
 
Kod:
// kratak pocetni tutorial za jednostavno povezane liste 
// by [email protected]


#include "stdafx.h"
#include "stdlib.h"
#include "string.h"


typedef struct _imenik
{
    char ime[30];
    char prezime[30];
    char adresa[50];
    char telefon[20];
} imenik ;

// sledeca struktura sadrzi same podatke strukture imenik kao i pokazivac na sledeci element liste.
// pNext se koristi kao veza izmedju aktuelnog i sledeceg elementa , 
// u ovom slucaju govorimo o jednostavno povezanoj listi. Duplopovezane liste sadrze dodatno i pokazivac 
// pPrev koji omogucuje kretanje po listi u obadva pravca jer pokazuje na predhodni element 

typedef struct _element
{
    imenik*   pData;
    _element*  pNext;
} element;

// Pokazivac PListHead pokazuje uvek na pocetak Liste inicializira se samo jednom u dodaj_element funkciji 
element* pListHead = NULL;

// Pokazivac PListWork je radni pokazivac i koristi se za pretrazivanje liste , kretanju po istoj 
element* pListWork = NULL;


///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// 
// funkcija pronadji_zadnjiElement prolazi listu do kraja i vraca zadnji element
////////// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// 
element* pronadji_zadnjiElement()
{
    // krecemo iz pocetka zato radni pokazivac stavljamo na pocetak.
	pListWork = pListHead;
	element* pLast = NULL;

	// Dok ima elemenata u listi 
	while(pListWork != NULL)
	{
		pLast = pListWork;
		pListWork = pListWork->pNext;
	}
	
	return pLast;
}

///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// 
// funkcija dodaj element rezervise memoriju za novi element i dodaje ga u listu 
// kao i obezbedjuje ukoliko je lista prazna da se initializira PListHead pokazivac
////////// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// 
element* dodaj_element(imenik* pNoviImenik)
{
	element* pZadnji = NULL;
	
	// alokiraj memoriju za novi element liste
	element* pNoviElement = (element*) malloc(sizeof(element));

	// ukoliko nema dovoljno memorije za malloc vrati NULL
	if(pNoviElement == NULL) 
		return NULL;
	
	// ukoliko lista nije prazna 
	if(pListHead != NULL) 
		 pZadnji = pronadji_zadnjiElement();  	// idi do kraja liste i vrati zadnji element
	else
		pListHead = pNoviElement; // u suprotnom novo rezervisani element postaje i pocetak liste 
	                              // , a kako bi bilo drugacije :)
		
	pNoviElement->pData = pNoviImenik; // dodeli novom elementu liste i podatke 

	// zadnji element u listi ima na pokazivacu pNext uvek NULL zato 
	// dodeljujemo novom elementu koji ce kad se doda upravo biti zadnji i tu vrednost.
	pNoviElement->pNext = NULL;

	if(pZadnji != NULL) // ukoliko je postojao zadnji 
		pZadnji->pNext = pNoviElement;  // povezujemo aktuelni zadnji element sa novim elementom.

	// vrati novi element
	return pNoviElement;
}


///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// 
// funkcija ucitaj , preuzima podatke od korisnika i vraca rezervisani objekat sa podacima nazad
////////// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// 
imenik* ucitaj()
{
	// rezervisi memoriju za novi imenik
	imenik* pNoviImenik = (imenik*) malloc(sizeof(imenik));

	// ukoliko nema dovoljno memorije za malloc vrati NULL
	if(pNoviImenik == NULL) 
		return NULL;

	// preuzmi podatke od korisnika 

	printf("\n");
	printf("Unesite ime osobe: ");
	scanf("%s",pNoviImenik->ime);
	printf("Unesite prezime osobe: ");
	scanf("%s",pNoviImenik->prezime);
	printf("Unesite adresu osobe: ");
	scanf("%s",pNoviImenik->adresa);
	printf("Unesite telefon osobe: ");
	scanf("%s",pNoviImenik->telefon);
	printf("\n");
	fflush(stdin);

	// vrati objekat sa unetim podacima 
	return pNoviImenik;
}

void stampaj_listu()
{
    // krecemo iz pocetka zato radni pokazivac stavljamo na pocetak.
	pListWork = pListHead;

	// dok ima elemenata u listi 
	while(pListWork != NULL)
	{
		printf("%s %s %s %s\n",pListWork->pData->ime,
			                   pListWork->pData->prezime,
							   pListWork->pData->adresa,
							   pListWork->pData->telefon);

		pListWork = pListWork->pNext;
	}
}


///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// 
// funkcija obrisi_listu , cisti sve rezervisane elemente odnosno alokiranu memoriju za iste.
// obavezno je pozivanje na kraju programa !
////////// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// 
void obrisi_listu()
{
	if(pListHead == NULL) // ako je lista prazna nemamo sta da brisemo 
		return;

	// krecemo iz pocetka zato radni pokazivac stavljamo na pocetak.
	pListWork = pListHead;

	// sve dok ima sledeceg elementa u listi 
	while(pListWork != NULL)
	{
		element* pElement = pListWork;
		// pre nego ocistimo aktuelni element prebacujemo pokazivac na 
		// sledeci u suprotnom bi izgubili tu informaciju 
		pListWork = pListWork->pNext;

		// oslobodi memoriju za imenik
		free(pElement->pData);

		// oslobodi memoriju za element
		free(pElement);
	}

	// sobzirom da smo sve izbrisali ponovo mozemo staviti pListHead na vrednost NULL 
	pListHead = NULL;

}



int main(int argc, char* argv[])
{
	char odluka[3];

	do
	{
		// ucitaj podatke za novi imenik 
		imenik* pImenik = ucitaj();

		if(pImenik != NULL) // ukoliko nije doslo do neke greske 
			dodaj_element(pImenik); // dodaj nove podatke u listu
		
		printf("Da li zelite da unesete jos podataka (da/ne): ");
		scanf("%s",odluka);
	}
	while(strcmp(odluka,"da") == 0); 

	printf("\npodaci koje ste uneli su :\n ");

	stampaj_listu();

	// cisti sve !
	obrisi_listu();

	return 0;
}

Ovaj primer sam ti postavio da kao prvo vidis jednostavan nacin primene jednostavno povezanih listi a kao drugo da ti skrenem paznju na strukturu tvojih programa.

Uvek gledaj da postavis sam program modularno odnosno strukturisano stim sto ces posebne zadatke izdvojiti u zasebne funkcije kasnije u objektorijentisanom jeziku "klase".

U ovom primeru je podeljen rad sa listom u zasebne funkcije od konkretnog zadatka unosenja i stampanja podataka.
Sto ti omucava da implementirane funkcije za rad sa listama kasnije u drugim proramcicima jednostavnije preuzmes.
 
Poslednja izmena:
@monteboy respect :happy:

Ja vec duze vreme jusrim sebe da napisem skup biblioteka osnovnih struktura. stack, red, lista, binarno stablo,AVL binarno stablo pretrazivanja, B i B* stablo.. Ali bas me mrzi, mogli bi da organizujemo jedan bench projekat, da se napise biblioteka struktura podataka. Damo prototipove klasa i vidimo koliko ima zainteresovanih i podelimo posao.. Posto mislim da tako nesto ce koristiti mnogima..
 
@monteboy respect :happy:

Ja vec duze vreme jusrim sebe da napisem skup biblioteka osnovnih struktura. stack, red, lista, binarno stablo,AVL binarno stablo pretrazivanja, B i B* stablo.. Ali bas me mrzi, mogli bi da organizujemo jedan bench projekat, da se napise biblioteka struktura podataka. Damo prototipove klasa i vidimo koliko ima zainteresovanih i podelimo posao.. Posto mislim da tako nesto ce koristiti mnogima..

Generalno bih ucestvovao u takvom nekom projektu ukoliko zazivi.
Treba se postaviti pitanje i koliko je za ovakve namene i cist "C" idealan.
Kad u drugim jezicima tipa C++ imamo sablone pa mozemo generisati odekvatne klase za bilo koji elementarni ili kompleksni tip podataka.

Sa druge strane imamo gotovu STL biblioteku sto bi opet znacilo da je mozda neki osnovni projekat ili tutorial za pocetnike ipak bolje resenje vise kao projekat da se razumije pozadina i implementacija takvih struktura. E sad koliko bi neko istu i u praksi pored vec gotovih resenja koristio je drugo pitanje 🙂
 
Poslednja izmena:
Generalno bih ucestvovao u takvom nekom projektu ukoliko zazivi.
Treba se postaviti pitanje i koliko je za ovakve namene i cist "C" idealan.
Kad u drugim jezicima tipa C++ imamo sablone pa mozemo generisati odekvatne klase za bilo koji elementarni ili kompleksni tip podataka.

Sa druge strane imamo gotovu STL biblioteku sto bi opet znacilo da je mozda neki osnovni projekat ili tutorial za pocetnike ipak bolje resenje vise kao projekat da se razumije pozadina i implementacija takvih struktura. E sad koliko bi neko istu i u praksi pored vec gotovih resenja koristio je drugo pitanje 🙂

Naravno c++, mnogo elegantije nego. Tipa napravi se jedna apstraktna klasa lista, i izvedene klase od nje( dvostruko ulancana, kruzna...), tako sam i mislio nesto, znaci sa komentarima kao i nekim objasnjenjima o samim strukturama i algoritmima.
 
U gore prilozenom source primeru je postavljen program tako da su funkcije za implementaciju jednostavno vezane liste cvrsto izvedene za strukturu "imenik" sto nije lijepo jer pri koriscenju neke druge strukture mora se delimicno promeniti i implementacija za organizaciju same liste.

Ono sto je totalno nebitno za tip koji koristimo u listi bi trebalo da bude njegovo dodavanje , ciscenje kompletne liste kao i pronalazenje zadnjeg elementa. Te funkcije ne smiju biti specificne za odredjenu strukturu ili tip.

zato prilazem izmenjen source sa posebno oznacenim mestima koja nam omogucuju ove tri funkcije univerzalnije da koristimo :

Kod:
// kratak pocetni tutorial za jednostavno povezane liste 
// by [email protected]


#include "stdafx.h"
#include "stdlib.h"
#include "string.h"


typedef struct _imenik
{
    char ime[30];
    char prezime[30];
    char adresa[50];
    char telefon[20];
} imenik ;


typedef struct _automobil
{
    char model[30];
    char snagaks[3];
    char maksbrzina[3];
    char cena[6];
} automobil ;


// sledeca struktura sadrzi same podatke u vidu pokazivaca na bilo koju strukturu ili tip (void*) kao i pokazivac na sledeci element liste.
// pNext se koristi kao veza izmedju aktuelnog i sledeceg elementa , 
// u ovom slucaju govorimo o jednostavno povezanoj listi. Duplopovezane liste sadrze dodatno i pokazivac 
// pPrev koji omogucuje kretanje po listi u obadva pravca jer pokazuje na predhodni element 

typedef struct _element
{
    [B][COLOR="Red"]void* [/COLOR][/B]  pData;
    _element*  pNext;
} element;

// Pokazivac PListHead pokazuje uvek na pocetak Liste inicializira se samo jednom u dodaj_element funkciji 
element* pListHead = NULL;

// Pokazivac PListWork je radni pokazivac i koristi se za pretrazivanje liste , kretanju po istoj 
element* pListWork = NULL;


///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// 
// funkcija pronadji_zadnjiElement prolazi listu do kraja i vraca zadnji element
////////// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// 
element* pronadji_zadnjiElement()
{
    // krecemo iz pocetka zato radni pokazivac stavljamo na pocetak.
	pListWork = pListHead;
	element* pLast = NULL;

	// Dok ima elemenata u listi 
	while(pListWork != NULL)
	{
		pLast = pListWork;
		pListWork = pListWork->pNext;
	}
	
	return pLast;
}

///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// 
// funkcija dodaj element rezervise memoriju za novi element i dodaje ga u listu 
// kao i obezbedjuje ukoliko je lista prazna da se initializira PListHead pokazivac
////////// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// 
element* dodaj_element([B][COLOR="Red"]void* [/COLOR][/B]pNoviPodaci)
{
	element* pZadnji = NULL;
	
	// alokiraj memoriju za novi element liste
	element* pNoviElement = (element*) malloc(sizeof(element));

	// ukoliko nema dovoljno memorije za malloc vrati NULL
	if(pNoviElement == NULL) 
		return NULL;
	
	// ukoliko lista nije prazna 
	if(pListHead != NULL) 
		 pZadnji = pronadji_zadnjiElement();  	// idi do kraja liste i vrati zadnji element
	else
		pListHead = pNoviElement; // u suprotnom novo rezervisani element postaje i pocetak liste 
	                              // , a kako bi bilo drugacije :)
		
	pNoviElement->pData = pNoviPodaci; // dodeli novom elementu liste i podatke 

	// zadnji element u listi ima na pokazivacu pNext uvek NULL zato 
	// dodeljujemo novom elementu koji ce kad se doda upravo biti zadnji i tu vrednost.
	pNoviElement->pNext = NULL;

	if(pZadnji != NULL) // ukoliko je postojao zadnji 
		pZadnji->pNext = pNoviElement;  // povezujemo aktuelni zadnji element sa novim elementom.

	// vrati novi element
	return pNoviElement;
}


///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// 
// funkcija ucitaj , preuzima podatke od korisnika i vraca rezervisani objekat sa podacima nazad
////////// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// 
imenik* ucitaj()
{
	// rezervisi memoriju za novi imenik
	imenik* pNoviImenik = (imenik*) malloc(sizeof(imenik));

	// ukoliko nema dovoljno memorije za malloc vrati NULL
	if(pNoviImenik == NULL) 
		return NULL;

	// preuzmi podatke od korisnika 

	printf("\n");
	printf("Unesite ime osobe: ");
	scanf("%s",pNoviImenik->ime);
	printf("Unesite prezime osobe: ");
	scanf("%s",pNoviImenik->prezime);
	printf("Unesite adresu osobe: ");
	scanf("%s",pNoviImenik->adresa);
	printf("Unesite telefon osobe: ");
	scanf("%s",pNoviImenik->telefon);
	printf("\n");
	

	// vrati objekat sa unetim podacima 
	return pNoviImenik;
}

///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// 
// funkcija ucitaj , preuzima podatke od korisnika i vraca rezervisani objekat sa podacima nazad
////////// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// 
automobil* ucitaj_auto()
{
	// rezervisi memoriju za novi automobil
	automobil* pNoviAuto = (automobil*) malloc(sizeof(automobil));

	// ukoliko nema dovoljno memorije za malloc vrati NULL
	if(pNoviAuto == NULL) 
		return NULL;

	// preuzmi podatke od korisnika 

	printf("\n");
	printf("Unesite model automobila : ");
	scanf("%s",pNoviAuto->model);
	printf("Unesite snagu automobila: ");
	scanf("%s",pNoviAuto->snagaks);
	printf("Unesite brzinu automobila: ");
	scanf("%s",pNoviAuto->maksbrzina);
	printf("Unesite cenu automobila: ");
	scanf("%s",pNoviAuto->cena);
	printf("\n");
	
	// vrati objekat sa unetim podacima 
	return pNoviAuto;
}


void stampaj_listu()
{
    // krecemo iz pocetka zato radni pokazivac stavljamo na pocetak.
	pListWork = pListHead;

	// dok ima elemenata u listi 
	while(pListWork != NULL)
	{
		printf("%s %s %s %s\n",((imenik*)pListWork->pData)->ime,
			                   ((imenik*)pListWork->pData)->prezime,
							   ((imenik*)pListWork->pData)->adresa,
							   ((imenik*)pListWork->pData)->telefon);

		pListWork = pListWork->pNext;
	}
}

void stampaj_listu_automobil()
{
    // krecemo iz pocetka zato radni pokazivac stavljamo na pocetak.
	pListWork = pListHead;

	// dok ima elemenata u listi 
	while(pListWork != NULL)
	{
		printf("%s %s %s %s\n",((automobil*)pListWork->pData)->model,
			                   ((automobil*)pListWork->pData)->snagaks,
							   ((automobil*)pListWork->pData)->maksbrzina,
							   ((automobil*)pListWork->pData)->cena);

		pListWork = pListWork->pNext;
	}
}




///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// 
// funkcija obrisi_listu , cisti sve rezervisane elemente odnosno alokiranu memoriju za iste.
// obavezno je pozivanje na kraju programa !
////////// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// ///// 
void obrisi_listu()
{
	if(pListHead == NULL) // ako je lista prazna nemamo sta da brisemo 
		return;

	// krecemo iz pocetka zato radni pokazivac stavljamo na pocetak.
	pListWork = pListHead;

	// sve dok ima sledeceg elementa u listi 
	while(pListWork != NULL)
	{
		element* pElement = pListWork;
		// pre nego ocistimo aktuelni element prebacujemo pokazivac na 
		// sledeci u suprotnom bi izgubili tu informaciju 
		pListWork = pListWork->pNext;

		// oslobodi memoriju za podatke 
		free(pElement->pData);

		// oslobodi memoriju za element
		free(pElement);
	}

	// sobzirom da smo sve izbrisali ponovo mozemo staviti pListHead na vrednost NULL 
	pListHead = NULL;

}



int main(int argc, char* argv[])
{
	char odluka[3];

	do
	{
		// ucitaj podatke za novi imenik 
		imenik* pImenik = ucitaj();

		if(pImenik != NULL) // ukoliko nije doslo do neke greske 
			dodaj_element(pImenik); // dodaj nove podatke u listu
		
		printf("Da li zelite da unesete jos podataka (da/ne): ");
		scanf("%s",odluka);
	}
	while(strcmp(odluka,"da") == 0); 

	printf("\npodaci osoba koje ste uneli su :\n ");
	
	stampaj_listu();
	obrisi_listu();

	///////////////////////////////////////////////////////// 

	do
	{
		// ucitaj podatke za novi automobil
		automobil* pAutomobil = ucitaj_auto();

		if(pAutomobil != NULL) // ukoliko nije doslo do neke greske 
			dodaj_element(pAutomobil); // dodaj nove podatke u listu
		
		printf("Da li zelite da unesete jos podataka (da/ne): ");
		scanf("%s",odluka);
	}
	while(strcmp(odluka,"da") == 0); 

	printf("\npodaci automobila koje ste uneli su :\n ");
	
	stampaj_listu_automobil();

	obrisi_listu();

	return 0;
}

Ono sto je specificno i sto nema veze sa samom implemantacijom li organizacijom jedne liste i dalje ostaje specificno za odredjeni tip (strukturu) a to su funkcije ucitivanja i stampanja podataka.

U ovom primeru sam dodao strukturu automobil i dodao funkcije za ucitavanje i stamapanje podataka automobila.
Ostale funkcije za organizaciju liste se nisu menjale.

Kao osnovni tip za podatke koristimo pokazivac na typ "void" koji nije definisan i moze da pokazuje na bilo sta.
Zato nam je u funkcijama za stampanje obavezno potreban cast operator u ovom primeru (imenik*) i (automobil*)
Mana void'a je sto nam pri kompiliranju ne daje tip-sigurnost i otezava kod komplesknih programa trazenje greski i servisiranje.

U modernijim jezicima tipa C++ koji podrzavaju polimorfiju,sablone ovakve stvari se resavaju tip-sigurno stim ste se izvedu iz baznih klasa odredjene specificne klase kao i implementiraju operatori tako da nam na kraju "void" i nije potreban i program postaje lakse razumeti odnosno lakse debugovati i servisirati.

to be continued ....
 
Poslednja izmena:
Nazad
Vrh Dno