Романов Е

„Мога да си позволя кафе в леглото,

но първо трябва да станеш, да се облечеш и да сготвиш,

и след това се съблечете, легнете и пийнете"

Статично и динамично

Локалните променливи изискват отделно обсъждане. Въпреки че те вече са създадени в стека, когато програмата се изпълнява, техните размери обаче не могат да бъдат променяни (например размерите на локалните масиви също трябва да бъдат константи). Следователно това не променя нищо в общата теза за всички именувани променливи: техният брой, размери и общ обем вече са известни по време на превода и не могат да бъдат променени по време на изпълнение на програмата.

Но когато се пишат много програми, измерението на данните, които се обработват, не е известно предварително. При използване на обикновени променливи в такива случаи е възможен единственият изход - да се определи размерът "до максимума". В ситуация, в която се изисква да се обработват данни с още по-голямо измерение, е необходимо да се направят промени в текста на програмата и да се преведе отново. За такива цели се използва командата на препроцесора #define, за да не се променя стойността на една и съща константа на няколко места в програмата.

// Подреждане на модела в масив с ограничен размер

ако (sp==SZ) върне 0;

Динамични променливи

Целта и механизмът за внедряване на динамични променливи трябва да се разбират в светлината на току-що формулираното разочароващо заключение: програмата не може да надхвърли ограничението на паметта, планирано по време на превода. В противен случай ще е необходима допълнителна памет, разпределена по време на изпълнение на програмата.

  • динамичните променливи се създават и унищожават от работещата програма чрез изпълнение на специални изрази или извиквания на функции;
  • броят и размерите на динамичните променливи (масиви) могат да се променят в зависимост отхода на програмата. Това се определя от броя на извикванията на съответните функции по техните параметри;
  • динамичната променлива няма име, може да бъде достъпна само чрез указател;
  • функцията за унищожаване на динамична променлива получава указател към променливата, която се унищожава.

романов
Най-важното свойство на динамична променлива е нейната "анонимност" и достъпност на указателя. Между другото, това пасва добре на едно от имената на операцията * - dereference на указателя. По този начин динамична променлива не може да бъде достъпна „сама по себе си“, а само индиректно чрез други променливи или обикновени наименувани указатели. Една динамична променлива може от своя страна да съдържа един или повече указатели към други динамични променливи. В този случай получаваме динамични структури от данни, в които броят на променливите и връзките между тях могат да се променят по време на програмата (списъци, дървета);

Работата с динамични променливи и системата DRP има редица особености и трудности. Те се утежняват от факта, че в C, в съответствие с изискванията за ефективност на програмния код, библиотечните функции са минимално защитени от програмни грешки:

  • ако е създадена динамична променлива, но указателят към нея е "изгубен" от програмата, тогава такава променлива е "нещо само по себе си" - тя съществува, но не е достъпна за използване. Въпреки това, паметта, която заема, остава в програмата;
  • грешки в процеса на създаване, изтриване и работа с динамични променливи (многократен опит за унищожаване на динамична променлива, опит за унищожаване на променлива, която не е динамична и др.) водят до непредсказуеми последствия в програмата. Освен това програмата "пада" понякога не на мястото, къдетогрешни действия при последващи извиквания на функциите за работа с библиотеката.

pd = ново двойно; // Обикновена динамична променлива

vo> *malloc(int размер); // разпределете област на паметта с размер

vo> free(vo > // освобождаване на областта на паметта,

vo> *realloc(void *p, int size);

// разширяване на разпределената област на паметта

// пренаписване на старото съдържание на блока

# включва разпределение. h > // библиотека от функции за управление на паметта

двойно *pd; // Обикновена динамична променлива

pd = (double *)malloc(sizeof(double));

В заключение трябва да се каже, че динамичните променливи са „нещастни“: въпреки изключителната им важност при представянето на данни с променливи размери, те се поддържат синтактично от библиотеката, а не от транслатора, тоест като цяло „не са включени в езика“.

Динамични масиви

Аритметичният адрес на указателя и операторите за динамично разпределение на паметта ви позволяват да създавате масиви от динамични променливи или динамични масиви (DM), синтактично неразличими от обикновените:

тези действия се случват, докато програмата работи и следователно размерът на масива може да се променя от едно изпълнение на програмата в друго.

# включва разпределение. h > // Библиотека с функции за управление на паметта

двойно *pd1,*pd2; // Масиви от динамични променливи

pd 1= ново двойно [n 1]; // Разпределяне на памет за DM

if (pd1 ==NULL pd2==NULL) връщане;

for (i=0; i // Работа с DM

  • не прилагайте функции и операции за динамично управление на паметта към динамична променлива или динамичен масив едновременно;
  • когато работите с обекти, използвайте само операцииnew / delete ;
  • при освобождаване на памет отпод динамичен масив, „подсказвайте“ на преводача, че указателят препраща към масива, като напишете празни скоби пред указателя:delete [] pd 1.

Масивите, създадени в динамична памет, се наричат ​​динамични. Свойствата на указателя ви позволяват да третирате динамичните и редовните масиви по един и същи начин. В много езици от интерпретативен тип (например BASIC) подобен механизъм е скрит в самия преводач, така че масивите там „по своята същност“ могат да бъдат с променливо измерение.

Всъщност способността да се получи масив с дадено измерение не решава проблема. Програмата не винаги знае колко данни ще получи за обработка. Как е възможно да се стигне в такъв случай? Има няколко опции:

най-неефективният: за всеки тип данни резервирайте памет предварително "до максимума". По отношение на масив това означава, че ние предварително избираме измерение, което никога няма да бъде надвишено. Подобно "никога" обаче може да се случи рано или късно, така че процесът на попълване на масива е по-добре контролиран;

Приемлива опция може да бъде приложена, ако в даден момент от времето за изпълнение програмата „знае“ какво ще бъде измерението на данните, които се обработват този път. Тогава тя може да създаде динамичен масив от това измерение и да работи с него. За съжаление, такова "знание" не винаги е възможно;

идеалният вариант е да се създаде структура от данни, която автоматично увеличава размерите си, когато се попълни. За съжаление, в случай на масив, нито езикът, нито библиотеката ще помогнат тук - той може да бъде реализиран само програмно.

Както знаете, една функция не може да върне масив като резултат, но може да създаде динамичен масив по време на изпълнение ивърнете указател към него.

// Низ като динамичен масив - конкатенация на низове

char *twoToOne(char *p1, char *p2)

овъгляване*извън; intn1,n2;

за (n1=0; p1[n1]!='\0'; n1++); // Дължина на първия низ

за (n2=0; p2[n2]!='\0'; n2++); // Дължина на втория ред

if ((out = new char [n1+n2+1]) == NULL)

връща NULL; // Заделяне на памет за резултата

докато (*p2!=0) out[n1++] = *p2++; // Копиране на редове

В този пример размерът на изходната линия се определя елементарно като сбор от дължините на линиите. Понякога това изисква да направите едно и също нещо два пъти. Например, когато се връщат факторите на число за първи път, е необходимо разлагане, за да се определи броят на факторите и след разпределяне на паметта за втори път, за да се запълни.

//------ Динамичен масив от прости множители

// Размерността на масива се определя чрез двойно изчисление

int *mnog(longvv)

за (int i=2; vv%i!=0; i++); // Определяне на следващия множител

int *p=ново int[sz+1]; // Създаване на динамичен масив

за (int i=2; nn%i!=0; i++); // Определяне на следващия множител

p[k]=i; // Съхранявайте множителя в масив

p[k]=0; връщане p;> // Указател за връщане към DM

Но най-универсалният и често срещан случай е преразпределението на паметта при препълване на масив: създава се нов масив с по-големи размери, в който съдържанието на стария се презаписва. След това старият се унищожава и указателят към стария масив се задава на новия (новият се счита за стар). Всички тези действия могат да бъдат извършени от една единствена функция за прехвърляне на по-ниско ниво.

„Мога да си позволя кафе в леглото, но първо трябва да станете, да се облечете, да сготвите и след това да се съблечете, да легнете иизпийте“. М. Жванецки.

// Зареждане на динамичен масив от цели числа от файл

int *loadInt(char nm[],int &n)

int sz=10,*p =new int[sz]; n=0;

sz*=2; // двойно измерение

int *q=ново int[sz]; // Създай нов

for (int i=0;i // копиране на старо в ново

изтриване; // унищожи стар

p=q; // брои нови като стари

p = (int*) realloc(p,sizeof(int)*(i+1+N));

Въпроси без отговори

Определете смисловото значение на функцията, целта и метода за формиране на динамичен масив.