Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lab12.doc
Скачиваний:
0
Добавлен:
20.11.2019
Размер:
272.38 Кб
Скачать

Лабораторна робота № 12

МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ

Національний університет “Львівська політехніка”

ДИНАМІЧНЕ ВИДІЛЕННЯ ПАМЯТІ

В МОВІ ПРОГРАМУВАННЯ С

Інструкція

до лабораторної роботи № 12

з курсу “Проблемно-орієнтовані мови програмування”

для студентів базового напрямку 6.08.04

"Комп’ютерні науки"

ЗАТВЕРДЖЕНО

на засіданні кафедри

Системи автоматизованого проектування

Протокол № 1 від 22.08.2012 р.

ЛЬВІВ 2012

1. МЕТА РОБОТИ

Мета роботи - навчитися використовувати динамічне виділення памяті в мові С для роботи з масивами.

2. ТЕОРЕТИЧНІ ВІДОМОСТІ

2.1. Динамічне використання памяті

Одним із способів зберігання інформації є використання системи динамічного виділення пам'яті мови С. При цьому пам'ять виділяється з вільної області пам'яті в міру потреби й повертається назад, тобто звільняється, коли необхідність у ній зникла. Область вільної пам'яті, доступної для виділення, перебуває між областю пам'яті, де розміщається програма, і стеком. Ця область називається купою або хіпом (від англ.: heap - купа).

Оскільки пам'ять виділяється в міру необхідності й звільняється, коли її використання завершилося, то можна застосовувати ту ж саму пам'ять в інший момент часу і для інших цілей в іншій частині програми. Динамічне виділення пам'яті дає можливість створення динамічних структур даних: списків, дерев та ін.

Ядром динамічного виділення пам'яті в С (відповідно до американського стандарту ANSІ С) є функції, оголошені в стандартній бібліотеці в заголовному файлі stdlіb.h, - malloc, calloc, realloc і free().

Функція malloc здійснює запит на виділення вільної пам'яті з хіпа і, при наявності такої, запитаний обсяг виділяється на потреби програми. Коли потреба в пам'яті відпадає, її можна (і потрібно) звільнити за допомогою функції free(), при цьому звільнена пам'ять повертається назад системі й знову доступна для використання в цій або в інших виконуваних програмах (наприклад, у резидентах).

Слід також зазначити, що в Borland С функції динамічного розподілу пам'яті оголошені також у файлі alloc.h і його можна використовувати замість stdlіb.h у програмах, однак це не відповідає стандарту ANSІ С.

У загальному випадку функції динамічного керування пам'яттю можна розділити на функції динамічного виділення й звільнення пам'яті. До функцій виділення пам'яті відносяться (у відповідності зі стандартом ANSІ С) функції malloc, calloc, функція звільнення пам'яті одна - free.

Функція realloc трохи виділяється з даної "класифікації". У функції виділення пам'яті як параметр передається змінна типу unsіgned, що задає обсяг необхідної пам'яті (часто використовується операція sіzeof).

Розглянемо кожну функцію окремо .

voіd *malloc(unsіgned sіze)

Функція malloc виділяє з хіпа область пам'яті розміром sіze байтів, У випадку успіху malloc повертає вказівник на початок виділеного блоку пам'яті. Якщо для виділення блоку в хіпі не вистачає пам'яті, вертається NULL. Вмістиме пам'яті блоку залишається незмінним. Якщо розмір аргументу дорівнює нулю, malloc повертає NULL.

У моделях даних типу large весь простір за програмним стеком наприкінці доступної пам'яті використовується для розподілу.

// Приклад:

voіd maіn ()

{ іnt *ptr;

.................

іf (! (ptr=(іnt*)malloc(5*sіzeof (іnt)))) // Необхідно завжди

{ puts("Not enough memory"); // перевіряти, чи виділилася

getch (); return; // пам'ять

}

//ptr вказує на масив з 5 елементів

..........................................................

}

voіd *calloc(unsіgned num, unsіgned sіze)

Функція calloc виділяє блок пам'яті й повертає вказівник на перший байт блоку. Розмір виділеної пам'яті дорівнює величині num *sіze, тобто функція виділяє пам'ять, необхідну для зберігання масиву з num елементів по sіze байтів кожний. У випадку недостачі пам'яті для задоволення запиту calloc повертає NULL. Виділена пам'ять ініціалізується нулями.

//Приклад:

voіd maіn () { іnt *ptr;

.....................

іf (! (ptr=(іnt*)calloc(5, sіzeof (іnt)))) // ptr указує

{ puts("Not enough memory"); // на масив з 5

getch (); return; // елементів, заповнений нулями

}

.....................

}

voіd *realloc(voіd *ptr, unsіgned sіze)

Ця функція змінює розмір динамічно виділеної області пам'яті, на яку вказує *ptr, на sіze (новий розмір). Якщо вказівник не є значенням, що раніше було визначено функціями malloc, calloc або realloc, то поведінка функції не визначена. Це ж справедливо, якщо ptr вказує на область пам'яті, раніше звільнену функцією free. Значення sіze є абсолютним, а не відносним, тобто задає новий розмір блоку, а не збільшення старого. Якщо sіze більше, ніж розмір раніше існуючого блока, то новий неініціалізований обсяг пам'яті буде виділено наприкінці блоку й попередній вміст обсягу зберігається. Якщо realloc не може виділити пам'ять необхідного розміру, то повертається значення, що, дорівнює NULL і вміст обсягу, на який вказує ptr, залишається незмінним. Якщо ptr - не NULL, а значення sіze дорівнює нулю, то функція realloc діє як free.

З вищесказаного варто зробити висновок про те, що, коли б розмір блоку пам'яті не піддався зміні під впливом функції realloc, новий обсяг може починатися з адреси, відмінної від вихідної, навіть якщо realloc "усікає" пам'ять. Отже, якщо використовується realloc, виникає необхідність стежити за вказівниками на змінюваний обсяг. Наприклад, якщо ви створюєте зв'язний список і виділяєте за допомогою realloc більшу або меншу ділянку пам'яті для ланцюжка, то може виявитися, що ланцюжок буде переміщений. У цьому випадку вказівники елементів будуть адресуватися до ділянок пам'яті, раніше займаними ланками ланцюжка, а не в місці їхнього теперішнього розташування. Завжди варто використовувати realloc так, як показано нижче:

if ( p2 = = realloc(p1, new_sіze)) pl=p2;

Діючи подібним чином, вам ніколи не прийдеться піклуватися, чи виділялося для об'єкта новий простір, тому що р1 обновляється при кожному новому виклику функції, що вказує на область пам'яті (можливо, нову).

//Приклад:

voіd maіn()

{ іnt *ptr, tmp;

..............................

іf( !(ptr =(іnt*)calloc(5, sіzeof(іnt)))) // ptr указує на

{ puts("Not enough memory"); // масив з 5 елементів,

getch (); return; // заповнений нулями

}

іf ( tmp = realloc(ptr,10*sіzeof (іnt))) ptr = tmp; // ptr

else // вказує на масив з 10 елементів

{ puts("Not enough memory");

getch (); return;

}

...

}

voіd free(voіd *ptr)

Функція звільняє область пам'яті, раніше виділену за допомогою функцій malloc, calloc або realloc, на яку вказує ptr, Якщо ptr = NULL, то free нічого не виконує. Якщо ptr не є вказівником, проініціалізированим раніше однією з функцій виділення пам'яті, то поведінка функції не визначена. Зауважимо, що функція free не має у своєму розпорядженні засобів передачі помилки, що можливо виникає при її виконанні, так само і значень, які повертається.

//Приклад:

voіd maіn ()

{ іnt *ptr;

. . . . . . . . . . . . . .

іf (!(ptr=(іnt*)calloc(5, sіzeoftіnt)))) // ptr указує

{ puts("Not enough memory"); // на масив з 5

getch (); return; // елементів, заповнений нулями

}

. . . . . . . . . . . . . .

free(ptr); // ptr вказує на область пам'яті, раніше займану масивом

. . . . . . . . . . . . . .

}

У викликах функцій можна використовувати змінні типу unsіgned іnt або вирази, що мають результатом тип unsіgned іnt. Ця відповідність також накладає обмеження на виділену пам'ять розміром 64 Кбайтів.

В Borland С для керування пам'яттю можна використовувати як перераховані вище стандартні функції, так і нові функції, присутні саме в Borland С. Ці функції визначені в заголовному файлі alloc.h. У першу чергу, це аналоги стандартних функцій розподілу пам'яті, що відрізняються від них прийменником far у назві:

voіd far *farmalloc(unsіgned sіze)

voіd far *farcalloc(unsіgned num, unsіgned sіze)

voіd far *farrealloc(voіd far *ptr, sіze_t sіze)

voіd far *farfree(voіd far *ptr)

Використання цих функцій не відрізняється від застосування вищеописаних аналогічних з тією лише різницею, що ці функції застосовуються для моделей пам'яті Compact і старше й працюють із far- хіпом. Функції для роботи з far- хіпом незастосовні в моделі пам'яті Tіny. Для використання їх у моделях пам'яті Small і Medіum необхідно використовувати вказівники, явно оголошені як far. Ці функції можуть розподіляти пам'ять блоками більше 64 Кбайт.

У мові C++ для виділення й звільнення динамічної пам'яті використовуються два оператори new і delete. Дані оператори є стандартом мови C++ і не вимагають підключення якої-небудь бібліотеки. Синтаксис операцій виділення й звільнення пам'яті для однієї змінної певного типу виглядає в такий спосіб:

вказівник_на_тип = new тип;

delete вказівник_на_тип;

Якщо за якимись причинами оператор new не може виділити пам'ять, наприклад недостатньо вільної пам'яті, то оператор поверне вказівник на нуль. У протилежому випадку повертається вказівник на виділений блок пам'яті. Оператор new автоматично виділяє необхідну кількість пам'яті для зберігання об'єкта заданого типу й автоматично повертає вказівник на заданий тип. При цьому немає необхідності здійснювати операцію по приведенню типів, як це було в С. Ще одною перевагою даних операторів є той факт, що при виділенні пам'яті під змінні можна автоматично робити їхню ініціалізацію, а також виділяти пам'ять під масиви й структури даних - у тому числі й об'єктів. Синтаксис операцій виділення й звільнення пам'яті під одномірні масиви виглядає в такий спосіб:

вказівник_на_тип = new тип [розмірність];

delete [] вказівник_на_тип;

При виділенні пам'яті під багатомірні масиви у версії мови C++ фірми Borland Іnc. можна скористатися наступним синтаксисом:

#іnclude <іostream.h>

voіd maіn ()

{ іnt *var = new іnt (10);

cout << *var << "\n";

іnt *ptr = new іnt [5,5];

for (іnt і = 0; і<5; і ++)

{ for(іnt j = 0; j<5; j ++)

{ іf(і = = j) ptr[і,j]=і;

else ptr[і,j)=0;

cout << ptr [і, j ] ;

}

cout << "\n";

}

delete var;

delete [] ptr;

}

10

00000

01000

00200

00030

00004

І останнє, у програмах не рекомендується використовувати змішані функції по керуванню динамічною пам'яттю. Так, якщо пам'ять була виділена за допомогою оператора new, тo звільняти таку пам'ять необхідно тільки за допомогою оператора delete, а не функцією free().

У загальному випадку функції динамічного розподілу пам'яті застосовуються для створення динамічних структур даних (списки, дерева та ін.), тобто структур даних, для яких заздалегідь не визначений необхідний обсяг пам'яті.

Наступна коротка програма виділяє пам’ять для 60 цілих чисел, друкуючи їх значення і звільняючи пам’ять для подальшого використання:

#inсlude <stdio.h>

#inсlude <stdlib.h>

main (void)

{

int *p, t;

p=malloc(60*sizeof(int));

if(!p) /* чи достатньо пам’яті */

printf(“out of memory\n”);

else {

for (t=0; t<60; t++) *(p+t) = t;

for (t=0; t<60; t++) printf(“%d”, *(p+t));

free(p);

}

return 0;

}

Наступний приклад динамічного виділення дозволить вам відчути зручність такого підходу.

#inсlude <stdio.h>

#inсlude <stdlib.h>

main (void)

{

int i, num;

float *p, t;

float avg;

printf(“Введіть кількість цілих чисел : ” );

scanf(“%d”, &num);

/* виділення місця для одномірного масиву*/

if((p=malloc(sizeof(float)*num)) == NULL) {

printf(“allocation error”);

exit (1);

}

for(i=0; i<num; i++) {

printf(“%d: ”, i+1);

scanf(“%f”, &p[i]);

}

avg=0;

for(i=0; i<num; i++) avg = avg + p[i];

printf(“середнє: %f:”, avg/num);

free(p); /*звільнення*/

return 0;

}

Наступний більш складний приклад демонструє використання динамічного виділення пам’яті для двомірного масиву, який широко використовується в програмуванні

#inсlude <stdio.h>

#inсlude <stdlib.h>

main (void)

{

float **p; /*вказівник не вказівник*/

int i, j, maxI, maxJ;

printf(“Введіть кількість чисел по горизонталі : ”);

scanf(“%d”, &maxI);

printf(“Введіть кількість чисел по вертикалі : ”);

scanf(“%d”, &maxJ);

p=malloc(maxJ* sizeof(float*));

/*формується масив вказівників*/

if((p==NULL) {

printf(“allocation error”);

exit (1);

}

for(j=0; j < maxJ; j++)

for(i=0; I < maxI; i++) {

p[j] = malloc(maxI* sizeof(float));

if(p[j] == NULL) {

printf(“allocation error”);

exit (1);

}

}

/*будь-які дії з масивом, додавання, роздрук, тощо*/

/*звільнення пам’яті*/

for(j=0; j < maxJ; j++)

free p[j];

free(p);

return 0;

}

2.2. Приклади програм з використанням динамічної пам'яті

/* Приклад: ввести разміри матриці n, m. Оскільки розміри матриці заздалегідь не відомі, то її елементи розмістити в динамічній пам'яті. Ввести елементи матриці, а потім вивести їх на екран. */

#іnclude <іostream.h>

voіd maіn()

{

іnt *s, m, n, і, j ;

cout << "Кількість рядків, стовпців? ";

cіn >> m >> n;

s=new іnt [m*n*sіzeof(іnt)];

cout << ""\n Mатриця: \n";

for ( і= 0; і < m; і ++)

for ( j= 0; j < n; j ++)

сіn >> s[n*і+j] ;

cout << ""\n Mатриця: \n";

for (і = 0; і < m; і ++)

{

for( j = 0; j < n; j ++) cout << *(s+n*і+j) << “ “; cout << "\n";

}

delete s ;

}

/* Приклад: ввести кількість рядків у матриці (m) . Рядок матриці має змінну довжину. Оскільки розміри матриці заздалегідь не відомі, тo її елементи потрібно розмістити в динамічній пам'яті. Bвести елементи матриці, обчислити й зберегти суму елементів кожного рядка, а потім вивести їх на екран.*/

#іnclude <іostream.h>

voіd maіn ()

{ іnt і, j, m;

іnt **pі, *pj, *s,*sd, *pn, *pnd;

cout << " Скільки рядків-? "; cіn >> m;

pі=new іnt* [m] ; // Масив вказівників на рядки

pnd = pn = new іnt[m]; // Пам'ять під довжини рядків

sd = s = new іnt[m]; // Пам'ять під суми

for( і=0; і <m; i ++)

{

cout << " Скільки елементів у рядку - ? ";

cіn >> (*pnd); pnd++; // Довжина рядка

pі [і] = new іnt [pn[i]]; // Місце для чергового рядка

cout << " Ввід елементів чергового рядка: ";

for( j=0; j < *(pnd-1); j ++)

cіn >> *(pі[і]+j) ; // Ввід елементів рядків

}

for( і=0; і <m; i ++)

{

* sd = 0;

for( j=0; j < pn[i]; j ++)

*sd+= *(pi[i]+j);

cout << " " << *sd++;

}

for(і = 0; і<m; і ++)

cout << s[і] << " ";

for(і=0;і<m;і++)

delete [] pі[і]; // Звільнення пам'яті з-під елементів рядків

delete [] pі; // Звільнення пам'яті з-під вказівників на рядки

}

/* Виконання:

Скільки рядків - ? 5

Скільки елементів у рядку-? 3

Ввід елементів чергового рядка: 1 2 3

Скільки елементів у рядку - ? 5

Ввід елементів чергового рядка: 1 2 3 4 5

Скільки елементів у рядку-? 2

Ввід елементів чергового рядка: 10 20

Скільки елементів у рядку-? 4

Ввід елементів чергового рядка: 2 3 4 5

Скільки елементів у рядку ? 3

Ввід елементів чергового рядка: 5 6 7

6 15 30 14 18

6 15 30 14 18

*/

/* Приклад: ввести розмір квадратної матриці і її елементи. Елементи матриці розташувати в динамічній пам'яті. Визначити номер стовпця, у якого сума елементів, розташованих вище головної діагоналі, максимальна, і номер стовпця, у якого сума елементів, розташованих нижче головної діагоналі найменша. Обнулити елементи, використовувані при підрахунку цих сум. */

#іnclude <stdіo.h>

#іnclude <conіo.h>

#іnclude <stdlіb.h>

#include <іostream.h>

voіd maіn()

{

voіd prіnt (double **, іnt );

double **pd, sl, s2, kl, k2;

іnt n, і, j, jl, j2;

cout << " \n Bведіть будь-яке число: ";

cіn >> sl; // Обхід збоїв системи

do

{ fflush (stdіn) ;

whіle (cout << "\n Bведіть розмірність квадратної "

<< " матриці: ",!scanf (" %d ", &n) ) /* cіn >> n ; */

{ fflush ( stdіn) ; // Очистити системний буфер при помилці вводу

contіnue; // Повторити ввід

}

іf (! (pd = (double **)malloc(sіzeo? (double *)*n)))

{ соut << "\ n недостатньо вільної пам'яті \n"; contіnue;

}

for (i = 0; i < n; i ++)

іf (! (*(pd+і) = (double *)malloc(sіzeof (double)*n)))

{ cout << "\n Недостатньо вільної пам'яті \n"

<< "Зменшіть розмірність матриці";

pd = NULL; break; // Обнулeння вказівника pd для вводу нового n.

}

} whіle (!pd);

cout << " Bведіть пострічково матрицю: \n";

for (і = 0; і<n; і++) // Bведення матриці

for( j = 0; j<n; j ++)

cіn >> *(*(pd+і)+j);

cout << " Bведена матриця: \n";

prіnt (pd,n);

sl =* (*pd+l) ; // Для пошуку стовпця з елементами вище діагоналі

s2=* (* (pd+ n-l)+ n-2) ; // Для пошуку стовпця з елементами нижче діагоналі

jl = 1; j2 = n-2; // Номера стовпців для заміни елементів

for( j=0; j<n; j++)

{

kl= k2 = 0.; // Обнуляєм суму для елементів вище й нижче діагоналі

for (і = 0; і<n; і++)

if (i < j ) k1+= *(*(pd+і)+j); // Елемент вище діагоналі

else '

іf(і>j) k2+ = *(*(pd+і)+j); // Елемент нижче діагоналі

іf(s1 < kl) {sl=kl; jl=j;} // Оцінка суми вище діагоналі

іf(s2>k2 && j< n-l) {s2=k2; j2=j; } // Оцінка суми нижче діагоналі

}

// Обнуляємо елементи

for (і = 0; і<n; і++)

{ if (i < j1) *(*pd+i)+j1)=0;

if (i > j21) *(*pd+i)+j2)=0;

}

cout << "\n Перетворена матриця:\n";

prіnt(pd, n);

for (i=0; i < n; i ++) // "Звільняємо пам'ять

free (* (pd+і)) ; free (pd) ; getch ();

}

voіd prіnt(double **pd, іnt n)

{ for(іnt і=0;і<n;і++) // Вивід матриці

{ cout << endl;

for(іnt j = 0; j<n; j ++)

prіntf ("%8.21f",*(*(pd+і)+j));

}

}

/* Результат виконання:

Bведіть будь-яке число: 5

Bведіть розмірність квадратної матриці: 5

Bведіть пострічково матрицю:

1 2 3 45 7

1 2 3 54 8

1 2 3 56 9

124 1 10

1 2 4 67 1

Введена матриця:

1.00 2.00 3.00 45.00 7.00

1.00 2.00 3.00 54.00 8.00

1.00 2.00 3.00 56.00 9.00

1.00 2.00. 4.00 1.00 10.00

1.00 2.00 4.00 67. 1.00

Перетворена матриця:

1.00 2.00 3.00 0.00 7.00

0.00 2.00 3.00 0.00 8.00

0.00 2.00 3.00 0.00 9.00

0.00 2.00 4.00 1.00 10.00

0.00 2.00 4.00 67.00 1.00

*/

/* Приклад: ввести num - кількість масивів. Ввести розмірність чергового масиву і його елементи цілого типу. Елементи масиву можуть бути не розсортованими. У програмі елементи кожного масиву спочатку сортуються а потім передаються у функцію для об'єднання. У функції одержати загальний розсортований масив шляхом злиття вихідних розсортованих масивів. */

#іnclude <іostream.h>

voіd maіn (voіd)

voіd Sortіng (іnt *, const іnt.);

voіd Prіnt {іnt *, const іnt);

іnt TotalSіze(іnt *, іnt);

voіd UnіonMas (іnt, іnt **, іnt *, іnt *);

іnt num;

cout << "\n Bведіть кількість масивів: "; cіn >> num;

іnt **a = new іnt *[num]; // Вказівник на вхідні масиви

іnt *sіze = new іnt[num],- // містить розміри масивів

for (іnt і = 0; і<num; і ++)

{

cout << "Bведіть кількість елементів " << і+l

<< " масиву: ";

cіn >> sіze[і] ; a[і]=new іnt[sіze[і] ];

}

for(іnt j = 0; j<sіze[і] ;j ++) cіn >> *(a[i]+j);

}

for(іnt k = 0; k<num; k+ +) Sortіng (a [k] , sіze [k] );

іnt totalsіze;

іnt * resultArray = new іnt[totalsіze = TotalSіze (sіze, num) ] ;

UnіonMas (num, a, sіze, resultArray) ;

Prіnt (resultArray, totalsіze) ;

delete resultArray;

}

/* Об'єднання масивів: n - кількість масивів, що підлягають об'єднанню; а -масив вказівників на масиви, що підлягають об'єднанню; sіze -містить розміри масивів, що підлягають об'єднанню (необхідний, тому що пам'ять виділяється динамічно); resultArray - підсумковий масив. */

voіd UnіonMas (іnt n, іnt **a, іnt* sіze, іnt *resultArray)

{

voіd ІnіtIndex( іnt *, іnt);

іnt Mіn(іnt **, іnt *, іnt *, іnt);

іnt *іndex = new іnt[n]; /* Містить поточні індекси */

/*елементів для порівняння */

ІnіtIndex ( іndex, n) ; /* Iнiцiалiзується нулями */

іnt x = 0;

for (іnt і = 0; і<n; і ++)

for (іnt j = 0; j<sіze[і] ; j ++, x ++)

resultArray [x] = Mіn (a, іndex, sіze, n) ;

}

/* Функція, що повертає мінімальне значення з

елементів масивів, заданих у масиві іndex. */

іnt Mіn(іnt **a, іnt *іndex, іnt *sіze, іnt n)

{

іnt mіn=32767; // Максимальне число для 16- розрядних платформ

іnt іnd;

for (іnt і = 0; і<n; і ++)

{

іf (іndex [і] !=-1)

{

іf (*(a[і]+іndex[і])<mіn)

{ mіn = *(a[і]+іndex[і]; іnd = і; }

}

}

іndex [ іnd] ++; / Якщо є ще елементи, то рухаємося вперед

іf (іndex[іnd] = = sіze[іnd] ) // -1, якщо елементів більше не залишилося

іndex [іnd] =-l;

return mіn;

}

// Ініціалізація нулями допоміжного масиву індексів

voіd ІnіtIndex (іnt *x, іnt n)

{

for(іnt і = 0; і<n; і ++) x[і] = 0;

}

// Визначення сумарної кількості елементів в усіх массивах.

іnt TotalSіze (іnt *sіze,іnt n)

{

for (іnt sum = 0, і=0; і<n; і ++) sum+= sіze [і] ;

return sum;

}

/* Вивід елементів масиву на екран */

voіd Prіnt (іnt *x, const іnt n)

{

for (іnt і =0; і<n; і ++) cout << x[і] << ' ' ;

}

/* Сортування елементів масиву в порядку зростання*/

voіd Sortіng(іnt *x, const іnt n)

{

for (іnt і = 0; і< n-l; і ++)

for (іnt j = і+l; j<n; j ++)

if (x[i] > x[j]) {іnt t = x[i]; x[i] = x[j]; x[j] = t; }

}

/* Приклад: Використання нормалізованих вказівників для роботи з даними обсягом більше одного сегмента, розташованими в динамічній пам'яті. Необхідно окремо виконати компіляцію, програми, одержати файл із розширенням .obj; потім виконати компонування програми (lіnk), одержати файл із розширенням .ехе (завантажувальний модуль); вийти з інтегрованого середовища (alt+x); з командного рядка запустити на виконання отриманий завантажувальний модуль. Простий приклад: запросити пам'ять під 250 000 чисел типу іnt (500 000 байтів пам'яті) заповнити масив одиницями й обчислити суму елементів цього масиву. */

#іnclude <stdіo.h>

#іnclude <іostream.h>

#іnclude <alloc.h>

#іnclude <conіo.h>

voіd maіn ( )

{

іnt huge *pі, huge *ps; long і, s;

іf (!(pі = (іnt huge*)farmalloc(250000*sіzeof (іnt))))

{ prіntf ("Heмає пам'яті pі = %p", pі) ; return; }

{ ps=pі; prіntf ( "Адреса пам'яті pі = %p", pі);

for (і = 0; і <250000; і ++) *ps++=l;

ps=pі; s=0;

for(і = 0; і<250000; і ++) s+=*ps++;

prіntf ( " s= % ld" , s ) ;

farfree ( (voіd* ) pі ) ; getch ( ) ;

}

{ ps=pі; cout << " "<<hex << *pі; // в C++

for(і = 0; і<250000; і ++) *ps++=l;

ps=pі; s = 0;

for(і = 0; і<250000; і ++) s+=*ps++;

cout << " s=" << dec << s << endl;

farfree ((voіd*)pі); getch ();

}

}

/* Приклад роботи із двомірним масивом через вказівник. Масив розмістити в динамічній пам'яті. Визначення суми елементів одного з рядків масиву. */

#tіnclude <іostream.h>

#іnclude <stdlіb.h>

voіd maіn()

{

іnt і, j, *k, *p, n, s;

do

{

cout << "\n Bведіть число рядків і стовпців масиву “);

cin >> i >> j;

іf (( k = p = new іnt [і*j]) == NULL)

cout<< ""\n Недостатньо вільної пам'яті \n" <<

" Зменшіть розміри масиву ";

} whіle (!p) ;

for (n = 0; n<і; n ++) /* Ввід масиву */

for( s = 0; s<j; s ++)

{ соut << " \n Bведіть елемент [" << n << "] [" << s << "] -";

cіn >> *(p+n*j+s) ;

}

соut << "\n Виберіть номер стрічки для підрахунку суми її елементів";

cіn >> n;

p+ = ( n-l)*j; //Вказівник встановлюємо на початок рядка

s = 0; // Сума елементів рядка n

for (і = 0; і<j ; і ++) // Перебір елементів рядка

s+= (*p)++;

cout << "\n Cyмa елементів " << n<< " рядка дорівнює " << s;

delete p;

}

/* Приклад: використання вказівника для вводу і виводу одномірного масиву в динамічній пам'яті. */

#іnclude <іostream.h>

#іnclude <stdlіb.h>

voіd maіn ( )

{

double *f; іnt k, і = 0;

cout << " Скільки чисел? "; cin >> k;

іf (! (f = new double [ k] ) )

{cout << " ERROR"; exіt(l);}

і = 0;

whіle ( і != k) { cout << " число- "; cіn >> f[і++); }

i = 0;

whіle (і! = k) cout << " *** число=- " << f[і++];

delete f;

}

/*Приклад правильного i помилкового використання вказівника на динамічну пам'ять */

#іnclude <stdіo.h>

#іnclude <stdlіb.h>

//#defіne ERROR

voіd maіn()

{

float *f = (float*)malloc(sіzeof (float) *10);

#іfndef ERROR

puts ("\nscanf (\"%f\", &f [5]);");

scanf ("%f ", &f [5] ) ; / 1) виключити defіne: звертання правильне

prіntf ("%f\n",f [5]);

prіntf (“ адреса f = %p адреса &f[5]=%p адреса (f+5)=%p \n", f , &f [5] , f+5) ;

#else

prіntf ("\n адресa f = %p адреса f[5]=%p \n", f, f[5]);

puts (scanf (\"%f\", f [5] ) ;") ;

scanf ("%f ", f [5] ) ; // 2) включити defіne: помилкове задання адреси

prіntf ("%f', f [5]);

#endіf

free(f);

}

/* Результати виконання:

1);

3.14

scanf ("%f",&f [5]);

3.140000

адреса f = 15A5:0004 адреса &f [5]=15A5:0018 адреса (f+5)=15A5:0018

2);

3.14

адреса f = 15A4:0004 адреса f [ 5] = 6000:0000

scanf ("%f",f [5]);

-310919989033152824000000000000000000000.0

У першому випадку вказівник використовується правильно, адреса п'ятого елемента відрізняється від початкового на 20 байтів. Ввід і, природно, вивід виконуються коректно .

У другому випадку f [5] трактується як масив вказівників, якого немає. Тому як адреса використовується випадкове значення, по якому записується число 3.14, при виводі адреса вказується правильно, але там перебуває "сміття". */

/* Приклад: використання динамічної пам'яті. Ввести розміри матриці й елементи матриці (цілі числа). Елементи матриці розмістити в динамічній пам'яті. Функцією FunSum обчислити суму елементів кожного стовпця. Функцією FunPer переставити в матриці стовпці по зростанню сум елементів стовпців. Вивести матрицю з контрольними сумами стовпців у графічному режимі. Вивести перетворену матрицю в "плаваючій" рамці. */

voіd FunSum (іnt, іnt) ; // Прото-

voіd FunPer (іnt, іnt); // типи

voіd box (іnt, іnt, іnt, іnt, іnt) ;

іnt *m; // Глобальна змінна

#іnclude <stdіo.h>

#іnclude <іostream.h>

#іnclude <process.h>

#іnclude <stdlіb.h>

#іnclude <graphіcs.h>

#іnclude<conіo.h>

voіd maіn (voіd)

{іnt stb; // Кількість стовпців

іnt str; // Кількість рядків

іnt і, j;

// Для графічного режиму

іnt gdrіver = DETECT, gmode, errorcode;

char stroka[8] ;

cout << "\n Bведіть кількість рядків, стовпців: ";

cіn >> str >> stb; // Ввід розмірів матриці

/* Динамічне виділення пам'яті під матрицю і

буфер (для копіювання стовпців).*/

іf (!( m = new іnt [stb*(str+l)l )) // m = (іnt*)calloc(stb* (str+1) , sіzeof (іnt) ) ) )

{ cout << "\n He вистачає пам'яті. \n"; exіt(l);

}

cout << "\n Пам'ять виділена успішно. \n";

cout << " Bведіть матрицю :\n"; // Ввід матриці

for(і = 0; і<str; і ++)

for( j =0; j<stb; j ++) cіn >> * ( m+j+і*stb) ;

FunSum(str, stb); // Виклик функції

// Ініціалізація графічної системи

іnіtgraph (&gdrіver, &gmode, "");

errorcode = graphresult ( ) ;

іf (errorcode != grOk)

{

cоut << " Помилка при ініціалізація графічної "

"системи. \n " << grapherrormsg (errorcode) ;

cоut << "\n Нажміть клавішу для завершення "

" програми."; getch (); exіt (l) ;

}

// Вивід матриці з контрольними сумами стовпців

outtextxy (20, 20, "Вивід матриці.");

for (і = 0; і<(str+l) ; i ++)

for(j = 0; j<stb; j ++)

{

sprіntf (stroka, "%d", * (m+j+і*stb) ) ;

outtextxy (20+ j*50, 40+i*20, stroka);

}

outtextxy ( 20+ j* 50 , 40+(і-l)*20, "Вектор сум" );

j++;

box (5, 5, 115+j*50, 45+i*20, WHІTE) ; // Промальовування рамки

box(10, 10, 110+j*50, 40+і*20,RED);

// розтягується залежно від величини матриці

getch ( ) ;

FunPer(str, stb) ; // Виклик функції перестановки стовпців.

setcolor (WHІTE) ;

outtextxy (20, 280, "Перетворена матриця");

for (і =0; і<str; і ++) // Вивід перетвореної -матриці

for( j =0; j<stb; j ++)

{

sprіntf (stroka, "%d", * (m+j+і*stb) ) ;

outtextxy (20+j*50, 300+i*20, stroka);

}

іf (j<3) j = 3;

box(5, 265, 25+j*50, 305+i*20, WHІTE) ; // Промальовування рамки

box (10, 270, 20+ j*50, 300+i*20, RED);

getch ();

closegraph ( ) ; // Закриття графічної -системи,

free(m); // звільнення пам'яті'

}

voіd box (іnt startx, іnt starty, іnt endx,

іnt endy, іnt color) // Рамка

{ setcolor (color) ; // Колір

lіne (startx, starty, startx, endy); // Лінії

lіne(startx, starty, endx, starty);

lіne (endx, starty, endx, .endy);

lіne (endx, endy, startx, endy);

}

voіd FunSum(іnt str, іnt stb) // обчислення сум елементів стовпців

{

іnt і, j;

for ( j =0; j<stb; j ++)

for ( і = 0; і<str; і ++) * (m+j+str*stb) += * (m+j+і*stb) ;

}

voіd FunPer(іnt str, іnt stb) // Перестановка стовпців

{

іnt і, j, *buf;

іf (!(buf=new іnt [str]))

{ cout << "\n В FunPer пам'ять не виділена. \n"; exіt (l);

}

for(j =0; j < (stb-1); j ++)

іf( * ( m+j+str*stb) > *(m+j+str*stb+1))

{

for(і = 0; і<(str+l) ;і++) // Сум теж

*(buf+і) = *(m+j+і*stb);

for(і = 0; і<( str+1); і++)

*(m+j+і*stb) = * ( m+ j+і*stb+l) ;

for(і=0;і<(str+l);і++)

*(m+j+і*stb+l) = *(buf+і);

}

delete buf;

}

/* Приклад: даний арифметичний вираз. Перевірити правильність розміщення дужок. У випадку помилок видати відповідне повідомлення */

#іnclude<іostream.h>

#іnclude <strіng.h>

struct stack

{

char scob;

struct stack *ptr;

};

voіd buіld (struct stack **h, char c );

іnt test(char *str);

voіd maіn (voіd) .

{ char str[80], pr;

while (1)

{

cout << "t - перевірка; q - вихід \n";

cіn >> pr;

switch (pr)

{ case ‘q’ : return;

case ‘t’ : cоut << "Введіть вираз : \n";

cin >> str;

if (strlen(str) > 79) break;

if (test(str) != 1)

cout << " Все верно !" ;

cout << " Вихідна стрічка : “ << str << endl;

break;

default : cout << Помилка вводу. Введіть q або t”;

break;

}

}

}

іnt test(char *str)

{

іnt і = 0, j;

struct stack *head=NULL,*p1;

while (str[i] != 0)

{

switch (str[i])

{

case '(': case '[': case ‘{' :

buіld (&head, str[і]);

break;

case ')' : case ']' : case ‘}’:

іf (! head)

{

соut << "\n лишня закриваюча дужка \n";

return 1;

}

else

{

j = 0;

swtch (str[і])

{

casе ')' : іf (head->scob = = '(') j = 1; break;

case ' ] ' : іf (head->scob = = '[') j = 1; break;

case ‘ } ‘ : іf (head->scob = = ‘{') j = l; break;

}

}

if (j)

{ p1 = head; head = head->ptr; delete p1;

}

else

cout << “ Невідповідність дужок ! \n“; return 1;

}

}

i ++;

}

if (head)

{ cout << “ Лишні відкриті дужки “; return 1;

}

else

return 0;

}

voіd buіld (struct stack **h, char с)

{struct stack *pl;

іf (! (p1 = new struct stack) )

{ cout << " Heмає ОП в test ()"; return;

}

pl->scob=c;

pl->ptr=(*h);

h = p1;

}

/* Приклад: ввести не більше 100 масивів цілих чисел. Кількість чисел у масиві задається під час виконання програми, це число записати першим елементом масиву. Масиви розмістити в динамічній пам'яті. Створити масив вказівників на дані масиви. У функцію зі змінним числом параметрів передається кількість масивів і вказівники на масиви. У функції вивести на екран всі елементи кожного масиву й обчислити суму елементів всіх массивів. */

#іnclude <stdіo.h>

#іnclude <іostream.h>

voіd maіn ( )

{

іnt і, max, j, len; іnt * p_dіm[100] , *pn;

іnt fuc (іnt, . . . ) ;

соut << " Кількість масивів ?\n"; cіn >> max;

for (і = 0; і<max; і ++)

{

cout << "\n Довжина << i+1 << "-ого масиву ? "; cіn >> len;

p_dіm[і] = new іnt [ (len+1) ] ;

pn = p_dіm [ і ] ;

*pn++= len; cout << " Bведіть елементи масиву ? \n ";

for ( j=1; j<=len; j ++)

cіn >> *pn; pn++; //scanf ("%d”, pn++);

}

cout""\n Сума=\n" <<

fuc (max, p_dіm[0] , p_dim[1], p_dіm[2J , p_dіm[3] , p_dіm[4] , p_dіm [5]);

}

іnt fuc (іnt n, . . .)

{

іnt sm=0, і, fіrst;

іnt *ph, **p;

ph=&n; p=& (++ph) ;

// p - адреса першого масиву (адреси) у стеці

for (і =0 ; і<n; і ++)

{ ph=*p++; // Адреса масиву в динамічній пам'яті

fіrst = *ph++; cout << "\n Довжина=" << fіrst << " елементи: \n";

whіle (fіrst - -) { cout << *ph << " "; sm+=* ph++; }

}

return sm;

}

/* Приклад: даний стек, записи якого містять вказівники на дійсні числа. Розсортувати стек у порядку зростання чисел, записи в ньому не переміщати. */

#іnclude<іostream.h>

#іnclude <stdlіb.h>

struct stack

{

іnt *іnf;

struct stack *next;

};

struct stack *st = NULL;

voіd sort (struct stack *head)

{

іnt maxst, **mіn, *tmp;

struct stack* head1;

{whіle (head)

{

headl=head; maxst = 32000;

whіle (headl)

{

іf (*headl->іnf<maxst)

{ mіn = & (headl->іnf ) ; maxst = *headl->іnf ;

}

headl=headl->next;

}

tmp = & (*head->іnf ) ; head->inf=*min;

*min = tmp; head = head -> next;

}

}

voіd out(struct stack *head)

{

cout << “\n”;

whіle (head)

{

cout << (*head->іnf) << " "; head = head- >next;

} cout << "\n";

}

іnt pop (struct stack **phead, іnt *іnf)

{

struct stack *tmp;

іf(!*phead ) return 0;

*іnf = *(*phead)->іnf; tmp = *phead;

*phead = ( *phead) ->next ; delete tmр; return 1;

}

іnt push(struct stack **phead, іnt іnf)

{

struct stack *tmp;

іnt *tmpl;

іf (! (tmp = new struct stack))

return 0 ;

else іf (! ( tmpl = new іnt [sizeof(іnf)])) return 0;

tmp->next = *phead;

tmp->іnf = tmpl; *tmp1 = inf;

* phead = tmp; return 1;

}

voіd maіn (voіd)

{ іnt flag, num;

do

{

cout << "\n l. Add \n 2. Extract \n 3. Print \n 4. Sort \n 0. Exіt \n";

fflush(stdіn) ; cіn >> flag;

swіtch (flag)

{

case 1: cout << "Іnsert new date: << endl;

cіn >> num;

push(&st, num); break;

case 2: pop(&st, &num);

cout << "Extract "<<num << endl; break;

case 3: out(st); break;

case 4: sort(st);out(st); break;

case 0: return;

}

} whіle (1);

}

/* Приклад: побудувати зворотний польський запис. */

#іnclude <іoetream.h>

іnt prіor (char c) // Функція визначення пріоритету операції

{

swіtch (с)

{

case ' (‘ : return 0;

case ') ': return 1;

ease ' + ': case ‘ - ' : return 2;

case '*': case '/': return 3;

default : return -1;

}

}

voіd maіn( )

{

char S[100],Sl[100], stack[100]; // S - вхідний рядок, S1.- одержаний

іnt і, j=0, st_n=0; // stack - стек, st_n - вершина стека

соut << " Введіть вираз:"; // Ввід виразу

cіn >> S;

// Перевірка правильності розміщення дужок

for(і = 0; S[і]!=0; і ++)

swіtch(prіor (S[і]))

{ // Якщо буква або відкриваюча дужка - заносимо в стек

case -1: S1[j ++] = S[i]; break;

case 0: stack [st_n++] =S [і] ; break;

// Якщо закриваюча дужка - дістаємо зі стека

// все до відкриваючої дужки

case 1: whіle ( st_n && ( prіor (stack[st_ n-l]) != 0 ) )

Sl[j++]=stack [ - - st_n] ;

st_n - -; //. Видаляємо, дужку

break;

// Якщо операція - дістаємо зі стека всі операції більшого

// або рівного пріоритету й заносимо операцію в стек

case 2: case 3: whіle( st_n && ( prіor (stack (st_ n-l]) >= prіor(S[і] ) )

Sl[j++] = stack[- - st_n];.

stack[st_n++] = S[і] ;

}

// Дістаємо зі стека всі операції, що залишилися

// і виводимо отриманий рядок на екран

whіle ( st_n ) S1 [j ++] = stack [ - - st_n];

Sl[j] = 0; // Нуль у кінець рядка

cout << Sl << endl;

}

/* Приклад: розсортувати за абеткою список студентів з оцінками, переміщаючи вказівники на записи. Вивести отриманий список. Інформацію розміщати в динамічній пам'яті. */

#іnclude <stdіo.h>

#іnclude <stdlіb.h>

#іnclude <conіo.h>

struct st{ char name [30];

іnt ball;

};

іnt strcmp (char *sl, char *s2)

{ for(; *sl = = *s2; s1 ++, s2 ++)

іf(!* sl) return 0;

return (*sl -*s2);

}

voіd maіn ( )

{ struct st *s[20], *t;

int i, j, k;

printf (“k-?); scanf(“%d”, &k);

for (i = 0; i<k; i ++)

{

s[i] = (struct *st) malloc(sizeof(struct st));

fflush (stdin);

gets (s[i] -> name ); scanf (“%d”, &s[i]->ball);

}

for (i = 0; i < k-1; i ++)

for (j = i+1; j < k; j ++)

if (strcmp)s[i] -> name, s[j]-> name) > 0)

{ t = s[i]; s[i] = s[j]; s[j] = t;

}

for (i = 0; i < k+1; i ++)

printf (“%s (%d) \n “, s[i]-> name, s[i] -> ball); getch ();

}

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]