Потрібно пам'ятати, що пам'ять для всіх масивів, які визначені як глобальні, відводиться в процесі компіляції і зберігається весь час, поки працює програма.
Часто двовимірні масиви використовуються для роботи з таблицями, що містять текстову інформацію. Також дуже часто використовуються масиви рядків.
ІНІЦІАЛІЗАЦІЯ МАСИВІВ
Дуже важливо уміти ініціалізувати масиви, тобто привласнювати елементам масиву деякі початкові значення. У мові С для цього є спеціальні можливості. Самий простий спосіб ініціалізації наступний: в процесі оголошення масиву можна указати в фігурних дужках список ініціалізаторів:
float а[6]={1.1, 2.2, 3.3, 4.0, 5, 6};
В іншому випадку така форма запису еквівалентна набору операторів:
а[0]=1.1; а[1]=2.2; ... а [5] =6.
Багатовимірні масиви, в тому числі і двовимірні масиви, можна ініціалізувати, розглядаючи іх як масив масивів.
Ініціалізації int а[3][5]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
і int а[3][5]={{1,2,3,4,5}, {6,7,8,9,10}, {11,12,13,14,15}};
еквівалентні.
Кількість ініціалізаторів не зобов'язана співпадати з кількістю
елементів масиву. Якщо ініціалізаторів менше, то значення решти
елементів масиву не визначені.
У той же час ініціалізації
int а[3][5]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
і
int а[3][5]={{1, 2, 3}, {4, 5, 6, 7, 8}, {9, 10, 11}};
різні.
//change strings: 1-6, 2-5, 3-4
#i nclude<std io. h >
void mai n()
{ int temp, i, j, a[6][4]={1,2,3,4,
5,6,7,8,
9,10,11,12,
1 3,14,1 5,16,
17,18,19,20,
21 ,22,23,24};
for (i=0;i<3;i++) for (j=0;j<4;j++)
{ temp=a[i][j]; a[i][j]=a[5-i][j]; a[5-i][j]=temp; } for (i=0; i<6; i++)
{
for (j=0;j<4;j++)
printf ("%4d", a[i][j]);
printf("\n");
}}
Символьні масиви можуть ініціалізувати як звичайний масив:
char str[15]={'В', ' о ' , ' г ' , ' 1 ' , ' а ' , ' n ' , ' d' , ' ',' С',^',^'};
а можуть - як рядок символів:
"char str[15]= Borland C++";
Відмінність цих двох способів полягає в тому, що у другому випадку буде доданий ще і нульовий байт. До того ж другий спосіб коротше. Допускається також оголошення і ініціалізація масиву без явної вказівки розміру масиву. Наприклад, для виділення місця під символьний масив звичайним способом
char str[80]= "Це оголошення і ініціалізація масиву символів";
ми повинні вважати кількість символів в рядку або указати явно більший розмір масиву.
При ініціалізації масиву без вказівки його розміру
char str[ ]= "Це оголошення і ініціалізація масиву символів";
компілятор сам визначить необхідну кількість елементів масиву, включаючи нульовий байт. Можна оголошувати таким же способом масиви будь-якого типу:
int mass []={!, 2, 3, 1, 2, 3, 4};
Від LG: При ініціалізації можна не вказувати розмірність масиву, вона обчислюється автоматично (проте для двовимірних масивів кількість стовпців треба указати), а при оголошенні - обов'язково. При оголошенні масивів з невідомою кількістю елементів можна не вказувати розмір тільки в самих лівих квадратних дужках.
ПОКАЖЧИКИ І АДРЕСИ (Керніган, Рітчі і Б.І.Березін,С.)(Б.Березін)
Пам'ять машини являє собою масив послідовно розташованих і пронумерованих комірок, з якими можна працювати окремо і зв'язаними ділянками. Покажчик - це група комірок в пам'яті комп'ютера, в яких може зберігатися адреса.
Унарний оператор & видає адресу об'єкта, так що інструкція
р=&а;
привласнює адресу комірки а змінній р (тепер р вказує на а або посилається) .
Оператор & застосовується тільки до об'єктів, розташованих в пам'яті: до змінних і елементам масивів. Його операндом не може бути ні вираз, ні константа, ні регістрова змінна.
Унарний оператор * є оператор розкриття посилання. Застосований до покажчика, він видає об'єкт, на який даний покажчик посилається.
ОГОЛОШЕННЯ ПОКАЖЧИКІВ
Якщо змінна буде покажчиком, то вона повинна бути оголошена таким чином:
тип *<ім'я змінної>;
У цьому оголошенні тип - деякий тип мови С, визначальний тип об'єкта, на який вказує покажчик (адреса якого містить); * - означає, що наступна за нею змінна є покажчиком.
ОПЕРАЦІЇ НАД ПОКАЖЧИКАМИ
З покажчиками пов'язані дві спеціальні операції.: & і *. Обидві ці операції є унарними, т. е. мають один операнд, перед якими вони ставляться. Операція & відповідає операції "взяти адресу". Операція * відповідає словам "значення, розташоване за вказаною адресою" .
Особливість мови С полягає в тому, що знак * відповідає двом операціям, що не мають один до одного ніякого відношення: арифметичній операції множення і операції взяти значення. У той же час сплутати їх в контексті програми не можливо, оскільки одна з операцій унарна (містить один операнд), інша - множення - бінарна (містить два операнди). Унарні операції & і * мають найвищий пріоритет нарівні з унарним мінусом.
В оголошенні змінної, що є покажчиком, дуже важливий базовий тип. Якщо покажчик має базовий тип int, то змінна займає 2 байти, char - 1 байт тощо. Приклад.
int а=3, Ь=5;
int *р;
р = &а; /* тепер р вказує на а*/ Ь = *р; /* b тепер дорівнює З*/ *р= 0; /*а тепер дорівнює О*/
&*а => а - розадресація.
Унарні оператори * і & мають більш високий пріоритет, ніж арифметичні оператори:
b = *р + 1 (взяти те, на що вказує р, додати до нього 1, а результат привласнити змінній b.
До покажчиків можна застосувати операцію привласнення. Покажчики одного і того ж типу можуть використовуватися в операції привласнення, як і будь-які інші змінні. Розглянемо приклад. #include <stdio. h> void mai n() { int x= 1 0;
int *p, *g;
p=&x;
g=p;
printf("%p", р); /* друк вмісту р */
printf("%p",g); /* друк вмісту g */
р г і n t f (" % d % d ", x, * g); / * друк величини хі величини за адресою g*/
} Результат: FFF4 FFF4 10 10
У цьому прикладі приведена ще одна специфікація формату функції printf() - %р. Цей формат використовується для друку адреси пам'яті в шістнадцятковій формі.
Не можна створити змінну типу void, але можна створити покажчик на тип void. Покажчику на void можна привласнити покажчик будь-якого іншого типу. Однак при зворотному привласненні необхідно використати явне перетворення покажчика на void/void *pv;
float f, *pf;
pf=&f;
pv=pf;
pp=(fioat*) pv;
У мові С допустимо привласнити покажчику будь-яку адресу пам'яті. Однак, якщо оголошений покажчик на ціле
int *р;
а за адресою, яка привласнена даному покажчику, знаходиться змінна х типу float, то при компіляції програми буде видане повідомлення про помилку в рядку
р=&х;
Цю помилку можна виправити, перетворювавши покажчик на int до типу покажчика на float явним перетворенням типу:
p=(int*)&x;
Але при цьому втрачається інформація про те, на який тип вказував початковий покажчик.
Як і над іншими типами змінних, над покажчиками можна виробляти арифметичні операції: складання і віднімання (операції ++ і є окремими випадками операцій складання і віднімання). Арифметичні
дії над покажчиками мають свої особливості. Виконаємо найпростішу програму
#include <stdio. h> void main() { і n t x= 1 0;
int *p, *g;
p=&x;
g=p;
printf("%p", p); /* друк вмісту p */ printf("%p", p++); /* друк вмісту g */ } Результат: FFF4 FFF6
Після виконання цієї програми ми побачимо, що при операції ++1 значення покажчика р збільшилося не на 1, а на 2. І це правильне, оскільки нове значення покажчика повинно вказувати не на наступну адресу пам'яті, а на адресу наступного цілого. А ціле, як ми пам'ятаємо, займає 2 байти. Якби базовий тип покажчика був не int, a double, то були б надруковані адреси, відмінні на 8 (Результат:
FFEE FFF6), саме стільки байт пам'яті займає змінна типу double, тобто при кожній операції ++р значення покажчика буде збільшуватися на кількість байт, що займаються змінної базового типу покажчика .
Операції над покажчиками не обмежуються тільки операціями ++ і
--. До покажчиків можна додавати деяке ціле або відняти ціле. int *p=2000; float *p=2000;
Р=Р+3; р=р+10;
Результат: р=2006 Результат: р=2040
Загальна формула для обчислення значення покажчика після виконання операції р=р+п; буде мати вигляд
<р>=<р>+п*<кільк.байтів пам'яті базового типу покажчика>
Можна також відняти один покажчик з іншого. Так, якщо р і pi -покажчики на елементи одного і того ж масиву, то операція р-рі дає такий же результат, як і віднімання індексів відповідних елементів масиву.
Інші арифметичні операції над покажчиками заборонені, наприклад не можна скласти два покажчики, помножити покажчик на число і т.д.
#include <std io. h > void rnai n() { int *p, *g, x; p=&x; g=p; printf("\n\n\np=%p", p); P= P + 8; printf(" p+5=%p", p); printf(" g=%p", g); printf(" p-g=%p", p-g); } Результат: p=07DO p+5=07EO g=07DO p-g=0008 | #incl ude <std io. h> void main() { int *p, *g, x; p=&x; g=p; p r і n t f (" \ n \ n \ n p = % p ", p); P= P + 8; printf(" p+5=%p", p); printf(" g=%p", g); printf(" p+g=%p", p+g); } Результат: Error UKAZAT2.CPP 14: Invalid pointer addition |
Покажчики можна порівнювати. Застосовні всі 6 операцій:
<, >, <=, >=, =, == і !=.
Порівняння р < g означає, що адреса, що знаходиться в р, менше адреси, що знаходиться в g.
Якщо рід вказують на елементи одного масиву, то індекс елемента, на який вказує р, менше індексу масиву, на який вказує g.