c. dvourozměrné dynamické pole. Jednorozměrná dynamická pole
// deklarace dvourozměrného dynamického pole s 10 prvky:
float **ptrarray = new float* ; // dva řetězce v poli
for (int počet = 0; počet< 2; count++)
ptrarray = nový plovák ; // a pět sloupců
// kde ptrarray je pole ukazatelů na alokovanou paměťovou oblast pro pole reálná čísla plovoucí typ
Nejprve je deklarován float **ptrarray ukazatel druhého řádu, který odkazuje na pole float* ukazatelů, kde velikost pole je dvě . Poté, ve smyčce for, každý řádek pole deklarovaný v řádek 2 paměť je alokována pro pět prvků. Výsledkem je získání dvourozměrného dynamického pole ptrarray. Uvažujme příklad uvolnění paměti přidělené pro dvourozměrné dynamické pole.
// uvolnění paměti přidělené pro dvourozměrné dynamické pole:
for (int počet = 0; počet< 2; count++)
odstranit ptraray;
// kde 2 je počet řádků v poli
#zahrnout
#zahrnout
#zahrnout
void main()
{
int*a; // ukazatel na pole
system("chcp 1251");
scanf("%d", &n);
scanf("%d", &m);
// Alokace paměti
a = (int*) malloc(n*m*sizeof(int));
// Zadejte prvky pole
for(i=0; i for(j=0; j printf("a[%d][%d] = ", i, j); scanf("%d", (a+i*m+j)); // Zobrazení prvků pole for(i=0; i for(j=0; j printf("%5d", *(a+i*m+j)); // 5 znakových mezer pro prvek pole getchar(); getchar(); Výsledek provedení Zadejte počet řádků: 3 Zadejte počet sloupců: 4 Existuje také další způsob, jak dynamicky alokovat paměť pro dvourozměrné pole – pomocí pole ukazatelů. K tomu potřebujete: Funkce malloc() vrací ukazatel na první bajt oblasti paměti o velikosti, která byla přidělena z dynamicky alokované oblasti paměti. Pokud v oblasti dynamické paměti není dostatek paměti, vrátí se ukazatel null. #zahrnout int **a; // ukazatel na ukazatel na řetězec system("chcp 1251"); printf("Zadejte počet řádků: "); scanf("%d", &n); printf("Zadejte počet sloupců: "); scanf("%d", &m); // Alokace paměti pro ukazatele na řetězce a = (int**)malloc(n*velikost(int*)); // Zadejte prvky pole for(i=0; i // Alokace paměti pro ukládání řetězců a[i] = (int*)malloc(m*velikost(int)); for(j=0; j printf("a[%d][%d] = ", i, j); scanf("%d", &a[i][j]); // Zobrazení prvků pole for(i=0; i for(j=0; j printf("%5d", a[i][j]); // 5 znakových mezer pro prvek pole zdarma(a[i]); // uvolnění paměti pro řetězec getchar(); getchar(); Výsledek spuštění programu je podobný jako v předchozím případě. Pomocí dynamické alokace paměti pro řetězcové ukazatele můžete přidělit volná pole. Volný, uvolnit
nazývá se dvourozměrné pole (matice), jehož velikost řádků může být různá. Výhodou použití volného pole je to, že nemusíte alokovat příliš mnoho paměti počítače, abyste pojali nejdelší možný řetězec. Ve skutečnosti je volné pole jednorozměrné pole ukazatelů na jednorozměrná datová pole. Ukazatele. Ukazatel je proměnná, jejíž hodnotou je adresa, kde se data nacházejí. Adresa je číslo paměťového místa, ve kterém nebo se kterým jsou data uložena. Podle typu dat v SI se ukazatele dělí na: Typovaný ukazatel je ukazatel, který obsahuje adresu dat určitého typu (systému nebo uživatele). Netypovaný ukazatel je ukazatel, který obsahuje adresu dat nedefinovaného typu (pouze adresa). Deklarace ukazatele; Nastavení ukazatele; přístup k hodnotě umístěné na ukazateli. Deklarace (popis) ukazatele v jazyce C je následující: Napište *název [=hodnota]; Ukazatel v SI lze inicializovat při deklaraci zadáním odpovídající hodnoty pomocí přiřazovacího znaku. Tato hodnota musí být adresa zapsaná v jednom z následujících tvarů: Hodnota null (identifikátor NULL); Další ukazatel; Variabilní adresa (prostřednictvím operace převzetí adresy); Výraz představující aritmetiku ukazatele; Adresa vyplývající z přidělení dynamické paměti. #zahrnout int var; // běžná celočíselná proměnná int *ptrVar; // celočíselný ukazatel (ptrVar musí být typu int, protože bude odkazovat na proměnnou typu int) ptrVar = // přiřazení ukazatele adresy buňky v paměti, kde leží hodnota proměnné var scanf("%d", &var); // vložte hodnotu zadanou z klávesnice do proměnné var printf("%d\n", *ptrVar); // výstup hodnoty přes ukazatel Výsledek provedení: 6 6 Přednáška číslo 3. Funkce. Funkce je syntakticky rozlišený pojmenovaný programový modul, který provádí určitou akci nebo skupinu akcí. Každá funkce má své vlastní rozhraní a implementaci. Funkční rozhraní - hlavička funkce, která udává název funkce, seznam jejích parametrů a typ návratové hodnoty. Popis funkce v jazyce C se provádí kdekoli v programu mimo popis ostatních funkcí a skládá se ze tří prvků: 1. funkční prototyp; 2. záhlaví funkce; 3. funkční tělo. Prototyp funkce je volitelná část popisu funkce určená k deklaraci nějaké funkce, jejíž rozhraní odpovídá danému prototypu. Deklarace prototypu má následující tvar: Název typu (seznam formálních typů parametrů); Parametry funkce jsou hodnoty předávané funkci při jejím volání. Hlavička funkce - popis rozhraní části funkce, která obsahuje: návratový typ, název funkce a seznam formálních parametrů funkce. Syntaxe deklarace záhlaví funkce: Název typu (seznam formálních parametrů) Příklady záhlaví funkcí: int func(int i, double x, double y) void func(int ind, char *string) Double func(void) Tělo funkce je implementační část, která obsahuje programový kód, který se provede při volání funkce. Tělo funkce vždy následuje bezprostředně za hlavičkou funkce (nelze je oddělit) a je uzavřeno ve složených závorkách. Implementace funkce v SI pro výpočet faktoriálu čísla. dvojitý faktoriál (bez znaménka); Dvojitý faktoriál (číslo bez znaménka) dvojí fakt = 1,0; For(bez znaménka i=1;i<=num;i++) Fakt *= (double)i; vrátit skutečnost; struktur. Struktura je komplexní datový typ, který je množinou prvků různých typů uspořádaných v paměti. Každý prvek ve struktuře má svůj vlastní název a nazývá se pole. Deklarace SI struktury je: Struktura [název typu] Pole_1; Pole_2; Pole_N; ) [seznam proměnných]; Deklarace pole struktury je možná pouze bez inicializace. Pokud má několik polí následujících za sebou v popisu struktury stejný typ, lze k jejich popisu použít syntaxi pro deklaraci několika proměnných stejného typu. Soubory. Soubor je pojmenovaná oblast dat na paměťovém médiu. Typy souborů (vzhledem k jazyku „SI“): Další operace: Režimy otevírání soubory s SI Přesměrování streamu Funkce vrací: Zavření souboru Funkce vrací: Kontrola konce souboru Funkce vrací: Otevírání textových souborů Čtení z textového souboru Formátované čtení Funkce vrací: Funkce vrací: Funkce vrací: Napište do textu soubor v SI Formátovaný výstup Zápis do binárního souboru Navigace v souborech Čtení aktuálního offsetu v souboru: SEEK_SET (0) - od začátku souboru. Získání příznaku chyby: Ukládání do vyrovnávací paměti Funkce vymazání vyrovnávací paměti: Vytvoří vyrovnávací paměť o velikosti BUFSIZ. Používá se před vstupem nebo výstupem do proudu. Dočasné soubory Funkce pro vytvoření dočasného souboru: Smazat a přejmenovat Funkce odstranění souboru: Přednáška číslo 4. Zásobník. Zásobník je jakýmsi opakem fronty, protože funguje na principu LIFO (poslední dovnitř, první ven). Chcete-li si představit stoh, představte si stoh talířů. První talíř na stole bude použit jako poslední a poslední talíř umístěný nahoře bude použit jako první. Zásobníky se často používají v systémovém softwaru, včetně kompilátorů a interpretů. Při práci se zásobníky jsou operace vkládání a vyjímání prvku základní. Tyto operace se tradičně nazývají „push on the stack“ (push) a „pop off the stack“ (pop). Proto, abyste implementovali zásobník, musíte napsat dvě funkce: push(), která vloží hodnotu do zásobníku, a pop(), která posune hodnotu ze zásobníku. Je také nutné přidělit oblast paměti, která bude použita jako zásobník. Za tímto účelem můžete alokovat pole nebo dynamicky alokovat část paměti pomocí funkcí jazyka C poskytovaných pro dynamické přidělování paměti. Stejně jako u fronty, funkce načítání vezme prvek ze seznamu a odstraní jej, pokud není uložen jinde. Následuje obecná podoba funkcí push() a pop(), které pracují s celočíselným polem. Zásobníky dat jiného typu lze uspořádat změnou základního datového typu pole. inttos=0; /* horní část zásobníku */ /* Zatlačte prvek na zásobník. */ void push (int i) if(tos >= MAX) ( printf("Zásobník je plný\n"); /* Získání horního prvku zásobníku. */ pokud (tos< 0) { printf("Zásobník je prázdný\n"); návratový zásobník; Proměnná tos ("vrchol zásobníku") obsahuje index vrcholu zásobníku. Při implementaci těchto funkcí je nutné počítat s případy, kdy je zásobník plný nebo prázdný. V našem případě je znaménko prázdného zásobníku tos rovno nule a znaménko přetečení zásobníku je takové zvýšení tos, že jeho hodnota ukazuje někam za poslední buňku pole. Příklad zásobníku. Zásobník bude umístěn do dynamicky alokované paměti, nikoli do pole s pevnou velikostí. Přestože použití dynamické alokace paměti není v tak jednoduchém příkladu vyžadováno, uvidíme, jak využít dynamickou paměť k ukládání dat zásobníku. /* Jednoduchá kalkulačka se čtyřmi kroky. */ #zahrnout #zahrnout int*p; /* ukazatel na volnou paměť */ int *tos; /* ukazatel na vrchol zásobníku */ int*bos; /* ukazatel na konec zásobníku */ void push(int i); p = (int *) malloc (MAX*velikost(int)); /* získat zásobníkovou paměť */ printf("Chyba při alokaci paměti\n"); bos=p+MAX-1; printf("Čtyřkroková kalkulačka\n"); printf("Stiskněte "q" pro ukončení\n"); printf("%d\n", a+b); printf("%d\n", b-a); printf("%d\n", b*a); printf("Dělení 0.\n"); printf("%d\n", b/a); case ".": /* zobrazí obsah horní části zásobníku */ printf("Aktuální hodnota na vrcholu zásobníku: %d\n", a); ) while(*s != "q"); /* Zatlačení prvku do zásobníku. */ void push (int i) if(p > bos) ( printf("Zásobník je plný\n"); /* Získání horního prvku ze zásobníku. */ pokud (str< tos) { printf("Zásobník je prázdný\n"); Fronta. Fronta je lineární seznam informací, se kterými se zachází na principu první dovnitř, první ven; tento princip (a fronta jako datová struktura) se někdy také nazývá FIFO. To znamená, že první položka umístěná ve frontě bude první položkou, která bude vyřazena z fronty, druhá položka umístěná ve frontě bude druhou položkou, která bude načtena atd. Toto je jediný způsob, jak pracovat s frontou; náhodný přístup k jednotlivým prvkům není povolen. Abychom si představili, jak fronta funguje, uvedeme dvě funkce: qstore() a qretrieve() (z „uložit“ – „uložit“, „načíst“ – „přijmout“). Funkce qstore() umístí prvek na konec fronty a funkce qretrieve() odebere prvek z hlavy fronty a vrátí jeho hodnotu. Tabulka ukazuje účinek sekvence takových operací. Všimněte si, že operace načtení odebere prvek z fronty a zničí jej, pokud není uložen jinde. Proto po extrahování všech prvků bude fronta prázdná. V programování se fronty používají k řešení mnoha problémů. Jedním z nejpopulárnějších typů takových problémů je simulace. Fronty se také používají v plánovačích úloh operačního systému a při ukládání do vyrovnávací paměti I/O. /* Mini plánovač událostí */ #zahrnout #zahrnout #zahrnout #zahrnout char *p, *qretrieve(void); void enter(void), qstore(char *q), review(void), delete_ap(void); for(t=0; t< MAX; ++t) p[t] = NULL; /* иницилизировать массив nulové ukazatele */ printf("Zadejte (E), Seznam (L), Vymažte (R), Konec (Q): "); *s = toupper(*s); /* Vložení nové schůzky do fronty. */ void enter (void) printf("Zadejte schůzku %d: ", spos+1); if(*s==0) break; /* nebyl proveden žádný zápis */ p = (char *) malloc(strlen(s)+1); printf("Nedostatek paměti.\n"); if(*s) qstore(p); /* Zobrazení obsahu fronty. */ void recenze (void) for(t=rpos; t< spos; ++t) printf("%d. %s\n", t+1, p[t]); /* Odebrat schůzku z fronty. */ void delete_ap(void) if((p=qretrieve())==NULL) return; printf("%s\n", p); /* Vložit schůzku. */ void qstore(char*q) printf("Úplný seznam\n"); /* Načtení události. */ char *qretrieve(void) if(rpos==spos) ( printf("Už se nesetkáváme.\n"); vrátit p; Seznam. jednoduše propojený cyklický seznam je rekurzivní deklarace struktur, nebo spíše ukazatel na ni v samotné struktuře typu: int data;//datové pole s *další;//další prvek ) *first,*curr;//první a aktuální prvek Inicializace: prvni->dalsi=curr; pro získání prvního prvku použijte first->data přidání nového prvku: curr->next=new s; curr=curr->next;//přechod na poslední a pro získání například 50 prvků pomocí smyčky iterujte seznam: curr=first;//přeskočit na první for(int i=0;i<50;i++) if(curr->next!=NULL) curr=curr->dalsi; Podobné informace. Obvykle se množství paměti požadované pro konkrétní proměnnou nastavuje před procesem kompilace deklarováním této proměnné. Pokud je potřeba vytvořit proměnnou, jejíž velikost je předem neznámá, použije se dynamická paměť. Rezervace
A uvolnění
Ke ztrátě paměti v programech C++ může dojít kdykoli. Probíhající operace rozdělení
paměť dvěma způsoby: Funkce malloc rezervy souvislý blok paměťových buněk pro uložení zadaného objektu a vrátí ukazatel na první buňku v tomto bloku. Volání funkce vypadá takto: void *malloc(velikost); Tady velikost- celočíselná hodnota bez znaménka, která určuje velikost alokované oblasti paměti v bajtech. Pokud byla rezervace paměti úspěšná, funkce vrátí proměnnou typu neplatný *, který lze přetypovat na libovolný požadovaný typ ukazatele. Funkce - calloc používá se také k alokaci paměti. Položka níže znamená, že bude zvýrazněna č prvky podle velikost byte. void *calloc(nime, velikost); Tato funkce vrací ukazatel na výběr, popř NULA když není možné alokovat paměť. Charakteristickým rysem funkce je nulování všech vybraných prvků. Funkce realloczmění velikost dříve přidělené paměti. Oslovují ji takto: char *realloc (neplatné *p, velikost); Tady p- ukazatel na oblast paměti, jejíž velikost má být změněna velikost. Pokud se v důsledku funkce změní adresa paměťové oblasti, bude jako výsledek vrácena nová adresa. Pokud je skutečná hodnota prvního parametru NULA, pak funkci realloc funguje stejně jako funkce malloc, to znamená, že přiděluje část paměti s velikostí velikost byte. Pro uvolnění přidělené paměti použijte funkci volný, uvolnit. Oslovují ji takto: void free (void *p velikost); Tady p- ukazatel na paměťovou oblast dříve přidělenou funkcemi malloc, calloc nebo realloc. Operátoři Nový A vymazat podobné funkce malloc A volný, uvolnit. Nový alokuje paměť a jeho jediným argumentem je výraz určující počet bajtů, které mají být rezervovány. Vrátí ukazatel operátora na začátek alokovaného paměťového bloku. Operátor vymazat uvolňuje paměť, jeho argumentem je adresa první buňky bloku, která má být uvolněna. dynamické pole- pole proměnné délky, pro které je alokována paměť během provádění programu. Alokaci paměti provádějí funkce calloc, malloc nebo operátor Nový. Adresa prvního prvku alokované paměti je uložena v proměnné deklarované jako ukazatel. Například následující příkaz znamená, že je deklarován ukazatel mas a je mu přiřazena adresa začátku souvislé oblasti dynamické paměti přidělené příkazu Nový: int *mas=new int; Přiděleno tolik paměti, kolik je potřeba k uložení hodnot 10 int. Vlastně v proměnné mas adresa nulového prvku dynamického pole je uložena. Adresa dalšího, prvního prvku v oblasti přidělené paměti je tedy mas+1 a mas+i je adresa i-tého prvku. Přístup k i-tému prvku dynamického pole lze provést jako obvykle mas[i] nebo jiným způsobem *(mas+i)
. Je důležité zajistit, abyste nepřekročili hranice oblasti přidělené paměti. Když dynamické pole (v kterémkoli okamžiku činnosti programu) přestane být potřeba, lze paměť uvolnit pomocí funkce volný, uvolnit nebo operátor vymazat. Navrhuji zvážit několik úkolů, které tuto lekci posílí: Najděte součet skutečných prvků dynamického pole. //Příklad použití funkcí malloc a free #include "stdafx.h" #include //Příklad použití funkcí malloc a free #include "stdafx.h" #zahrnout pomocí jmenného prostoru std ; int main() int i, n; plovák * a; //ukazatel na plovoucí plovák s ; cout<<
"\n"
;
cin
>>n; //zadejte rozměr pole //přidělení paměti pro pole n reálných prvků a = (float * ) malloc (n * sizeof (float) ) ; cout<<
"Zadejte pole A \n";
//vstup prvků pole pro (i = 0; i<
n
;
i
++
)
cin >> * (a + i) ; //akumulace součtu prvků pole pro (s = 0, i = 0; i<
n
;
i
++
)
s += * (a + i); //výstupní hodnota součtu cout<<
"S="
<<
s
<<
"\n"
;
// volná paměť zdarma(a); system("pauza" ); návrat 0; Upravte dynamické pole celých čísel tak, aby se jeho kladné prvky staly zápornými a naopak. Abychom problém vyřešili, vynásobíme každý prvek -1. //Příklad použití operátorů new a delete #include "stdafx.h" #include //Příklad použití operátorů new a delete #include "stdafx.h" #zahrnout pomocí jmenného prostoru std ; int main() setlocale(LC_ALL , "Rus" ) ; int i, n; //zadejte počet prvků pole cout<<
"n="
;
cin
>>n; //přidělení paměti int * a = new int [ n ] ; cout<<
"Zadejte prvky pole:\n";
//vstup pole Při shromažďování informací pro psaní tohoto článku jsem si vzpomněl na své první seznámení s ukazateli - byl tam smutek ... Proto po přečtení několika oddílů na toto téma z různých knih o programování v C++ bylo rozhodnuto jít jinou cestou a představit téma C++ ukazatelů v pořadí, ve kterém to považuji za nutné. Ihned vám dám krátkou definici a v práci zvážíme ukazatele - s příklady. Příští článek () pokryje nuance, použití ukazatelů s řetězci ve stylu C (pole znaků) a hlavní věci, které je třeba si zapamatovat. Ukazatel v C++ je proměnná, která ukládá do paměti adresu dat (hodnot) a ne data samotná.
Po zvážení následujících příkladů pochopíte hlavní věc - proč potřebujeme ukazatele v programování, jak je deklarovat a používat. Předpokládejme, že v programu potřebujeme vytvořit celočíselné pole, jehož přesnou velikost před spuštěním programu neznáme. To znamená, že nevíme, kolik čísel bude uživatel muset přidat do tohoto pole. Samozřejmě můžeme hrát na jistotu a deklarovat pole několika tisíc prvků (například 5 000). To by (podle našeho subjektivního názoru) mělo uživateli k práci stačit. Ano – skutečně – to by mohlo stačit. Nezapomínejme ale, že toto pole zabere hodně místa v RAM (5 000 * 4 (typ int) = 20 000 bajtů). Poté jsme se ujistili a uživatel vyplní pouze 10 prvků našeho pole. Ukazuje se, že 40 bajtů je skutečně v provozu a 19 960 bajtů zabírá paměť marně. nepřiměřené používání paměti RAM #zahrnout #zahrnout pomocí jmenného prostoru std ; int main() const int SizeOfArray = 5000 ; int arrWithDigits [ SizeOfArray ] = ( ) ; cout<<
"Pole obsazeno v paměti"<<
sizeof
(arrWithDigits
)
<<
" байт"
<<
endl
;
int částka = 0 ; cout<<
"Kolik čísel vložíte do pole?";
cin >> množství ; cout<<
"Opravdu potřebné"<<
amount
*
sizeof
(int
)
<<
" байт"
<<
endl
;
for (int i = 0; i<
amount
;
i
++
)
cout<<
i
+
1
<<
"-е число: "
;
cin >> arrWithDigits[ i ] ; cout<<
endl
;
for (int i = 0; i<
amount
;
i
++
)
cout<<
arrWithDigits
[
i
]
<<
" "
;
cout<<
endl
;
návrat 0; Ke standardní funkci knihovny velikost() předání deklarovaného pole arrWithDigitsřádek 10. Vrátí na stránku volání velikost v bajtech, kterou toto pole zabírá v paměti. Na otázku "Kolik čísel zadáte do pole?" odpověď - 10. V řádku 15 výraz množství * velikost (int) se rovná 10 * 4, protože funkce sizeof(int) vrátí 4 (velikost v bajtech typu int). Dále zadejte čísla z klávesnice a program je zobrazí na obrazovce. Ukazuje se, že zbývajících 4990 prvků bude ukládat nuly. Nemá tedy smysl je ukazovat. Hlavní informace na obrazovce: pole zabralo 20 000 bajtů, ale ve skutečnosti potřebuje 40 bajtů. Jak z této situace ven? Možná někdo bude chtít přepsat program tak, aby uživatel zadal velikost pole z klávesnice a po zadání hodnoty deklaroval pole s požadovaným počtem prvků. To se ale neobejde bez ukazatelů. Jak si pamatujete, velikost pole musí být konstantní. To znamená, že celočíselná konstanta musí být inicializována před deklarací pole a nemůžeme požadovat její vstup z klávesnice. Experiment - kontrola. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ V následujícím kódu použijeme ukazatel a operátory, které jsou pro vás nové. Nový(přidělit paměť) a vymazat(uvolňuje paměť). rozumné využití paměti RAM použitím ukazatelů #zahrnout #zahrnout #zahrnout pomocí jmenného prostoru std ; int main() setlocale(LC_ALL , "rus" ) ; int sizeOfArray = 0 ; // velikost pole (vstup uživatele) cout<<
"Chcete-li vytvořit pole čísel, zadejte jeho velikost: ";
cin >> sizeOfArray ; // POZORNOST! int* arrWithDigits - deklarace ukazatele // do oblasti paměti, kterou nový přidělí int * arrWithDigits = new int [ sizeOfArray ] ; for (int i = 0; i<
sizeOfArray
;
i
++
)
arrWithDigits[ i ] = i + 1 ; cout<<
arrWithDigits
[
i
]
<<
" "
;
cout<<
endl
;
smazat arrWithDigits ; // uvolnit paměť návrat 0; Uživatel zadá hodnotu z klávesnice - řádek 12. Ukazatel je definován níže: int * arrWithDigits Tento záznam to znamená arrWithDigits je ukazatel. Byl vytvořen pro uložení adresy buňky, ve které se bude nacházet celé číslo. V našem případě arrWithDigits bude ukazovat na buňku pole s indexem 0. Sign *
- stejné jako při násobení. Z kontextu kompilátor „pochopí“, že se jedná o deklaraci ukazatele, nikoli o násobení. Následuje znamení =
a operátor Nový, který přiděluje oblast paměti. Pamatujeme si, že naše paměť by měla být alokována pro pole, nikoli pro jedno číslo. Záznam nový int[sizeOfArray] lze dešifrovat takto: Nový(přidělit paměť) int(pro ukládání celých čísel)
(ve výši sizeOfArray
). Tak byl definován řádek 16 dynamické pole. To znamená, že paměť pro něj bude alokována (nebo nebude přidělena) během činnosti programu, a nikoli během kompilace, jak je tomu u běžných polí. To znamená, že alokace paměti závisí na vývoji programu a rozhodnutích, která jsou učiněna přímo v jeho práci. V našem případě záleží na tom, co uživatel do proměnné zadá sizeOfArray Řádek 25 používá příkaz vymazat. Uvolňuje přiděleného operátora Nový Paměť. Protože Nový alokovaná paměť pro alokaci pole, pak při jejím uvolnění je potřeba dát kompilátoru jasně najevo, že je potřeba uvolnit paměť pole a ne pouze jeho nulovou buňku, která ukazuje na arrWithDigits. Proto mezi vymazat a název ukazatele je uzavřen v hranatých závorkách –
smazat arrWithDigits ; Je třeba si uvědomit, že pokaždé je paměť alokována pomocí Nový, musíte tuto paměť uvolnit pomocí vymazat. Na konci programu se samozřejmě automaticky uvolní jím obsazená paměť. Udělejte si ale dobrý zvyk používat operátory Nový A vymazat spárovaný s. Koneckonců, program může obsahovat například 5-6 polí. A pokud uvolníte paměť pokaždé, když již nebude v běžícím programu v budoucnu potřeba, bude paměť využita inteligentněji. Řekněme, že jsme v našem programu naplnili pole deseti hodnotami. Pak spočítali svůj součet a zapsali ho do nějaké proměnné. A to je vše – s tímto polem již dále pracovat nebudeme. Program pokračuje v práci a pro některé účely se v něm vytvářejí nová dynamická pole. V tomto případě je vhodné uvolnit paměť obsazenou prvním polem. Poté, při alokaci paměti pro jiná pole, lze tuto paměť znovu použít v programu. Zvažte použití ukazatelů jako parametrů funkce. Chcete-li začít, zadejte a zkompilujte následující kód. V něm funkce přijímá dvě proměnné a nabízí provedení změn jejich hodnot. pokus o změnu proměnných předávaných funkci #zahrnout #zahrnout #zahrnout pomocí jmenného prostoru std ; void changeData (int varForCh1 , int varForCh2 ) ; int main() setlocale(LC_ALL , "rus" ) ; int proměnnáForChange_1 = 0 ; int proměnnáForChange_2 = 0 ; cout<<
"variableForChange_1 = "
<<
variableForChange_1
<<
endl
;
cout<<
"variableForChange_2 = "
<<
variableForChange_2
<<
endl
;
cout<<
endl
;
changeData (variableForChange_1 , variableForChange_2 ) ; cout<<
endl
;
cout<<
"variableForChange_1 = "
<<
variableForChange_1
<<
endl
;
cout<<
"variableForChange_2 = "
<<
variableForChange_2
<<
endl
;
návrat 0; void changeData(int varForCh1 , int varForCh2 ) cout<<
"Zadejte novou hodnotu pro první proměnnou: ";
cin >> varForCh1 ; cout<<
"Zadejte novou hodnotu pro druhou proměnnou: ";
cin >> varForCh2 ; Spusťte program a zadejte nové hodnoty proměnných. Nakonec uvidíte, že na konci funkce se proměnné nezměnily a jsou rovny 0. Jak si pamatujete, funkce nepracuje přímo s proměnnými, ale vytváří jejich přesné kopie. Tyto kopie jsou po ukončení funkce zničeny. To znamená, že funkce přijala jako parametr nějakou proměnnou, vytvořila její kopii, pracovala s ní a zničila ji. Samotná proměnná zůstane nezměněna. Pomocí ukazatelů můžeme předat proměnné adresy do funkce. Poté bude funkce schopna pracovat přímo s danými proměnnými na adrese. Udělejme změny v předchozím programu. změna hodnot proměnných pomocí ukazatelů #zahrnout Poprvé na tomto webu, tak tady. Jsem nováčkem v C++ a momentálně pracuji na knize "Datové struktury pomocí C++ 2nd ed, D.S. Malik". Malik v knize nabízí dva způsoby, jak vytvořit dynamické dvourozměrné pole. V první metodě deklarujete proměnnou jako pole ukazatelů, kde každý ukazatel je typu integer. např. int *deska; A pak použijte for-loop k vytvoření "sloupců" při použití pole ukazatelů jako "řádků". Druhá metoda, použijete ukazatel na ukazatel. Int **board; deska = new int* ; Moje otázka zní: která metoda je lepší? Metoda ** je pro mě snazší na vizualizaci, ale první metodu lze použít v podstatě stejným způsobem. Obě metody lze použít k vytvoření dynamických 2D polí. Edit: Nebylo to dostatečně jasné, jak je uvedeno výše. Zde je kód, který jsem zkoušel: Int row, col; cout<< "Enter row size:";
cin >>řada; cout<< "\ncol:";
cin >>col; int *p_board; for (int i=0; i< row; i++)
p_board[i] = new int;
for (int i=0; i < row; i++)
{
for (int j=0; j < col; j++)
{
p_board[i][j] = j;
cout << p_board[i][j] << " ";
}
cout << endl;
}
cout << endl << endl;
int **p_p_board;
p_p_board = new int* ;
for (int i=0; i < row; i++)
p_p_board[i] = new int;
for (int i=0; i < row; i++)
{
for (int j=0; j < col; j++)
{
p_p_board[i][j] = j;
cout << p_p_board[i][j] << " ";
}
cout << endl;
} 4 odpovědi První způsob nelze použít k vytvoření dynamický 2D pole, protože: int *deska; v podstatě jste alokovali pole 4 ukazatelů na int na zásobníku. Pokud tedy nyní naplníte každý z těchto 4 ukazatelů dynamickým polem: Pro (int i = 0; i< 4; ++i) {
board[i] = new int;
}
to, co skončíte, je 2D pole statický počet řádků (v tomto případě 4) a dynamický počet sloupců (v tomto případě 10). Tedy dynamika plně, protože když alokujete pole na zásobníku, vy musí naznačit pevná velikost, tj. Známý v čas. Dynamický pole se nazývá dynamický, protože jeho velikost nemusí být známa doba kompilace, ale spíše může být definován nějakou proměnnou v za běhu. Ještě jednou, když uděláte: int *deska; Const int x = 4; //<--- `const` qualifier is absolutely needed in this case!
int *board[x];
poskytujete konstantu známou v doba kompilace(v tomto případě 4 nebo x), takže kompilátor nyní může předem vybrat tuto paměť pro vaše pole, a když se váš program nahraje do paměti, bude již mít takové množství paměti pro pole desky, proto se nazývá statický, tj. protože velikost pevně zakódované A nelze dynamicky měnit(během exekuce). Na druhou stranu, když uděláte: Int **board; deska = new int*; int x = 10; //<--- Notice that it does not have to be `const` anymore!
int **board;
board = new int*[x];
kompilátor neví, kolik paměťového pole bude potřebovat, a tak to neví přiděluje předem Všechno. Ale když spustíte svůj program, velikost pole bude určena hodnotou proměnné x (za běhu) a odpovídající místo pro pole desky bude přiděleno tzv. chomáč- oblast paměti, kam se mohou alokovat všechny programy běžící na vašem počítači předem neznámý(v době kompilace) součet paměti pro osobní použití. V důsledku toho, abyste skutečně vytvořili dynamické 2D pole, musíte jít s druhou metodou: Int **board; deska = new int*; // dynamické pole (velikost 10) ukazatelů na int for (int i = 0; i< 10; ++i) {
board[i] = new int;
// each i-th pointer is now pointing to dynamic array (size 10) of actual int values
}
Právě jsme vytvořili čtvercové 2D pole 10 x 10. Abychom jej prošli a naplnili skutečnými hodnotami, jako je 1, mohli bychom použít vnořené smyčky: Pro (int i = 0; i< 10; ++i) { // for each row
for (int j = 0; j < 10; ++j) { // for each column
board[i][j] = 1;
}
} To, co popisujete pro druhou metodu, dává pouze 1D pole: int *board = new int; Toto pouze přidělí pole s 10 prvky. Možná jsi měl na mysli něco takového: Int **board = new int*; for (int i = 0; i< 4; i++) {
board[i] = new int;
}
V tomto případě alokujeme 4 int* s a pak každý bod přiřadíme dynamicky alokovanému poli 10 int s. Takže teď to porovnáváme s int* boardem; . Hlavní rozdíl je v tom, že při použití takového pole musí být v době kompilace znám počet „řádků“. Je to proto, že pole mají mít pevnou velikost doby kompilace. Problém můžete mít také, pokud chcete případně vrátit toto pole z int* s, protože pole bude na konci svého rozsahu zničeno. Metoda, která dynamicky přiděluje řádky i sloupce, vyžaduje sofistikovanější opatření, aby se zabránilo úniku paměti. Paměť musíte uvolnit takto: Pro (int i = 0; i< 4; i++) {
delete board[i];
}
delete board;
Místo toho bych měl doporučit použití standardní nádoby. Můžete použít std::array V obou případech může být váš vnitřní rozměr nastaven dynamicky (tj. převzat z proměnné), ale rozdíl je ve vnější dimenzi. Tato otázka je v zásadě ekvivalentní následujícímu: je int* x = new int; "lepší" než int x ? Odpověď je "ne, pokud nepotřebujete vybrat velikost pole dynamicky." Tento kód funguje dobře s velmi malým počtem požadavků na externí knihovny a ukazuje základní použití int **array . Tato odpověď ukazuje, že pole každý má dynamickou velikost a také jak přiřadit dynamicky velké lineární pole k poli větví s dynamickou velikostí. Tento program přebírá argumenty ze STDIN v následujícím formátu: 2 2
3 1 5 4
5 1 2 8 9 3
0 1
1 3
Kód programu je níže... #zahrnout Toto je velmi jednoduchá implementace int main a závisí pouze na std::cin a std::cout . Barebones, ale dost dobrý na to, aby ukázal, jak pracovat s jednoduchými vícerozměrnými poli. Účel přednášky: studovat deklarace, alokaci a uvolnění paměti pro jednorozměrná dynamická pole, přístup k prvkům, naučit se řešit problémy pomocí jednorozměrných dynamických polí v jazyce C++. Při použití mnoha datových struktur často postačí, že během nich musí mít proměnlivou velikost dodací lhůta programy. V těchto případech je nutné aplikovat dynamická alokace paměti. Jednou z nejběžnějších takových datových struktur jsou pole, ve kterých zpočátku není velikost definována a není pevně daná. Podle jazykový standard pole je kolekce prvků, z nichž každý má stejné atributy. Všechny tyto prvky jsou umístěny v sousedních paměťových oblastech v řadě, počínaje adresou odpovídající začátku pole. To znamená, že celkový počet prvků pole a velikost pro něj přidělené paměti jsou získány zcela a jednoznačně dané definicí pole. Ale to není vždy pohodlné. Někdy je potřeba, aby alokovaná paměť pro pole byla dimenzována pro vyřešení konkrétního problému a její množství není předem známo a nelze ji opravit. Vytváření polí s proměnnými velikostmi (dynamická pole) lze organizovat pomocí ukazatelů a nástrojů dynamická alokace paměti. dynamické pole je pole, jehož velikost není předem pevně dána a může se během provádění programu měnit. Chcete-li změnit velikost dynamické pole programovací jazyk C++, který podporuje taková pole, poskytuje speciální vestavěné funkce nebo operace. Dynamická pole umožňují flexibilnější práci s daty, protože umožňují nepředvídat objemy uložených dat, ale upravit velikost pole podle skutečně požadovaných objemů. Pod deklarací jednorozměrné dynamické pole rozumět deklaraci ukazatele na proměnnou daného typu tak, aby tato proměnná mohla být použita jako dynamické pole. Syntaxe: Typ * Název pole; Typ – typ deklarovaných prvků dynamické pole. Elementy dynamické pole nemohou existovat funkce a prvky typu void . Například: int*a; dvojité *d; V těchto příkladech jsou a a d ukazatele na začátek oblasti přidělené paměti. Ukazatele přebírají hodnotu adresy alokované oblasti paměti pro hodnoty typu int a typu double. Při dynamické alokaci paměti pro dynamická pole byste tedy měli popsat odpovídající ukazatel , kterému bude přiřazena hodnota adresy začátku oblasti alokované paměti. Aby bylo možné alokovat paměť pro jednorozměrné dynamické pole V jazyce C++ existují 2 způsoby. 1) s pomocí operace new , který přiděluje část dynamické paměti vhodné velikosti pro umístění pole a neumožňuje inicializovat prvky pole. Syntaxe: ArrayName = nový typ [ConstantTypeExpression]; ArrayName - identifikátor pole, tj. název ukazatele na alokovaný paměťový blok. ExpressionTypeConstants– nastavuje počet prvků ( rozměr) pole. Výraz konstantního typu je vyhodnocen v době kompilace. Například: int *mas; mas = new int ; /* dynamická alokace paměti 100*velikost(int) bajtů*/ double *m = new double [n]; /*přidělí n*velikost(dvojitých) bajtů dynamické paměti*/ long (*lm); lm = nový dlouhý ; /*přidělit 2*4*velikost (dlouhých) bajtů dynamické paměti*/ Při přidělování dynamické paměti musí být plně definovány rozměry pole. 2) pomocí funkce knihovny malloc (calloc) , který se používá k alokaci dynamické paměti. Syntaxe: Název pole = (Typ *) malloc(N*velikost(Typ)); Název pole = (Typ *) calloc(N, sizeof(Typ)); ArrayName - identifikátor pole, tj. název ukazatele na alokovaný paměťový blok. Typ je typ ukazatele na pole. N je počet prvků pole. Například: plovák *a; a=(float *)malloc(10*sizeof(float)); // nebo a=(float *)calloc(10,sizeof(float)); /*přidělení velikosti dynamické paměti 10*velikost (float) bajtů*/ Protože funkce malloc(calloc) vrací netypovaný ukazatel void * , pak musíte výsledek převést
}
- alokovat blok RAM pro pole ukazatelů;
- alokovat bloky RAM pro jednorozměrná pole, což jsou řádky požadované matice;
- zapsat adresy řetězců do pole ukazatelů.
#zahrnout
#zahrnout
void main()
{
}
text;
binární.
Základní operace prováděné se soubory:
1.Otevírání souborů.
2. Čtení a zápis dat.
3. Zavírání souborů.
1.Navigace v souboru.
2. Řešení chyb práce se soubory.
3.Smažte a přejmenujte soubory.
4. Popis proměnné
FILE * freopen (const char *název souboru, const char *režim, FILE *stream);
Ukazatel souboru - vše je v pořádku,
NULL je chyba předefinování.
int fclose(FILE *stream);
0 - soubor byl úspěšně uzavřen.
1 - Při zavírání souboru došlo k chybě.
intfeof(FILE*stream);
stream je ukazatel na otevřený soubor.
0 - pokud ještě nebylo dosaženo konce souboru.
!0 – Dosažen konec souboru.
Druhý parametr navíc specifikuje znak t (volitelné):
rt, wt, at, rt+, wt+, at+
int fscanf(FILE *stream, const char * formát, ...);
>0 – počet úspěšně načtených proměnných,
0 - žádná z proměnných nebyla úspěšně načtena,
EOF - chyba nebo konec souboru dosažen.
Čtení řádku
buffer - vše je v pořádku,
Čtení řádku
char * fgets(char * buffer, int maxlen, FILE *stream);
buffer - vše je v pořádku,
NULL - chyba nebo konec souboru dosažen.
Čtení postavy
int fgetc(FILE *stream);
Funkce vrací:
znakový kód - pokud je vše v pořádku,
EOF - pokud dojde k chybě nebo je dosaženo konce souboru.
Vložení postavy zpět do streamu
int ungetc(int c, SOUBOR *stream);
Funkce vrací:
znakový kód - pokud je vše úspěšné,
EOF Došlo k chybě.
int fprintf(FILE *stream, const char *format, ...);
Funkce vrací:
počet napsaných znaků - pokud je vše v pořádku,
záporná hodnota - v případě chyby.
Zadání řetězce
int fputs(const char *řetězec, FILE *stream);
Funkce vrací:
počet napsaných znaků - vše v pořádku,
EOF Došlo k chybě.
Zadání symbolu
int fputc(int c, SOUBOR *stream);
Funkce vrací:
kód psaného znaku - vše je v pořádku,
EOF Došlo k chybě.
Otevírání binárních souborů
Druhý parametr navíc specifikuje znak b (povinný): rb, wb, ab, rb+, wb+, ab+
Čtení z binárních souborů
size_t fread(void *buffer, size_t size, size_t num,FILE *stream);
Funkce vrací počet přečtených bloků. Pokud je menší než num, došlo k chybě nebo
konec souboru.
size_t fwrite(const void *buffer, size_t size, size_t num, FILE *stream);
Funkce vrací počet zapsaných bloků. Pokud je menší než num, došlo k chybě.
long int ftell(FILE *stream);
Změna aktuálního offsetu v souboru:
int fseek(FILE *stream, long int offset, int origin);
SEEK_CUR (1) - z aktuální pozice.
SEEK_END (2) – od konce souboru.
Funkce vrací:
0 - vše v pořádku,
!0 - došlo k chybě.
Přesunout na začátek souboru:
void přetočit (FILE *stream);
Čtení aktuální pozice v souboru:
int fgetpos(FILE *stream, fpos_t *pos);
Nastavení aktuální pozice v souboru:
int fsetpos(FILE *stream, const fpos_t *pos);
Funkce vrací:
0 - vše je úspěšné,
!0 - došlo k chybě.
struktura fpos_t:
typedef struct fpos_t (
dlouho pryč;
mbstate_t wstate;
) fpos_t;
int ferror(FILE *stream);
Pokud dojde k chybě, funkce vrátí nenulovou hodnotu.
Funkce resetování chyby:
void clearerr(FILE *stream);
Funkce výstupu chybové zprávy:
void perror(const char *string);
int flush(FILE *stream);
Funkce vrací:
0 - vše v pořádku.
EOF Došlo k chybě.
Funkce správy vyrovnávací paměti:
void setbuf(FILE *stream, char * buffer);
SOUBOR * tmpfile(void);
Vytvoří dočasný soubor v režimu wb+. Po zavření souboru se soubor automaticky odstraní.
Funkce dočasného generování názvu souboru:
char * tmpnam(char *buffer);
int remove(const char *název souboru);
Funkce přejmenování souboru:
int rename(const char *fname, const char *nname);
Funkce vrací:
0 - v případě úspěchu,
!0 jinak.
Akce Obsah fronty
qstore(A) A
qstore(B) A B
qstore(C) A B C
qretrieve() vrací A PŘED NAŠÍM LETOPOČTEM
qstore(D) B C D
qretrieve() vrátí B C D
qretrieve() vrátí C D
Úkol 1
Úkol 2
Zde nás operátor zvýrazní červeně >>
protože nemůžete změnit konstantní hodnotu.
Zde jsme varováni, že velikost pole NEMŮŽE být hodnotou obyčejné proměnné. Je vyžadována konstantní hodnota!
Deklarace jednorozměrných dynamických polí
Alokace paměti pro jednorozměrné dynamické pole