Указатели и масиви в С/C++. Видове, инициализация. Структура на паметта. Управление на динамичната памет. Връзка между масиви и указатели. Аритметични операции с указатели. Предаване на данни към функция чрез стойност, указател и адрес. Предаване на масиви.
1. Масиви - деклариране и използване
Масивите представляват поредици от наредени последователно в паметта елементи от един и същ тип. За достъп до всеки един елемент на масива се използва индекс, който указва поредния номер на елемента.
Дефиниране на масив:
тип име_на_масив [брой_елементи];
Пример: Нека в една програма на С++ са необходими 5 променливи от целочислен тип. Вместо да декларираме 5 отделни променливи ще декларираме масив от 5 целочислени елемента:
int A [5]; - името на масива е А

На схемата се вижда масивът А с последователно разположените елементи, всеки един от които е от тип int.
Важно! Индексът на всеки масив трябва да бъде цяло число, като първият елемент на масива е с индекс 0.
Инициализиране на масив (първи начин):
int A [5] = { 2, 5, 8, 10, 22 }; - елементите на масива получават конкретни стойности. В тоя случай броят на тези стойности трябва да съответства на броя на елементите в масива.
Инициализиране на масив (втори начин):
int A [] = { 2, 5, 8, 10, 22 }; - елементите на масива получават конкретни стойности. В тоя случай не се указва броя на елементите в масива.
Достъп до елементите на масива: Елементът на масива се използва както променлива, като в квадратни скоби се посочва индексът на този елемент в масива:
име_на_масив [индекс];
Пример: Нека вторият елемент на масива А получи стойност 10 (Забележка: вторият елемент има индекс 1):
A [1] = 10;
Пример: Да се изведе на екрана стойността на втория елемент на масива А:
cout << A [1];
Пример: Променливата х да получи удвоената стойност на втория елемент на масива А:
x = A [1] * 2 ;
Важно! При достъп до елемент на масив индексът не трябва да излиза извън границите на масива.
Пример: A [5] - ще върне неопределена стойност, тъй като индекс 5 надхвърля границата на масива (4).
Пример: Да се направи програма на С++, която въвежда масив М с 10 елемента и намира тяхната сума.
#include "stdafx.h"
#include "iostream.h"
main()
{
int M [10];
int sum=0;
for (int i=0; i<10; i++ )
{
cout<<"Element M[" << i << " ] = ";
cin>>M[i];
sum=sum+M[i];
}
cout << "Sumata e "<< sum << "\n";
}
2. Многомерни масиви - представят се като масиви от масиви
а) двумерен масив (таблица)
Пример: Да се дефинира матрица М (3х5):
int M [3] [5] ;
| 0 | 1 | 2 | 3 | 4 | |
| 0 | |||||
| 1 | |||||
| 2 |
В случая M е двумерен масив, който може да се представи във вид на таблица. За достъп до елемент от този масив се използват два индекса - един, който указва номера на реда и друг, който указва номера на стълба. Например M [1] [2] е елементът на втория ред и на третия стълб.
Важно! Броят на елементите на един многомерен масив е произведение от броя на елементите за всяко измерение. Например масивът М [3] [5] има общо 15 елемента.
б) триизмерен масив
Пример: Да се дефинира масивът M (3x5x2)
int M [3] [5] [2] ;
в) многомерен масив
int M [3] [5] [2] [10] [12] [4]; - шестмерен масив
Пример: Да се въведе матрица М (2х3) и се намери сумата от всичките й елементи.
#include "stdafx.h"
#include "iostream.h"
main()
{
int M [2][3];
int sum=0;
for (int i=0; i<2; i++ )
for (int j=0; j<3; j++ )
{
cout<<"Element M[" << i << "][" << j << "]=";
cin>>M[i][j];
sum=sum+M[i][j];
}
cout << "Sumata e "<< sum << "\n";
}
3. Указатели
а) определение и дефиниране
Всеки обект на езика С++ (променлива, константа, елемент на масив и т.н.) се съхранява в определени клетки от паметта на компютъра. А всяка клетка от паметта има определен номер, който се нарича адрес. Променлива, чиято стойност е адрес от паметта на компютъра, се нарича указател. Стойността на указателя посочва (указва) местоположението на дадена променлива и позволява косвен достъп до стойността на тази променлива, за разлика от директния достъп с помощта на нейното име.
Дефиниране на указател:
тип * име_на_указател;
Примери:
int * p1; - указател към променлива от тип int
float * p2; - указател към променлива от тип float
double * z; - указател към променлива от тип double
char * s; - указател към променлива от тип char
| Адреси в паметта | 100 | 101 | 102 | 103 | 104 |
| Стойности на клетките | 500 | 123 | 0 | 23 | 2 |
При p1=100 - указателят сочи към клетка от паметта с адрес 100. Стойността на тази клетка съгласно схемата е 500.
б) извличане на адрес - операторът &
При деклариране на променливи адресите им се определят от операционната система. Достъп до адреса на една променлива може да се осъществи с оператора "&".
Операторът за извличане на адрес се прилага пред променливата и връща адреса на тази променлива.
Например: &x означава адреса на променливата х.
Валидно е следното присвояване:
p = &x; - в случая променливата р (указател) получава стойността на х.
в) извличане на стойност - операторът * (да не се бърка с оператора за умножение!)
Използвайки указател може да се извлече стойността, към която този указател сочи. Това става с оператора "*".
Пример: a = *p; - а получава стойността, указана от р
Пример: // използване на указатели
#include "stdafx.h"
#include "iostream.h"
main()
{
int value1 = 5, value2 = 15;
int *p;
p = &value1; // p получава адреса на value1
*p = 10; // value1 получава стойност 10
p = &value2; // p получава адреса на value2
*p = 20; // value2 получава стойност 10
cout << "value1= "<< value1 << "\n"<< "value2="<<value2;
}
г) връзка между указатели и масиви
При дефиниране на един масив името му всъщност представлява указател към първия елемент на масива.
Например:
int A [20];
int *p;
p = A; - това присвояване е валидно, тъй като масивът А е всъщност указател
Получавайки указателя на първия елемент на масива А, можем да извършваме следните операции:
int main( )
{
int A [5];
int *p;
p = A;
*p = 10;
p++;
*p = 20;
p = &A[2];
*p = 30;
p = A + 3;
*p = 40;
p = A;
*(p+4) = 50;
for (int n=0; n<5; n++)
cout << A [n] << ", ";
}
Следните два израза са еквивалентни:
A [5] = 0;
* (A+5) = 0;
д) аритметични операции с указатели
Нека имаме следните 3 указателя:
char *mychar; - типът char заема 1 байт
short *myshort; - типът short заема 2 байтa
long *mylong; - типът long заема 4 байтa
и нека те да сочат към адреси от паметта съответно 1000, 2000, 3000
Следните изрази:
mychar++;
myshort++;
mylong++;
задават следните стойности за указателите:
mychar = 1001
myshort = 2002
mylong = 3004
Това е така, защото увеличавайки стойността на един указател с 1 означава той да сочи към следващия елемент от типа, с който е бил дефиниран. С други думи към указателя се добавя размерът в байтове на указания тип.

За определяне броя байтове, които един тип може да приема, се използва операторът sizeof ( тип )
Пример:
sizeof (char) - връща стойност 1
sizeof (short) - връща стойност 2
sizeof (long) - връща стойност 4
4. Управление на динамичната памет
Статична памет - памет, която се заделя за променливи, масиви и други преди стратиране на изпълнение на програмата.
Динамична памет - памет, която се заделя за променливи, масиви и други по време на изпълнение на програмата.
a) оператор new - заделя динамична памет с нужното количество байтове
указател = new тип [ брой_елементи ]
Пример:
int * A;
A = new int [5]; - заделя се памет от 5 елемента от тип int.
б) оператор delete - освобождаване на дамична памет
delete указател
Пример:
delete A; - освобождава паметта, заделена преди това с оператора new
#include "stdafx.h"
#include "iostream.h"
main()
{
int * A;
int n,i;
cout << "Колко числа ще въвеждате? ";
cin >> n;
A = new int [n]; // заделя памет
for (i=0; i<n; i++)
cin >> A [i];
for (i=0; i<n; i++)
cout << A [i] << ", ";
delete A; // освобождава памет
}
5. Предаване на масиви като параметри към функция
В С++ не е възможно да се предаде по стойност цял масив, но може да се предаде неговия адрес. За тази цел при дефиниране на функцията се определя типа на масива по следния начин:
тип име_на_функция (тип име_на_масив [ ] );
Пример:
#include "stdafx.h"
#include "iostream.h"
void print_masiv ( int A[ ] , int L)
{
for (int i=0; i<=L; i++)
cout << A[i] << ", ";
}
main ( )
{
int Masiv1 [ ] = { 2, 4, 2, 5, 6 };
int Masiv2 [ ] = { 1, 2, 3, 4, 5, 10, 34, 45 };
print_masiv ( Masiv1, 5);
print_masiv ( Masiv2, 8);
}