[Cvičení 4] | [Obsah] | [Cvičení 6] |
Napište program, který načítá z klávesnice posloupnost celých čísel zakončených -1 (hodnota -1 se již do pole neukládá, velikost pole není předem známa). Program alokuje na počátku dynamicky pole určité velikosti. Pokud velikost pole v průběhu načítání nestačí, program provede realokaci. Použijte pouze funkcimalloc
, realokaci naprogramujte kompletně, zatím bez využití funkcerealloc
. Pro kopírování pole využijte funkcimemcpy: void *memcpy(void *cil, const void *zdroj, size_t n);
z knihovnymem.h
.Řešení:
Dev C++: dyn_pole2.dev, dyn_pole2.c CodeBlocks: dyn_pole2.cbp, dyn_pole2.c Dev C++: dyn_pole2b.dev, dyn_pole2b.c CodeBlocks: dyn_pole2b.cbp, dyn_pole2b.c
Přepište program z úlohy 5.1 za použití funkcerealloc
. Před prvním voláním funkce nezapomeňte přiřadit do ukazatele na pole hodnotuNULL
.Řešení:
Dev C++: dyn_pole3.dev, dyn_pole3.c CodeBlocks: dyn_pole3.cbp, dyn_pole3.c
Mějme např. následující deklarace a dynamicky alokované pole o velikosti 10 prvků typu int :
int *pole; int *uk; pole = (int *)malloc(sizeof(int)*10); uk = pole+5;Ukazatel pole odkazuje na počátek dynamicky alokované paměti. Hodnotou výrazu
pole + 2
je ukazatel na
třetí prvek pole, tj. prvek s indexem 2 (obr. 1). Neznamená to však, že ukazatel pole + 2
ukazuje na buňku paměti,
která má adresu vyšší o hodnotu dva. Při ukazatelové aritmetice se bere v úvahu velikost prvků, na které
ukazatel ukazuje. Předpokládejme, že sizeof(int)=4, sizeof(char)=1
a že paměť je organizována (adresována) po slabikách - bytech (jak
to ostatně dodnes počítače PC mají). Ukazatel pole+2
tedy ukazuje na adresu zvětšenou o 8, tedy na adresu
((char*)pole)+2*sizeof(int)
.
Obrázek 1: Ukazatelová aritmetika
uk+2
ukazuje na osmý prvek pole. Pokud bychom do kódu následně zapsali porovnávací výraz uk == pole+5
,
jeho hodnota by byla nenulová (pravda).pole-1
ukazuje na oblast paměti,
která k poli již nepatří. Protože jazyk C nekontroluje meze polí, můžeme tento výraz v programu napsat (pokud budeme manipulovat s daty na této
adrese, přepíšeme zpravidla hodnoty jiných proměnných).
*pole = 2; *(pole+2) = 10; *uk= -4; // zde je možné napsat též *(uk+0) = -4; *(uk+2) = 5;Zde je důležité poznamenat, že přístup k prvkům statického a dynamického pole je naprosto stejný. Následující dva zápisy jsou v jazyce C zcela ekvivalentní, je jedno, který z nich v programu použijeme:
*(pole+2) = 10; pole[2] = 10;Pokud máme deklarované statické pole, např.
int A[30]
, A je konstatní ukazatel na počátek pole, lze tedy psát
*(A+20)=10
a tento zápis je ekvivalentní zápisu A[20]=10
. Do samotné proměnné A nelze přiřadit žádnou
hodnotu, neboť jde o konstatní ukazatel inicializovaný při překladu (překladač hlásí chybu).
Ukazatelová aritmetika se efektivně využívá ve funkci scanf
, kde se jako skutečné parametry uvádějí adresy paměti, na které
se má uložit zkonvertovaná hodnota. Místo scanf("%d",&a[i]);
je lépe psát scanf("%d",a+i);
.
Napište program, který vypíše v desítkové a šestnáctkové soustavě zadané číslo typu int. Dále program vypíše v šestnáctkové a desítkové soustavě jednotlivé slabiky (byty) zadaného čísla.
(Návod: Využijte ukazatele na int a ukazatelové aritmetiky, přetypování ukazatele na typ (unsigned char*), obr. 2.)
Obrázek 2: Přetypování ukazatelů
Řešení:
Předkládáme dvě řešení, první z nich je záměrně napsané špatně (nechť si čtenář zkusí zadat hodnotu např. 1256). Příčina chybného výpisu výsledku tkví v tom, že typ
Chybné řešení: Dev C++: vypis_slabik1.dev, vypis_slabik1.c CodeBlocks: vypis_slabik1.cbp, vypis_slabik1.c Správné řešení: Dev C++: vypis_slabik2.dev, vypis_slabik2.c CodeBlocks: vypis_slabik2.cbp, vypis_slabik2.c char
je uvažován se znaménkem. Při zadání čísla 1256 má slabika nejnižšího řádu hodnotu0xE8
, tedy 7. bit má hodnotu 1. Ve formátovacím řetězci je požadavek na tisk hodnoty celého čísla (4 slabiky) v šestnáctkové soustavě%X
, Velikost dat dereferencovaných*((char*)p+i)
je přirozeně 1 slabika, jde o přetypování na ukazatel na typ char. Před předáním této slabiky funkci printf se automaticky provede roztažení na velikost 4 slabiky. Protože je typ char uvažován se znaménkem a nejvyšší bit slabiky0xE8
má hodnotu 1, což je v reprezentaci čísel v doplňkovém kódu znaménkový bit, je považována hodnota0xE8
za záporné číslo. Roztažení na 32 bitů je provedeno podle pravidel pro záporná čísla v doplňkovém kódu, tedy všechny bity vyšších řádů jsou nastaveny na hodnotu 1. Musíme přetypovat ukazatel nikoliv na typchar
, ale naunsigned char
.(Poznámka: tento program bude tisknout na různých počítačích různé výstupy; jednak se u překladačů liší velikost reprezentace typu int, jednak každý procesor ukládá data do paměti jiným způsobem - slabiky nižšího řádu na nižší adresy (little endian, např.Intel) nebo opačně, slabiky vyššího řádu na nižší adresy (big endian, např. Motorola).
sizeof(int)=4
. Mějme následující fragment kódu:
int *p = (int*)malloc(sizeof(int)*10); int *k; k = p + 5;Hodnota rozdílu
(k-p)
je 5, tedy ((char*)k-(char*)p)/sizeof(int)
.
Na závěr ještě ukážeme, jak provést výpis prvků pole pouze s využitím ukazatelů, ukazatelové aritmetiky a porovnání ukazatelů. Výpis pozice hledaného prvku je proveden s využitím rozdílu ukazatelů:
int A[5] = {1,2,3,4,5}; int x; int *p, int *za_konec; za_konec = A+5; for(p=A;p<za_konec;p++) printf("%d ",*p); printf("Zadej hledane cislo: "); scanf("%d",&x); for(p=A;p<za_konec && *p != x;p++) ; //strednik ma vyznam prazdneho prikazu if (p==za_konec) printf("Cislo nenalezeno"); else printf("Cislo je na pozici %d\n",p-A+1);Kód je k dispozici ke stažení:
Dev C++: priklad_ukaz.dev, priklad_ukaz.c CodeBlocks: priklad_ukaz.cbp, priklad_ukaz.c
Napište program, který vypíše pět posledních prvků pole několika způsoby. Využijte právě různé způsoby přístupu k prvkům pole.Řešení:
Dev C++: vypis_pole.dev, vypis_pole.c CodeBlocks: vypis_pole.cbp, vypis_pole.c
[Cvičení 4] | [Obsah] | [Cvičení 6] |