Dynamisk array-deklaration c. Dynamisk tvådimensionell array med pekare till pekare


Syftet med föreläsningen: att studera deklarationer, allokering och frigöring av minne för endimensionella dynamiska arrayer, komma åt element, att lära sig hur man löser problem med endimensionella dynamiska arrayer i C++-språket.

När man använder många datastrukturer händer det ofta att de måste variera i storlek under ledtid program. I dessa fall är det nödvändigt att ansöka dynamisk tilldelning minne... En av de vanligaste sådana datastrukturerna är arrayer, där storleken initialt inte är definierad och fixerad.

Enligt språkstandard en array är en samling element, var och en med samma attribut. Alla dessa element är placerade i angränsande minnesområden i rad, med början från adressen som motsvarar början av arrayen. Det vill säga det totala antalet arrayelement och storleken på minnet som tilldelats för det erhålls helt och otvetydigt definierade array. Men detta är inte alltid bekvämt. Ibland krävs det att det tilldelade minnet för arrayen är dimensionerat för att lösa ett specifikt problem, och dess storlek är inte känd i förväg och kan inte fixas. Bildandet av arrayer med varierande storlekar (dynamiska arrayer) kan organiseras med hjälp av pekare och verktyg dynamisk minnesallokering.

Dynamisk arrayÄr en array vars storlek inte är fixerad i förväg och kan ändras under programkörning. För att ändra storlek dynamisk array programmeringsspråk C ++ stödjande arrayer som denna ger speciella inbyggda funktioner eller operation. Dynamiska arrayer tillåter mer flexibelt arbete med data, eftersom de tillåter att inte förutsäga de lagrade datavolymerna, utan att justera storleken på arrayen i enlighet med de faktiska erforderliga volymerna.

Deklarera endimensionella dynamiska arrayer

Enligt deklarationen om endimensionell dynamisk array förstå deklarationen av en pekare till en variabel av en given typ så att denna variabel kan användas som dynamisk array.

Syntax:

Skriv * ArrayName;

Typ - typen av element som deklareras dynamisk array... Element dynamisk array funktioner och medlemmar av tomrumstypen kan inte vara.

Till exempel:

int * a; dubbel * d;

I dessa exempel är a och d pekare till början av det tilldelade minnesområdet. Pekare tar värdet på adressen till det tilldelade minnesområdet för värden av typ int respektive typ dubbel.

Sålunda, när du dynamiskt allokerar minne för dynamiska arrayer, bör du beskriva motsvarande pekare, som kommer att tilldelas värdet på adressen till början av det allokerade minnesområdet.

Tilldela minne för en endimensionell dynamisk array

För att allokera minne för endimensionell dynamisk array det finns 2 sätt i C++-språket.

1) genom operation new, som tilldelar en plats för att placera arrayen dynamiskt minne av lämplig storlek och tillåter inte att arrayelementen initieras.

Syntax:

ArrayName = ny typ [ConstantTypeExpression];

ArrayName - identifieraren för arrayen, det vill säga namnet på pekaren för det allokerade minnesblocket.

ConstantTypeExpression- ställer in antalet element ( dimension) av arrayen... Ett uttryck av konstant typ utvärderas vid kompilering.

Till exempel:

int * mas; mas = ny int; / * tilldelning av dynamiskt minne av storlek 100 * storlek på (int) bytes * / dubbel * m = ny dubbel [n]; / * tilldelning av dynamiskt minne av storlek n * storlek på (dubbla) byte * / lång (* lm); lm = ny lång; / * tilldelning av dynamiskt minne av storlek 2 * 4 * storlek på (långa) byte * /

Vid allokering av dynamiskt minne måste storleken på arrayen vara fullständigt specificerad.

2) med hjälp av en biblioteksfunktion malloc (calloc), som används för att allokera dynamiskt minne.

Syntax:

ArrayName = (Typ *) malloc (N * storleken på (Typ));

ArrayName = (Typ *) calloc (N, storlek på (Typ));

ArrayName - identifieraren för arrayen, det vill säga namnet på pekaren för det allokerade minnesblocket.

Typ - typen av pekare till arrayen.

N är antalet element i arrayen.

Till exempel:

flyta * a; a = (float *) malloc (10 * sizeof (float)); // eller a = (float *) calloc (10, sizeof (float)); / * tilldelning av dynamiskt minne av storlek 10 * storlek på (flytande) bytes * /

Eftersom malloc (calloc) funktionen returnerar oskriven pekare void *, då är det nödvändigt att utföra omvandlingen av det mottagna

Genom att samla information för att skriva den här artikeln kom jag ihåg min första bekantskap med pekare - det fanns sorg, sorg ... Därför, efter att ha läst flera avsnitt om detta ämne från olika böcker om C ++-programmering, beslutades det att gå åt andra hållet och presentera C++-pekare i den ordning jag anser det nödvändigt. Jag kommer omedelbart att ge dig en kort definition och vi kommer att överväga tips i arbetet - med exempel. Nästa artikel () kommer att täcka nyanserna, användningen av pekare med strängar i C-stil (teckenmatriser) och de viktigaste sakerna att komma ihåg.

En pekare i C++ är en variabel som lagrar adressen till data (värden) i minnet, och inte själva data.

Efter att ha övervägt följande exempel, du kommer att förstå det viktigaste - varför behöver vi pekare i programmering, hur man deklarerar och använder dem.

Låt oss säga att vi i ett program måste skapa en heltalsmatris, vars exakta storlek vi inte vet innan vi startar programmet. Det vill säga, vi vet inte hur många nummer användaren behöver för att ange i denna matris. Naturligtvis kan vi spela det säkert och deklarera en array med flera tusen element (till exempel 5000). Detta borde (enligt vår subjektiva uppfattning) vara tillräckligt för att användaren ska fungera. Ja – verkligen – det kan räcka. Men låt oss inte glömma att denna array kommer att ta in random access minne mycket utrymme (5000 * 4 ( int typ) = 20 000 byte). Vi försäkrade oss sedan, och användaren kommer bara att fylla i 10 delar av vår array. Det visar sig att i verkligheten är 40 byte i drift, och 19 960 byte slösar minne.

orimlig användning av RAM

#omfatta använder namnutrymme std; int main () (setlocale (LC_ALL, "rus"); const int SizeOfArray = 5000; int arrWithDigits = (); cout<< "Массив занял в памяти " << sizeof(arrWithDigits) << " байт" << endl; int amount = 0; cout << "Сколько чисел вы введёте в массив? "; cin >> mängd; cout<< "Реально необходимо " << 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; return 0; }

#omfatta

använder namnutrymme std;

int main ()

const int SizeOfArray = 5000;

int arrWithDigits [SizeOfArray] = ();

cout<< "Array upptagen i minnet"<< sizeof (arrWithDigits ) << " байт" << endl ;

int belopp = 0;

cout<< "Hur många nummer kommer du att lägga in i arrayen?";

cin >> mängd;

cout<< "Verkligen nödvändigt"<< amount * sizeof (int ) << " байт" << endl ;

för (int i = 0; i< amount ; i ++ )

cout<< i + 1 << "-е число: " ;

cin >> arrWithDigits [i];

cout<< endl ;

för (int i = 0; i< amount ; i ++ )

cout<< arrWithDigits [ i ] << " " ;

cout<< endl ;

returnera 0;

In i en standard biblioteksfunktion storlek av () passerar den deklarerade arrayen arrWithDigits rad 10. Den kommer att returnera i stället för anropet storleken i byte som denna array upptar i minnet. Till frågan "Hur många nummer kommer du att lägga in i arrayen?" svar - 10. På rad 15, uttrycket mängd * storlek på (int) blir lika med 10 * 4, eftersom funktionen storlek på (int) returnerar 4 (storlek i byte av typen int). Ange sedan siffrorna från tangentbordet och programmet kommer att visa dem på skärmen. Det visar sig att de återstående 4990 elementen kommer att lagra nollor. Så det är ingen idé att visa dem.

Huvudinformationen på skärmen: arrayen tog upp 20 000 byte, men i verkligheten behöver den 40 byte. Hur tar man sig ur denna situation? Kanske kommer någon att vilja skriva om programmet så att användaren anger storleken på arrayen från tangentbordet och, efter att ha angett värdet, deklarerar en array med det nödvändiga antalet element. Men detta kan inte göras utan tips. Som du kommer ihåg måste arrayens storlek vara konstant. Det vill säga, en heltalskonstant måste initieras innan arrayen deklareras och vi kan inte begära dess input från tangentbordet. Experiment - kolla.


Här är operatören rödmarkerad för oss >> eftersom du inte kan ändra det konstanta värdet.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


Här varnas vi för att storleken på en array INTE kan vara värdet på en vanlig variabel. Konstant värde krävs!

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I följande kod kommer vi att använda en pekare och nya operatorer åt dig ny(tilldelar minne) och radera(frigör minne).

förnuftig användning av RAM med hjälp av pekare

#omfatta #omfatta använder namnutrymme std; int main () (setlocale (LC_ALL, "rus"); int sizeOfArray = 0; // storleken på arrayen (anges av användaren) cout<< "Чтобы создать массив чисел, введите его размер: "; cin >> sizeOfArray; // OBS! int * arrWithDigits - deklaration av en pekare // till en minnesbit som kommer att allokeras av new int * arrWithDigits = new int; för (int i = 0; i< sizeOfArray; i++) { arrWithDigits[i] = i + 1; cout << arrWithDigits[i] << " "; } cout << endl; delete arrWithDigits; // освобождение памяти return 0; }

#omfatta

#omfatta

använder namnutrymme std;

int main ()

setlocale (LC_ALL, "rus");

int sizeOfArray = 0; // arraystorlek (användarinmatning)

cout<< "För att skapa en matris med siffror, ange dess storlek:";

cin >> sizeOfArray;

// OBS! int * arrWithDigits - pekardeklaration

// till en bit minne som nya kommer att allokera

int * arrWithDigits = ny int [sizeOfArray];

för (int i = 0; i< sizeOfArray ; i ++ )

arrWithDigits [i] = i + 1;

cout<< arrWithDigits [ i ] << " " ;

cout<< endl ;

ta bort arrWithDigits; // ledigt minne

returnera 0;

Användaren anger ett värde från tangentbordet - rad 12. Nedan är en pekare definierad: int * arrWithDigits Det här inlägget betyder det arrWithDigitsÄr en pekare. Den skapades för att lagra adressen till cellen där heltalet kommer att finnas. I vårat fall arrWithDigits kommer att peka på arraycellen med index 0. Sign * - samma som används för multiplikation. Genom sammanhanget kommer kompilatorn att "förstå" att detta är en pekardeklaration, inte en multiplikation. Detta följs av en skylt = och operatör ny som allokerar en bit minne. Vi kommer ihåg att vårt minne måste allokeras för en array och inte för ett enda nummer. Inspelning new int [sizeOfArray] kan tydas så här: ny(tilldela minne) int(för att lagra heltal) (i mängden sizeOfArray ).

Således definieras linje 16 dynamisk array... Detta innebär att minne för det kommer att allokeras (eller inte allokeras) medan programmet körs, och inte under kompilering, som är fallet med vanliga arrayer. Det vill säga att minnesallokering beror på utvecklingen av programmet och beslut som fattas direkt i dess arbete. I vårt fall beror det på vad användaren anger i variabeln sizeOfArray

Linje 25 tillämpar operatören radera... Det frigör de tilldelade av operatören ny minne. Eftersom ny tilldelat minne för placeringen av arrayen, då när den släpps är det nödvändigt att göra det klart för kompilatorn att det är nödvändigt att frigöra minnet för arrayen, och inte bara dess nollcell, som den pekar på arrWithDigits... Alltså mellan radera och namnet på pekaren sätts inom hakparenteser ta bort arrWithDigits; Kom ihåg att varje gång minne tilldelas med ny, måste du frigöra detta minne med hjälp av radera... Naturligtvis, när programmet slutar, kommer minnet som det tog upp automatiskt att frigöras. Men gör det till en god vana för dig att använda operatörerna ny och radera parad med. Faktum är att programmet kan innehålla 5-6 arrayer, till exempel. Och om du frigör minne, varje gång det inte längre behövs i framtiden i ett pågående program, kommer minnet att användas mer klokt.

Låt oss säga att vi i vårt program har fyllt en array med tio värden. Sedan beräknade vi deras summa och skrev ner den till någon variabel. Och det är allt - vi kommer inte längre att arbeta med den här arrayen. Programmet fortsätter att fungera och nya dynamiska arrayer skapas i det för något syfte. I det här fallet är det tillrådligt att frigöra minnet som upptas av den första arrayen. Sedan, när man allokerar minne för andra arrayer, kan detta minne återanvändas i programmet.

Låt oss överväga att använda pekare som funktionsparametrar. Skriv och kompilera först följande kod. I den tar funktionen emot två variabler och föreslår ändringar av deras värden.

försöker ändra variablerna som skickas till funktionen

#omfatta #omfatta använder namnutrymme std; void changeData (int varForCh1, int varForCh2); int main () (setlocale (LC_ALL, "rus"); int variabelForChange_1 = 0; int variabelForChange_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; return 0; } void changeData(int varForCh1, int varForCh2) { cout << "Введите новое значение первой переменной: "; cin >> varForCh1; cout<< "Введите новое значение второй переменной: "; cin >> varForCh2; )

#omfatta

#omfatta

använder namnutrymme std;

void changeData (int varForCh1, int varForCh2);

int main ()

setlocale (LC_ALL, "rus");

int variabelForChange_1 = 0;

int variabelForChange_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 ;

returnera 0;

void changeData (int varForCh1, int varForCh2)

cout<< "Ange det nya värdet för den första variabeln:";

cin >> varForCh1;

cout<< "Ange det nya värdet för den andra variabeln:";

cin >> varForCh2;

Kör programmet och ange de nya värdena för variablerna. Som ett resultat kommer du att se att när funktionen är klar har variablerna inte ändrats och är lika med 0.

Som ni minns fungerar funktionen inte direkt med variabler utan skapar deras exakta kopior. Dessa kopior förstörs när funktionen avslutas. Det vill säga, funktionen fick någon variabel som parameter, skapade en kopia av den, arbetade med den och förstörde den. Variabeln i sig förblir oförändrad.

Med hjälp av pekare kan vi skicka variabla adresser till funktionen. Då kommer funktionen att kunna arbeta direkt med variablernas data på adressen. Låt oss göra ändringar i det tidigare programmet.

ändra värdena på variabler med hjälp av pekare

#omfatta #omfatta använder namnutrymme std; void changeData (int * varForCh1, int * varForCh2); int main () (setlocale (LC_ALL, "rus"); int variabelForChange_1 = 0; int variabelForChange_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; return 0; } void changeData(int* varForCh1, int* varForCh2) { cout << "Введите новое значение первой переменной: "; cin >> * varForCh1; cout<< "Введите новое значение второй переменной: "; cin >> * varForCh2; )

Vanligtvis ställs mängden minne som krävs för en variabel in redan före kompileringsprocessen genom att deklarera denna variabel. Om det finns ett behov av att skapa en variabel, vars storlek är okänd i förväg, används heapminne. Reservation och befrielse minne i C++-program kan förekomma när som helst. Verksamhet pågår distribution minne på två sätt:

  • använder funktionen malloc, calloc, realloc och fri;
  • genom operatören ny och radera.

Fungera malloc reserver ett sammanhängande block av minnesplatser för att hålla det specificerade objektet och returnerar en pekare till den första platsen för det blocket. Ett funktionsanrop ser ut så här:

void * malloc (storlek);

Här storlek- heltalsvärde utan tecken som bestämmer storleken på det tilldelade minnesområdet i byte. Om minnesreservationen lyckades, returnerar funktionen en variabel av typen tomhet * som kan gjutas till vilken pekare som helst.

Funktion - callocäven avsedd för minnesallokering. Posten nedan betyder att den kommer att markeras num element av storlek byte.

void * calloc (nime, storlek);

Denna funktion returnerar en pekare till markeringen, eller NULL om det är omöjligt att allokera minne. En funktion hos funktionen är nollställningen av alla valda element.

Fungera reallocändrar storlek tidigare tilldelat minne. De tilltalar henne så här:

char * realloc (tomt * p, storlek);

Här sid- en pekare till minnesområdet, vars storlek måste ändras till storlek... Om, som ett resultat av funktionen, adressen till minnesområdet ändras, kommer den nya adressen att returneras som ett resultat. Om det faktiska värdet för den första parametern NULL, sedan funktionen realloc fungerar på samma sätt som funktionen malloc, det vill säga allokerar en bit minne av storlek storlek byte.

För att frigöra det tilldelade minnet, använd funktionen fri... De tilltalar henne så här:

void free (void * p storlek);

Här sid- en pekare till ett minnesområde som tidigare allokerats av funktioner malloc, calloc eller realloc.

Operatörer ny och radera liknar funktioner malloc och fri. Ny allokerar minne, och dess enda argument är ett uttryck som anger antalet byte som ska reserveras. Operatören returnerar en pekare till början av det tilldelade minnesblocket. Operatör radera frigör minne, dess argument är adressen till den första cellen i blocket som ska frigöras.

Dynamisk array- en array med variabel längd, vars minne tilldelas under körningen av programmet. Minnestilldelning utförs av funktionerna calloc, malloc eller av operatör ny... Adressen för det första elementet i det allokerade minnesområdet lagras i en variabel som deklareras som en pekare. Till exempel betyder följande påstående att en pekare beskrivs mas och den tilldelas adressen för början av den sammanhängande regionen av högen som tilldelas med hjälp av operatören ny:

int * mas = ny int;

Så mycket minne tilldelas som behövs för att lagra 10 värden av typen int.

Faktiskt i variabeln mas adressen för nollelementet i den dynamiska matrisen lagras. Därför är adressen för nästa, första element i det allokerade minnesområdet mas+1, och mas+ i är adressen för det i-te elementet. Åtkomst till det i:te elementet i en dynamisk array kan utföras, som vanligt mas [i], eller på annat sätt *(mas + i) ... Det är viktigt att se till att inte gå utanför gränserna för det tilldelade minnesområdet.

När en dynamisk array (när som helst av programoperationen) inte längre behövs, kan minnet frigöras med funktionen fri eller operatör radera.

Jag föreslår att överväga flera uppgifter som förstärker denna lektion:

Problem 1

Hitta summan av verkliga element i en dynamisk array.

// Ett exempel på användning av malloc och gratisfunktionen #include "stdafx.h" #include använder namnutrymme std; int main () (setlocale (LC_ALL, "Rus"); int i, n; float * a; // pekare till float float s; cout<<"\n"; cin>> n; // mata in storleken på arrayen // minnesallokering för en array av n reella element a = (float *) malloc (n * sizeof (float)); cout<<"Введите массив A \n"; //ввод элементов массива for (i=0; i> * (a + i); ) // ackumulering av summan av matriselement för (s = 0, i = 0; i

// Ett exempel på användning av malloc och gratis-funktionen

#inkludera "stdafx.h"

#omfatta

använder namnutrymme std;

int main ()

int i, n;

flyta * a; // pekare att flyta

flyta s;

cout<< "\n" ; cin >> n; // mata in dimensionen för arrayen

// minnesallokering för en array av n reella element

a = (float *) malloc (n * sizeof (float));

cout<< "Ange array A \ n";

// input array element

för (i = 0; i< n ; i ++ )

cin >> * (a + i);

// ackumulering av summan av arrayelement

för (s = 0, i = 0; i< n ; i ++ )

s + = * (a + i);

// visa värdet på summan

cout<< "S=" << s << "\n" ;

// ledigt minne

fri (a);

system ("paus");

returnera 0;

Uppgift 2

Ändra en dynamisk array av heltal så att dess positiva element blir negativa och vice versa. För att lösa problemet multiplicerar vi varje element med -1.

// Ett exempel på att använda operatorerna new och delete #include "stdafx.h" #include använder namnutrymme std; int main () (setlocale (LC_ALL, "Rus"); int i, n; // mata in antalet element i cout-matrisen<<"n="; cin>> n; // minnesallokering int * a = ny int [n]; cout<<"Введите элементы массива:\n"; //ввод массива for (i=0; i> a [i]; // mata ut den givna matrisen för (i = 0; i

// Ett exempel på att använda operatorerna new och delete

#inkludera "stdafx.h"

#omfatta

använder namnutrymme std;

int main ()

setlocale (LC_ALL, "Rus");

int i, n;

// ange antalet arrayelement

cout<< "n=" ; cin >> n;

// minnesallokering

int * a = ny int [n];

cout<< "Ange arrayelement: \ n";

// input array

Första timern på denna webbplats, så här kommer.

Jag är ny på C ++ och jag arbetar för närvarande med boken Data Structures Using C ++ 2nd ed, D.S. Malik.

I boken föreslår Malik två sätt att skapa en dynamisk tvådimensionell array. I den första metoden deklarerar du variabeln som en array av pekare, där varje pekare är av typen heltal. ex.

Int * board;

Och använd sedan en for-loop för att skapa "kolumner" samtidigt som du använder en rad pekare som "strängar".

Den andra metoden, du använder en pekare till en pekare.

Int ** board; board = new int *;

Min fråga är vilken metod är bättre? Metod ** är lättare för mig att visualisera, men den första metoden kan användas på ungefär samma sätt. Båda metoderna kan användas för att skapa dynamiska 2-dimensionella arrayer.

Edit: var inte tillräckligt tydlig som sagt ovan. Detta är koden jag provade:

Int rad, kol; cout<< "Enter row size:"; cin >> rad; cout<< "\ncol:"; cin >> kol; int * p_board; för (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 svar

Den första metoden kan inte användas för att skapa dynamisk 2D-matriser eftersom:

Int * board;

du har i princip allokerat en array med 4 pekare till int på traven... Därför, om du nu fyller var och en av dessa 4 pekare med en dynamisk array:

För (int i = 0; i< 4; ++i) { board[i] = new int; }

det du slutar med är en 2D-array med statisk antalet rader (i detta fall 4) och dynamisk antalet kolumner (i detta fall 10). Det är alltså inte dynamiken fullt sedan när du tilldelar en array på stacken du måste påpeka konstant storlek, dvs. Berömd i tid. Dynamisk arrayen kallas dynamisk eftersom dess storlek inte behöver vara känd i sammanställningstid utan kan snarare definieras av någon variabel i vid körning.

Återigen när du gör:

Int * board;

Const int x = 4; //<--- `const` qualifier is absolutely needed in this case! int *board[x];

du ger en konstant känd i sammanställningstid(i detta fall 4 eller x) så att kompilatorn kan nu förtilldela detta minne för din array, och när ditt program laddas in i minnet kommer det redan att ha detta minne för kortets array, så det kallas statisk, dvs. eftersom storleken hårdkodad och kan inte ändras dynamiskt(vid körning).

Å andra sidan, när du gör:

Int ** board; board = new int *;

Int x = 10; //<--- Notice that it does not have to be `const` anymore! int **board; board = new int*[x];

kompilatorn vet inte hur mycket kortets minnesarray kommer att behöva, och därför gör den inte det fördelar i förväg Allt. Men när du kör ditt program kommer storleken på arrayen att bestämmas av värdet på variabeln x (vid körning), och motsvarande utrymme för board arrayen kommer att allokeras till det som kallas ett gäng- ett minnesområde där alla program som körs på din dator kan allokeras okänt på förhand(vid kompilering) summerar minne för personligt bruk.

Som ett resultat, för att faktiskt skapa en dynamisk 2D-array, måste du gå med den andra metoden:

Int ** board; board = new int *; // dynamisk array (storlek 10) av pekare till int för (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 }

Vi skapade precis en 2D-array på 10 gånger 10 kvadrat. För att gå igenom den och fylla den med faktiska värden, till exempel 1, kunde vi använda kapslade loopar:

För (int i = 0; i< 10; ++i) { // for each row for (int j = 0; j < 10; ++j) { // for each column board[i][j] = 1; } }

Det du beskriver för den andra metoden ger bara en 1D-array:

Int * board = ny int;

Detta allokerar bara en array med 10 element. Du kanske menade något sånt här:

Int ** board = ny int *; för (int i = 0; i< 4; i++) { board[i] = new int; }

I det här fallet allokerar vi 4 int * s och pekar sedan varje till en dynamiskt allokerad array på 10 int s.

Så nu jämför vi detta med int * board; ... Den största skillnaden är att när man använder en sådan array måste antalet "linjer" vara känt vid kompileringstillfället. Detta beror på att arrayer måste vara kompileringstidsfasta storlekar. Du kan också ha ett problem om du kanske vill returnera denna array från int * s eftersom arrayen kommer att förstöras i slutet av dess omfattning.

En metod som dynamiskt allokerar både rader och kolumner kräver mer sofistikerade åtgärder för att undvika minnesläckor. Du måste frigöra minne så här:

För (int i = 0; i< 4; i++) { delete board[i]; } delete board;

Jag skulle rekommendera att använda en standardbehållare istället. Du kan använda std :: array 4> eller kanske std :: vektor >, som du initierar med lämplig storlek.

I båda fallen kan din inre dimension ställas in dynamiskt (dvs. hämtad från en variabel), men skillnaden ligger i den yttre dimensionen.

Denna fråga motsvarar i princip följande:

Är int * x = ny int; är "bättre" än int x?

Svaret är "nej, om du inte behöver dynamiskt välja den här storleken på arrayen."

Den här koden fungerar bra med mycket få externa bibliotekskrav och visar grundläggande int ** arrayanvändning.

Detta svar visar att arrayen varjeär dynamiskt dimensionerad, och även hur man tilldelar en linjär array med dynamisk storlek till en grenarray med dynamisk storlek.

Detta program tar argument från STDIN i följande format:

2 2 3 1 5 4 5 1 2 8 9 3 0 1 1 3

Koden för programmet finns nedan...

#omfatta int main () (int ** array_of_arrays; int num_arrays, num_queries; num_arrays = num_queries = 0; std :: cin >> num_arrays >> num_queries; // std :: cout<< num_arrays << " " << num_queries; //Process the Arrays array_of_arrays = new int*; int size_current_array = 0; for (int i = 0; i < num_arrays; i++) { std::cin >> size_current_array; int * tmp_array = ny int; för (int j = 0; j< size_current_array; j++) { int tmp = 0; std::cin >> tmp; tmp_array [j] = tmp; ) array_of_arrays [i] = tmp_array; ) // Bearbeta frågorna int x, y; x = y = 0; för (int q = 0; q< num_queries; q++) { std::cin >> x >> y; // std :: cout<< "Current x & y: " << x << ", " << y << "\n"; std::cout << array_of_arrays[x][y] << "\n"; } return 0; }

Detta är en mycket enkel implementering av int main och beror bara på std :: cin och std :: cout. Barebones, men tillräckligt bra för att visa dig hur du arbetar med enkla flerdimensionella arrayer.

// deklaration av en tvådimensionell dynamisk array med 10 element:

float ** ptrarray = ny float *; // två rader i en array

för (int count = 0; count< 2; count++)

ptrarray = ny flyta; // och fem kolumner

// där ptrarray är en array av pekare till det allokerade minnesområdet för en array med reella tal av flyttyp

Först deklareras en andra ordningens pekare float ** ptrarray, vilket hänvisar till en array av float * pekare, där storleken på arrayen är två . Sedan, i for-loopen, deklarerades varje rad i arrayen linje 2 minne för fem element tilldelas. Resultatet är en tvådimensionell dynamisk array ptrarray.Låt oss överväga ett exempel på att frigöra minne som allokerats för en tvådimensionell dynamisk array.

// frigör minne tilldelat för en tvådimensionell dynamisk array:

för (int count = 0; count< 2; count++)

ta bort ptrarray;

// där 2 är antalet linjer i arrayen

#omfatta
#omfatta
#omfatta
void main ()
{

int * a; // pekare till array

system ("chcp 1251");

scanf ("% d", & n);

scanf ("% d", & m);

// Tilldela minne

a = (int *) malloc (n * m * storleken på (int));

// Inmatningsmatriselement

för (i = 0; i

för (j = 0; j

printf ("a [% d] [% d] =", i, j);

scanf ("% d", (a + i * m + j));

// Utdata av arrayelement

för (i = 0; i

för (j = 0; j

printf ("% 5d", * (a + i * m + j)); // 5 förtrogenhet för ett arrayelement

getchar (); getchar ();
}

Utförande resultat

Ange antal rader: 3

Ange antalet kolumner: 4

Det finns också ett annat sätt att dynamiskt allokera minne för en tvådimensionell array - med hjälp av en array av pekare. Detta kräver:
- allokera ett block av RAM för en grupp av pekare;
- allokera block av direktminne för endimensionella arrayer, vilka är rader av den erforderliga matrisen;
- skriv adresser till strängar i en array av pekare.

Malloc () funktion - returnerar en pekare till den första byten i ett minnesområde av storleksstorlek som tilldelades från en hög. Om det inte finns tillräckligt med minne i högen, returneras en nollpekare.

#omfatta
#omfatta
#omfatta
void main ()
{

int ** a; // pekare till pekare till sträng

system ("chcp 1251");

printf ("Ange antalet rader:");

scanf ("% d", & n);

printf ("Ange antalet kolumner:");

scanf ("% d", & m);

// Tilldela minne för pekare till strängar

a = (int **) malloc (n * storleken på (int *));

// Inmatningsmatriselement

för (i = 0; i

// Tilldela minne för att lagra strängar

a [i] = (int *) malloc (m * storlek på (int));

för (j = 0; j

printf ("a [% d] [% d] =", i, j);

scanf ("% d", & a [i] [j]);

// Utdata av arrayelement

för (i = 0; i

för (j = 0; j

printf ("% 5d", a [i] [j]); // 5 förtrogenhet för ett arrayelement

gratis (a [i]); // ledigt minne för strängen

getchar (); getchar ();
}

Resultatet av programkörningen liknar det tidigare fallet.

Fria arrayer kan allokeras med dynamisk minnesallokering för strängpekare. Fri kallas en tvådimensionell matris (matris), vars storlek kan vara olika. Fördelen med att använda en ledig array är att du inte behöver allokera mer datorminne för att rymma maximalt möjliga stränglängd. Faktum är att en fri array är en endimensionell array av pekare till endimensionella dataarrayer.

Pekare.

En pekare är en variabel vars värde är adressen där data finns. Adressen är numret på minnesplatsen där eller med vilken data finns.

Efter datatyp i SI är pekare indelade i:

En maskinskriven pekare är en pekare som innehåller adressen till data av en viss typ (system eller användare).

En otypad pekare är en pekare som innehåller en adress för data av odefinierad typ (bara en adress).

Pekardeklaration;

Pekarinstallation;

tillgång till värdet som finns vid pekaren. Deklarationen (beskrivningen) av indikatorn på SI-språket är som följer:

Skriv * namn [= värde];

Vid deklarering kan en CI-pekare initieras genom att ange motsvarande värde genom tilldelningstecknet. Detta värde måste vara en adress skriven i någon av följande former:

Nullvärde (NULL-identifierare);

En annan pekare;

Variabel adress (via operationen att ta adressen);

Ett uttryck som representerar pekarritmetik;

En adress som härrör från en heap-allokering.

#omfatta

int var; // vanlig heltalsvariabel

int * ptrVar; // heltalspekare (ptrVar måste vara av typen int, eftersom den kommer att referera till en variabel av typen int)

ptrVar = // tilldelade pekaren adressen till cellen i minnet där värdet på variabeln var ligger

scanf ("% d", & var); // i var-variabeln sätter du in värdet från tangentbordet

printf ("% d \ n", * ptrVar); // utvärde genom en pekare

Utföranderesultat: 6 6

Föreläsning nummer 3.

Funktioner.

En funktion är en syntaktiskt urskiljbar namngiven programenhet som utför en specifik åtgärd eller grupp av åtgärder. Varje funktion har sitt eget gränssnitt och implementering. Funktionsgränssnitt - funktionshuvudet, som innehåller namnet på funktionen, en lista över dess parametrar och typen av returvärde.

Beskrivningen av en funktion på C-språket utförs var som helst i programmet utanför beskrivningen av andra funktioner och består av tre element:

1. Prototyp av funktionen;

2. Funktionens titel;

3. kroppen av funktionen.

Funktionsprototyp är en valfri del av en funktionsbeskrivning avsedd att deklarera en funktion vars gränssnitt motsvarar en given prototyp. Prototypdeklarationen ser ut så här:

Typnamn (lista över typer av formella parametrar);

Funktionsparametrar är de värden som skickas till funktionen när den anropas.

Funktionshuvud - beskrivning av gränssnittsdelen av funktionen, som innehåller: typen av returvärde, namnet på funktionen och listan över formella parametrar för funktionen. Syntaxen för att deklarera ett funktionshuvud är:

Typnamn (lista över formella parametrar)

Exempel på funktionsrubriker:

Int func (int i, dubbel x, dubbel y)

Void func (int ind, char * string)

Dubbelfunktion (void)

En funktions body är en implementeringsdel som innehåller programkoden som exekveras när funktionen anropas. Funktionskroppen följer alltid omedelbart efter funktionsrubriken (de kan inte separeras) och är omsluten av hängslen.

Implementering av SI-funktionen för beräkning av fakulteten för ett tal.

Dubbelfaktorial (osignerad);

Dubbelfaktor (osignerat num)

Dubbla fakta = 1,0;

För (osignerad i = 1; i<=num;i++)

Fakta * = (dubbel) i;

Returnera fakta;

Strukturer.

En struktur är en komplex datatyp som är en uppsättning element av olika typer ordnade i minnet. Varje element i strukturen har sitt eget namn och kallas ett fält.

Deklarationen i strukturens SI ser ut så här:

Struktur [typnamn]

Fält_1;

Fält_2;

Fält_N;

) [lista över variabler];

Att deklarera strukturfält är endast möjligt utan initiering. Om flera fält som följer efter varandra i beskrivningen av strukturen har samma typ, kan du för att beskriva dem använda syntaxen för att deklarera flera variabler av samma typ.

Filer.

En fil är ett namngivet dataområde på något lagringsmedium. Filtyper (i förhållande till "SI"-språket):
text;
binär.
Grundläggande åtgärder som utförs på filer:
1.Öppning av filer.
2. Läsa och skriva data.
3. Stänga filer.

Ytterligare operationer:
1.Filnavigering.
2. Hantering av fel vid arbete med filer.
3. Ta bort och byta namn på filer.
4 variabel beskrivning

Öppningslägen filer med SI

Strömomdirigering
FIL * freopen (const char * filnamn, const char * läge, FILE * stream);

Funktionen returnerar:
Pekare till fil - allt är bra,
NULL är ett åsidosättande fel.

Stänger filen
int fclose (FILE * stream);

Funktionen returnerar:
0 - filen stängdes framgångsrikt.
1 - ett fel inträffade när filen stängdes.

Slut på filkontroll
int feof (FILE * stream);
stream är en pekare till en öppen fil.

Funktionen returnerar:
0 - om slutet av filen ännu inte har nåtts.
!0 - slutet av filen har nåtts.

Öppna textfiler
Den andra parametern anger dessutom tecknet t (valfritt):
rt, wt, at, rt +, wt +, at +

Läser från en textfil

Formaterad läsning
int fscanf (FILE * stream, const char * format, ...);

Funktionen returnerar:
> 0 - antalet variabler som lästs framgångsrikt,
0 - ingen av variablerna lästes framgångsrikt,
EOF - fel eller slutet på filen nått.
Läser en rad

Funktionen returnerar:
buffert - allt är bra,
Läser en rad
char * fgets (char * buffert, int maxlen, FILE * stream);

Funktionen returnerar:
buffert - allt är bra,
NULL - fel eller slutet på filen nått.
Att läsa en karaktär
int fgetc (FILE * stream);
Funktionen returnerar:
teckenkod - om allt är bra,
EOF - om fel eller slutet av filen har nåtts.
Sätta tillbaka en karaktär på streamen
int ungetc (int c, FIL * ström);
Funktionen returnerar:
teckenkod - om allt är lyckat,
EOF - Ett fel har inträffat.

Skriv till text fil i SI

Formaterad utdata
int fprintf (FILE * stream, const char * format, ...);
Funktionen returnerar:
antalet skrivna tecken - om allt är bra,
negativt värde om fel.
Att skriva ett snöre
int fputs (const char * string, FILE * stream);
Funktionen returnerar:
antalet skrivna tecken - allt är bra,
EOF - Ett fel har inträffat.
Att skriva en karaktär
int fputc (int c, FILE * stream);
Funktionen returnerar:
koden för det skrivna tecknet - allt är bra,
EOF - Ett fel har inträffat.
Öppna binära filer
Den andra parametern anger dessutom tecknet b (obligatoriskt): rb, wb, ab, rb +, wb +, ab +
Läser från binära filer
size_t fread (void * buffert, size_t size, size_t num, FILE * stream);
Funktionen returnerar antalet lästa block. Om det är mindre än num har ett fel inträffat eller har uppnåtts.
slutet av filen.

Skriver till en binär fil
size_t fwrite (const void * buffert, size_t size, size_t num, FILE * stream);
Funktionen returnerar antalet skrivna block. Om det är mindre än num har ett fel inträffat.

Filnavigering

Läser aktuell offset i filen:
long int ftell (FILE * stream);
Ändra aktuell offset i filen:
int fseek (FILE * stream, long int offset, int origin);

SEEK_SET (0) - från början av filen.
SEEK_CUR (1) - från den aktuella positionen.
SEEK_END (2) - från slutet av filen.
Funktionen returnerar:
0 - allt är bra,
!0 - ett fel uppstod.
Flytta till början av filen:
ogiltig spola tillbaka (FILE * stream);
Läser den aktuella positionen i filen:
int fgetpos (FILE * stream, fpos_t * pos);
Ställa in den aktuella positionen i filen:
int fsetpos (FILE * stream, const fpos_t * pos);
Funktioner returnerar:
0 - allt är framgångsrikt,
!0 - ett fel uppstod.
Fpos_t struktur:
typedef struct fpos_t (
långt ifrån;
mbstate_t wstate;
) fpos_t;

Får ett felsymptom:
int ferror (FILE * stream);
Funktionen returnerar från noll om ett fel uppstår.
Felåterställningsfunktion:
void clearerr (FILE * stream);
Utmatningsfunktion för felmeddelande:
void perror (const char * string);

Buffring

Buffertrensningsfunktion:
int fflush (FILE * stream);
Funktionen returnerar:
0 - allt är bra.
EOF - Ett fel har inträffat.
Bufferthanteringsfunktion:
void setbuf (FIL * ström, char * buffert);

Skapar en buffert av storleken BUFSIZ. Används före inmatning eller utmatning till en ström.

Tillfälliga filer

Tillfällig filskapande funktion:
FIL * tmpfile (void);
Skapar en temporär fil i wb+-läge. Efter att ha stängt filen raderas den senare automatiskt.
Tillfällig filnamnsgenereringsfunktion:
char * tmpnam (char * buffert);

Raderar och byter namn

Filraderingsfunktion:
int remove (const char * filnamn);
Byt namn på fil:
int byta namn (const char * fname, const char * nname);
Funktioner returnerar:
0 - om det lyckas,
!0 - annars.

Föreläsning nummer 4.

Stack.

En stack är som motsatsen till en kö, eftersom den fungerar enligt LIFO-principen sist in, först ut. För att visualisera en stapel, tänk på en stapel med tallrikar. Den första tallriken på bordet kommer att användas sist, och den sista tallriken som placeras ovanpå kommer att användas först. Stackar används ofta i systemprogramvara, inklusive kompilatorer och tolkar.

När du arbetar med stackar är operationerna för att infoga och hämta ett element de viktigaste. Dessa operationer kallas traditionellt push och pop. Därför, för att implementera stacken, måste du skriva två funktioner: push (), som "skjuter" värdet till stacken, och pop (), som "popper" värdet från stacken. Du måste också tilldela ett minnesområde som kommer att användas som en stack. För detta ändamål kan du allokera en array eller dynamiskt allokera ett minnesfragment med funktionerna i C-språket som tillhandahålls för dynamisk minnesallokering. Precis som med kön, hämtar hämtningsfunktionen ett objekt från listan och tar bort det om det inte finns lagrat någon annanstans. Följande är den allmänna formen för push () och pop () funktioner som fungerar på en heltalsmatris. Olika typer av datastackar kan organiseras genom att ändra den underliggande datatypen för arrayen.

int tos = 0; / * toppen av högen * /

/ * Skjut föremålet på högen. * /

void push (int i)

om (tos> = MAX) (

printf ("Stack full \ n");

/ * Få det översta föremålet i högen. * /

om (tos< 0) {

printf ("Stacken är tom \ n");

returstack;

Variabeln tos ("top of stack") innehåller indexet för toppen av stacken. När du implementerar dessa funktioner är det nödvändigt att ta hänsyn till de fall då stacken är full eller tom. I vårt fall är tecknet på en tom stack att tos är lika med noll, och tecknet på ett stackspill är en ökning av tos så att dess värde pekar någonstans utanför den sista cellen i arrayen.

Ett exempel på att arbeta med en stack.

Stacken kommer att ligga i en hög, inte i en array med fast storlek. Även om dynamisk minnesallokering inte krävs i ett så enkelt exempel, kommer vi att se hur man använder dynamiskt minne för att lagra stackdata.

/ * Enkel miniräknare med fyra steg. * /

#omfatta

#omfatta

int * p; / * pekare till ledigt minnesområde * /

int * tos; / * pekare till toppen av stapeln * /

int * bos; / * pekare till botten av stapeln * /

void push (int i);

p = (int *) malloc (MAX * storlek på (int)); / * få minne för stacken * /

printf ("Fel vid allokering av minne \ n");

bos = p + MAX-1;

printf ("Kalkylator med fyra åtgärder \ n");

printf ("Tryck" q "för att avsluta \ n");

printf ("% d \ n", a + b);

printf ("% d \ n", b-a);

printf ("% d \ n", b * a);

printf ("Division med 0. \ n");

printf ("% d \ n", b / a);

case ".": / * visa innehållet i toppen av stapeln * /

printf ("Aktuellt värde överst i stacken:% d \ n", a);

) while (* s! = "q");

/ * Att skjuta ett föremål på traven. * /

void push (int i)

om (p> bos) (

printf ("Stack full \ n");

/ * Ta bort det översta föremålet från stapeln. * /

om (s< tos) {

printf ("Stacken är tom \ n");

Kö.

En kö är en linjär lista med information som hanteras på först-in-först-ut-basis; denna princip (och kön som datastruktur) kallas ibland även FIFO. Detta innebär att det första objektet som skjuts in i kön kommer att hämtas först, det andra skjutsade objektet kommer att hämtas som andra, och så vidare. Detta är det enda sättet att arbeta med en kö; slumpmässig tillgång till enskilda element är inte tillåten.

För att visualisera hur en kö fungerar, låt oss introducera två funktioner: qstore () och qretrieve () (från "lagra" - "spara", "hämta" - "hämta"). Funktionen qstore () placerar ett objekt i slutet av kön, och funktionen qretrieve () tar bort objektet från framsidan av kön och returnerar dess värde. Tabellen visar verkan av sekvensen av sådana operationer.

Handling Köinnehåll
qstore (A) A
qstore (B) A B
qstore (C) A B C
qretrieve () returnerar A FÖRE KRISTUS
qstore (D) B C D
qretrieve () returnerar B CD
qretrieve () returnerar C D

Observera att en hämtningsoperation tar bort ett objekt från kön och förstör det om det inte lagras någon annanstans. Därför, efter att ha hämtat alla element, kommer kön att vara tom.

I programmering används köer för att lösa många problem. En av de mest populära typerna av sådana problem är simulering. Köer används också i operativsystemets uppgiftsschemaläggare och I/O-buffring.

/ * Schemaläggare för minievenemang * /

#omfatta

#omfatta

#omfatta

#omfatta

char * p, * qretrieve (void);

void enter (void), qstore (char * q), review (void), delete_ap (void);

för (t = 0; t< MAX; ++t) p[t] = NULL; /* иницилизировать массив

nollpekare * /

printf ("Enter (E), List (L), Delete (R), Exit (Q):");

* s = övre (* s);

/ * Infoga ett nytt möte i kön. * /

void enter (void)

printf ("Ange möte% d:", spos + 1);

om (* s == 0) bryta; / * inget skriv gjort * /

p = (char *) malloc (strlen (s) +1);

printf ("Inte tillräckligt med minne. \ n");

if (* s) qstore (p);

/ * Se innehållet i kön. * /

ogiltig recension (ogiltig)

för (t = rpos; t< spos; ++t)

printf ("% d.% s \ n", t + 1, p [t]);

/ * Ta bort ett möte från kön. * /

void delete_ap (void)

if ((p = qretrieve ()) == NULL) returnera;

printf ("% s \ n", p);

/ * Infoga möte. * /

void qstore (char * q)

printf ("Fullständig lista \ n");

/ * Hämta möte. * /

char * qretrieve (void)

if (rpos == spos) (

printf ("Det finns inget mer möte. \ n");

returnera p;

Lista.

En enkelt länkad cyklisk lista är en rekursiv deklaration av strukturer, eller snarare en pekare till den i själva typstrukturen:

int data; // datafält

s * nästa; // nästa element

) * första, * curr; // första och nuvarande element

Initiering:

first-> next = curr;

för att få det första elementet använd first-> data

för att lägga till ett nytt objekt: curr-> nästa = nytt s;

curr = curr-> nästa; // gå till den sista

och för att få till exempel 50 element genom en loop, iterera över listan:

curr = första; // gå till först

för (int i = 0; i<50;i++)

if (curr-> nästa! = NULL)

curr = curr-> nästa;


Liknande information.








2021 gtavrl.ru.