Práce s ukazateli
Práce s ukazateli, základní náhled.
Zadání
Napište funkci, která vymění obsah (jednotlivé prvky) dvou polí, která jsou funkci předána jako parametry. Funkci realizujte tak aby byla co nejrychlejší. Pokud je to možné, realizujte ji tak, aby byla (univerzálně) použitelná pro výměnu prvků polí libovolného typu.
0.krok: založte si nový projekt. Zpočátku pracujte se soubory .c,
po dokončení je přejmenujte na .cpp a odstraňte chyby plynoucí z
přísnějšího přístupu ke konverzi ukazatelů.
1.krok: Zkuste si prohlédnout následující obrázek, udělat si v něm jasno a zkusit se orientovat v pojmech: Hodnota, Paměť, Paměťové místo (buňka), Adresa, Ukazatel, Proměnná, Název proměnné, Přístup k proměnné (nastavení/čtení hodnoty), Paměť na které leží proměnná (adresa/ukazatel), Proměnná jejíž hodnota je adresa/ukazatel, Získání adresy proměnné, Přístup k proměnné na adrese, Definice proměnné, Deklarace proměnné. Inicializace proměnné.
V případě ukazatelů (či jiného programování) je výhodné „čmárat“. Získáte tak lepší představu o dané situaci. Obrázek znázorňuje ve střední části místa v paměti a jejich názvy (i, pi …). Vpravo jsou příslušné definice proměnných, které zajišťují názvy pro proměnné a příslušné místo v paměti, společné s inicializací. V levé části jsou odpovídající zápisy pro přístup k daným proměnným podle definicí z pravé části.
O předávání parametrů více zde
2. krok: Pro zadání existuje celá řada řešení. Jedno z elegantních
(rychlé a univerzální) je to, při kterém nedojde přímo k výměně prvků
polí, ale využije se toho, že pole je interně reprezentováno adresou
počátku zabraného datového prostoru. Proto lze (pro pole stejného
typu) pouze vyměnit adresy počátku polí a úkol změny hodnot v polích
je splněn. Toto řešení nelze provést u statických polí, protože ty
jsou (z hlediska svého počátku = názvu) konstantní (kapitola 5.2.15,
5.2.17). Výhodou tohoto řešení také je, že pracujeme/měníme pouze
počátky polí (adresy) a nepotřebujeme znát typ dat pole (ukazatel) se
kterými nepracujeme. Proto můžeme funkci napsat univerzálně =
předávat funkci počátek pole pomocí ukazatele typu void.
Prostudujte
si následující obrázek, který situaci popisuje (postupujeme-li do
funkce (vlevo), musíme získat adresu pomocí &. Postupujeme-li k
původním proměnným (vpravo) používáme pro přístup k prvku dereferenci
*).
V místě mezi P2 a P_Y (a P1 a P_X) je hranice mezi
volající a volanou funkcí
Vpravo jsou naznačená pole, uprostřed
jsou proměnné ve kterých jsou adresy polí a naznačené kam ukazují
před a po výměně.
Vlevo jsou proměnné ve funkci, mezi levým a
středním sloupcem jsou naznačeny „změny“ proměnných mezi
funkcí volající a volanou.
3. krok: Na základě vzoru zdrojového kódu, který je přiložen na
konci této stránky realizujte dané zadání. Vyberte pouze jednu z
funkcí exchange (tak aby splňovala požadavky zadání). Promyslete si
zdůvodnění vhodnosti/nevhodnosti, výhod/nevýhod jednotlivých řešení a
případně navrhněte změny v hlavičce tak, aby byly univerzálnější
(=lepší).
4. krok: naprogramujte funkci, která provede výměnu prvků po jednom a srovnejte rychlost jednotlivých řešení pro 10, 100, 1000 prvků.
Nápověda:
Na začátku programu předpokládáme naalokování dvou polí (p_x a p_y jsou ukazatele na int, tj. Adresy počátku pole intů, které dá malloc. V případě použití *p_x se přistupuje na hodnotu uloženou na adrese p_x, tj. K prvnímu prvku pole. Adresa, na které leží proměnná p_x se získá pomocí &p_x.)
int * pole intů
p_x -> int 10, int11, int12, int13 (p_x ukazuje na pole proměnných typu int)
p_y -> int 20, int 21, int 22, int 23
Naším úkolem je, aby (výměnou hodnot pole1 a pole2 ve funkci) došlo k následujícímu
int * pole intů
p_x -> int 20, int 21, int 22, int 23
p_y -> int 10, int11, int12, int13
Předáme-li do funkce hodnotu pole1 a pole2, budou ve funkci pouze lokální kopie (ukazatelů na počátek pole) a jejich výměna se neprojeví vně.
Proto je nutné do funkce dát adresu, na které leží proměnné (p_x, p_y), jejichž obsah chceme měnit. Výměnou obsahu (adres polí) vlastně vyměníme pole.
Typ void ve funkci používáme proto, že není důležité (z hlediska algoritmu) na co je ukazatel (typ pole), protože se mění pouze ukazatele ( a ty jsou pro všechny typy stejné). Funkce je tedy použitelná pro pole libovolného typu. (void se tedy dá chápat nejen jako ukazatel na „nic“ ale také jako ukazatel na „cokoli“ neboli univerzální ukazatel)
Sled proměnných a jejich obsahů je tedy ve funkci tento:
void ** (int **) |
void* (int *) |
(int) |
p1 (&p_x) |
*p1 (p_x) |
(p_x[ i ]) |
adresa na níž je měněná proměnná |
Obsah měněné proměnné (adresa počátku pole) |
(prvky) pole |
=================== vzor ====================
// Tento program vytvori dve pole v dynamicke pameti // Vytvori na ne ukazatele a ty pomoci funkce exchange // prehodi. #include <stdio.h> #include <stdlib.h> #define POCET 50 // vyberte nejlepsi prototyp a ostatní smazte void exchange (void **p1,void **p2) { } void exchange (void *p1,void *p2) { } void exchange (int **p1,int **p2) { } void exchange (int *p1,int *p2) { } void exchange (int **p1,int **p2, int delka1, int delka2) { } void exchange (int *p1,int *p2, int delka1, int delka2) { } void exchange (float **p1,float **p2) { } void exchange (float *p1,float *p2) { } void exchange (float **p1,float **p2, int delka) { } void exchange (float *p1,float *p2, int delka) { } int main ( ) { int *p_x,*p_y; p_x=malloc(sizeof(int)*POCET); p_y=malloc(sizeof(int)*POCET); // tady muzete cvicne naplnit pole ruznymi hodnotami printf("Alokovano......\n"); printf(" px= %p , py= %p\n",p_x,p_y); // pro lepsi prehled muzete vzdy po ukazatelich vytisknout i hodnoty printf("Prehazuju......\n"); // exchange (); // zavolejte tu spravnou funkci se spravnymi parametry // Semhle prijde napsat volani nejvhodnejsi funkce Exchange se vstupnimi parametry. printf(" px= %p , py= %p\n",p_x,p_y); free (p_x); // tady neco chybi free (p_y); // tady neco chybi printf("Dealokovano......\n"); printf(" px= %p , py= %p\n",p_x,p_y); // tohle uz nedava moc smysl pokud se to vyse neupravi printf("Konec.\n"); return 0; }
================ konec vzoru ==================
Poslední změna 2011-09-19