Масиви символьних рядків і масиви вказівників 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Масиви символьних рядків і масиви вказівників



Масиви символів можна створювати двома способами: з використанням масивів і з використанням вказівників:

 

char с1[ ] = "now is the time";

char * с2 = "now is the time";

 

В цьому визначенні с1 – це масив з кількістю елементів достатньою для розміщення символів рядка ініціалізації і нульового символу. Символи масиву можуть змінюватися, с1 є константним вказівником. с2 – це вказівник, який ініціалізований таким чином, щоб посилатися на рядкову константу. Значення вказівника можна змінювати, елементи символьного рядка – ні. Розглянемо програму, яка буде копіювати  один рядок в інший.

 

#include <iostream>

using namespace std;

int main() {

char c1[ 80 ] = "Good old string";

char * c2 = "Nice new string (pointer version)";

char c3[ 80 ] = "Nice new string (array version)";

char * t1, *t2;

int i;

for (t1 = c1, t2 = c2, i = 0; t2[ i ]!= '\0'; i++)

     t1[ i ] = t2[ i ];

cout << c1 << endl;

for (t1=c1, t2=c3, i=0; (t1[ i ] = t2[ i ])!= 0; i++);

cout << c1 << endl;

t1 = c1;

t2 = c2;

while ((* t1++ = * t2++)!= '\0');

cout << c1 << endl;

t1 = c1;

t2 = c3;

while (* t1++ = * t2++);

cout << c1 << endl;

}

 

Операцію звертання за адресою можна використовувати разом з префіксним інкрементом (декрементом). Ці операції мають однаковий пріоритет і асоціативність справа наліво, тобто спочатку буде збільшене (зменшене) значення вказівника, після цього – виконане звертання за новою адресою. Знайдемо кількість елементів рядка за допомогою вказівників.

 

#include <iostream>

using namespace std;

int main() {

char c[ 80 ] = "A string";

char * t;

t = c;

while (* ++t);

cout << t - c << endl;

}

 

Поширеним видом багатовимірних масивів є масиви символьних рядків, тобто масиви, кожен елемент яких – окремий символьний рядок. Наприклад, масив повідомлень про помилки можна оголосити таким чином:

 

char Error[ 3 ][ 20 ] = {

"File Write Error",

"File Read Error",

"File Open Error"

};

 

Масив Error в пам'яті буде розташований таким чином (рис. 8.1):

 

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

0

F

i

l

e

W

r

i

t

e

E

r

r

o

r

\0

1

F

i

l

e

R

e

a

d

E

r

r

o

r

\0

2

F

i

l

e

O

p

e

n

E

r

r

o

r

\0

 

Рис. 8.1. Розташування елементів масиву рядків в пам'яті

 

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

 

#include <iostream>

using namespace std;

int main() {

char Error[ 3 ][ 20 ] = {

     "File Write Error",

     "File Read Error",

     "File Open Error"

};

cout << Error[ 0 ];  // Перший рядок символів

cout << Error[ 1 ][ 0 ]; // Перший символ другого рядка

}

 

Як і для інших масивів, старшу розмірність можна не указувати, вона буде визначена по кількості ініціалізаторів. В наступній програмі виконується перетворення чисел у двійково-десятковий вид.

 

#include <iostream>

#include <ctime>

using namespace std;

int main() {

srand(unsigned(time (NULL)));

char code[ ][ 5 ] = {

     "0000", "0001", "0010", "0011", "0100",

     "0101", "0110", "0111", "1000", "1001"

};

for (int i = 0, t; i < 20; i++) {

     t = rand() % 100;

     cout << t << "\t" << code[ t / 10 ] << " "

           << code[ t % 10 ] << endl;

}

}

 

В цьому прикладі всі рядки в масиві мають однакову довжину – 5 символів (4 символи для цифр і один символ для нуль – символу), тому всі елементи цього двовимірного масиву будуть зайняті корисною інформацією. В попередньому прикладі для збереження кожного повідомлення про помилку мі виділяли 20 символів, по розміру найбільш довгого повідомлення. Через це частина елементів декількох з рядків не використовувалась.

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

 

#include <iostream>

using namespace std;

int main() {

char * month[ ] = {

     "Wrong month number",

     "January", "February", "March", "April", "May",

     "June", "July", "August", "September", "October",

     "November", "December"

};

int t;

cin >> t;

cout << (1 <= t && t <= 12? month[ t ]: month[ 0 ]);

}

 

тут month – це масив вказівників на char, кількість елементів масиву визначається кількістю ініціалізаторів, їх 13, кожен елемент масиву – вказівник на перший елемент відповідного рядка.

Напишемо програму, яка буде визначати день року для зазначеної дати і навпаки. В цьому прикладі змінна leap має значення 0, якщо рік не високосний і 1 – якщо високосний.

 

#include <iostream>

using namespace std;

int main() {

char * month_name[ ] = {

     "Wrong month number",

     "January", "February", "March", "April", "May",

     "June", "July", "August", "September", "October",

     "November", "Deсember"

};

char ndays[ 2 ][ 13 ] = {

     { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},

     { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}

};

int year = 2011, month = 3, day = 16, i, j, n;

// Номер дня для заданої дати

int leap= year % 4 == 0 && year % 100!= 0 || year % 400 == 0;

for (i = 1, n = day; i < month; i++)

     n += ndays[ leap ][ i ];

cout << n << endl;

// Дата для номера дня і року

n = 75;

for (i = 1; ndays[ leap ][ i ] <= n; i++)

     n -= ndays[ leap ][ i ];

cout << month_name[ i ] << " " << n << endl;

// Всі номера днів

for (i = 1, n = 1; i <= 12; i++)

     for (j = 1; j <= ndays[ leap ][ i ]; j++, n++) {

           cout << n << "\t";

           cout << month_name[ i ] << "\t";

           cout << j << endl;

     }

}

Динамічне виділення пам’яті

До цього часу ми зберігали дані в статичній і автоматичній пам'яті. Статична пам’ять використовується для зберігання глобальних і статичних змінних. Змінні статичної пам'яті створюються на початку роботи програми і видаляються з пам'яті при її завершенні. Автоматична пам’ять зберігає аргументи функцій і локальні змінні. Елементи пам'яті, що зберігаються в автоматичній пам'яті, створюються і знищуються за необхідністю.

Третій тип пам'яті – це динамічна пам’ять. Для використання цієї пам'яті під час виконання програми необхідно явно її виділити, а після використання – звільнити. Елементи даних, що зберігаються в динамічній пам'яті, існують до моменту звільненні пам'яті або до моменту завершення програми. Таким чином змінні, що зберігаються в динамічній пам'яті, не звільняються автоматично при виході з області видимості. Завдяки цьому можна створити змінну всередині функції, а використовувати за її межами.

Виділення пам'яті виконується з допомогою оператора new. Синтаксис оператора такий:

 

new тип_даних;

 

тут тип даних – будь – який допустимий тип даних або клас. Оператор повертає вказівник на виділену ділянку пам'яті, який необхідно зберегти у змінній – вказівнику відповідного типу.

Пам’ять, яка виділяється за допомогою оператора new, після використання має бути звільнена оператором delete. Якщо пам’ять не звільнити, вона буде недоступною для виконання інших операцій. Оператор delete виконується над вказівником на ділянку пам'яті, яка була виділена за допомогою оператора new, або над нульовим вказівником. Синтаксис оператора delete такий:

 

delete вказівник;

 

Після звільнення пам'яті зручно присвоїти вказівнику нульове значення. Це дозволить уникнути помилку, яка виникне при спробі повторного звільнення пам'яті. Приклад динамічного виділення пам'яті:

 

#include <iostream>

using namespace std;

int main() {

int * p;

p = new int;    // Виділення пам’яті

* p = 123;      // Використання пам’яті

cout << * p << endl; // Використання пам’яті

delete p;       // Звільнення пам’яті

}

 

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

 

#include <iostream>

using namespace std;

int main() {

int * p, n = 10;

p = new int[ 10 ];

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

     p[ i ] = i;

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

     cout << * (p + i) << endl;;

delete [ ] p;

}

Структури і об’єднання



Поделиться:


Последнее изменение этой страницы: 2021-12-15; просмотров: 42; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.135.190.232 (0.026 с.)