Uploaded by Рощеня Сергей

C++. Лекция 4. Массивы, указатели и функции

advertisement
ЛЕКЦИЯ 4.
МАССИВЫ, УКАЗАТЕЛИ И
ФУНКЦИИ
МАССИВЫ
МАССИВЫ
Массив – это последовательность однотипных элементов, имеющих
единый идентификатор и хранящихся в смежных ячейках памяти.
• Единственный оператор для массива – индексация [].
• Имеет наивысший приоритет (1).
• Начинается с 0.
• Индекс – всегда целое выражение.
ОБЪЯВЛЕНИЕ МАССИВОВ
int massiv[5];
float d[4];
Размер массива при объявлении – всегда целое константное выражение. Нельзя
объявить массив
int i =9;
int m[i];
// нельзя
Внутри квадратных скобок может стоять только константа, но не имя переменной,
чтобы компилятор точно знал, какой объем памяти резервировать. Размер массива
должен быть известен заранее и не может быть изменен в ходе выполнения
программы.
const int MAX_ARRAY = 30;
int massiv[MAX_ARRAY];
ОБЪЯВЛЕНИЕ МАССИВОВ
Когда массив объявлен, к его элементам можно обращаться как к отдельным
переменным, не забывая указывать индекс.
int massiv[5];
massiv[0] = 14;
massiv[1] = massiv[0]*2;
cout << massiv[1]; // 28
cout << massiv[2]; // ерунда
ОБЪЯВЛЕНИЕ МАССИВОВ
• заполним массив нулями:
for (int i = 0; i < 5; i++)
massiv[i] = 0;
• заполним массив от 1 до 5:
for (int i = 0; i < 5; i++)
massiv[i] = i+1;
• выведем массив на экран:
for (int i = 0; i < 5; i++)
cout << massiv[i] << '\t';
ИНИЦИАЛИЗАЦИЯ МАССИВОВ
Массив можно инициализировать одним из трех способов:
•
при создании массива — используя инициализацию по умолчанию (этот
метод применяется только для глобальных и статических массивов);
•
при создании массива — явно указывая начальные константные значения.
•
в процессе выполнения программы — путем записи данных в массив.
При создании в массив могут быть занесены только константные выражения. В
процессе выполнения программы в массив можно записывать и значения
переменных.
Инициализация по умолчанию – нулями (только для глобальных и статических).
ИНИЦИАЛИЗАЦИЯ МАССИВОВ
Массив можно проинициализировать при объявлении (явная инициализация):
int massiv[5] = {2,4,6,8,10};
float d[4] = {2.4, 5.8, 1.347, 3.14};
Можно инициализировать не все элементы:
int massiv[5] = {2,4};
В этом случае остальные элементы будут проинициализированы 0. Чтобы это
сработало, хотя бы один элемент должен быть проинициализирован.
int massiv[5] = {0};
ИНИЦИАЛИЗАЦИЯ МАССИВОВ
Если массив сразу инициализируется, его размер можно не задавать:
int massiv[] = {1,2,3,4,5,6,7,8,9,10,11,12};
Размер будет автоматически вычислен компилятором.
Контроль выхода за пределы массива возложен на программиста.
int massiv[5] = {0};
for(int i = 0; i < 10; i++)
cout << massiv[i] << "\t";
Увидите 5 нулей и 5 порций ерунды.
Размер массива
sizeof(massiv)
•Размер элемента массива
sizeof(massiv[0])
sizeof(int)
массива
//это предпочтительнее
//если не предполагаете менять тип
•К-во элементов в массиве:
sizeof(massiv)/ sizeof(massiv[0])
•№ последнего элемента в массиве:
sizeof(massiv)/ sizeof(massiv[0])-1
РАЗМЕРЫ
COUT << SIZEOF(MASSIV);
//РАЗМЕРА МАССИВА В БАЙТАХ
COUT << SIZEOF(MASSIV[0]);// РАЗМЕР ЭЛЕМЕНТА
// К-ВО ЭЛЕМЕНТОВ
COUT << SIZEOF(MASSIV) / SIZEOF(MASSIV[0]);
// НОМЕР ПОСЛЕДНЕГО ЭЛЕМЕНТА
COUT << SIZEOF(MASSIV) / SIZEOF(MASSIV[0]) - 1;
И.М.Желакович
БГУИР
ОСНОВНЫЕ ЗАДАЧИ С
МАССИВАМИ
сумма элементов массива
int massiv[5] = {6, -4, 17, 139, 0};
int sum = 0;
for (int i = 0; i < 5; i++)
sum += massiv[i];
0+6+(-4)+17+139+0
cout << sum << endl;
6
-4
17
2
19
139
0
0
6
И.М.ЖЕЛАКОВИЧ
158
БГУИР
158
ОСНОВНЫЕ ЗАДАЧИ С
МАССИВАМИ
среднее арифметическое:
int massiv[5] = {6, -4, 17, 139, 0};
double sum = 0;
for (int i = 0; i < 5; i++)
sum += massiv[i];
cout << sum/5 << endl;
6
-4
17
139
(6+(-4)+17+139+0)/5
И.М.ЖЕЛАКОВИЧ
БГУИР
0
ОСНОВНЫЕ ЗАДАЧИ С
МАССИВАМИ
максимальный элемент в массиве:
int massiv[5] = {6, -4, 17, 139, 0};
int max = massiv[0];
// считаем максимальным первый
for (int i = 1; i < 5; i++)
if(massiv[i] > max)
max = massiv[i];
cout << max<< endl;
6
6
-4
17
139
0
6
17
139
139
И.М.ЖЕЛАКОВИЧ
БГУИР
МНОГОМЕРНЫЕ МАССИВЫ.
Двумерный массив рассматриваются как массив элементов, каждый из
которых является одномерным массивом.
Трехмерный - как массив, элементами которого являются двумерные
массивы и т.д.
int matrix[3][4]; // Массив из 3-х элементов, каждый из которых является
//
int matrix[3][4][2];
массивом из 4-х целых чисел.
МНОГОМЕРНЫЕ МАССИВЫ.
Двумерные массивы в памяти хранятся по строкам, т.е. при обращении к элементам
в порядке их размещения в памяти быстрее всего меняется самый правый индекс.
Так, для массива c[2][3] его шесть элементов расположены в памяти так:
c[0][0]
c[0][1]
c[0][2]
c[1][0]
c[1][1]
c[1][2].
Многомерные массивы также можно инициализировать при описании:
int d[2][3]={ 1, 2, 0, 5 };
В этом случае первые 4 элемента массива получат указанные значения, а остальные
два инициализируются нулями.
МНОГОМЕРНЫЕ МАССИВЫ.
Если инициализируется многомерный массив, то самую первую
размерность можно не задавать (и только ее). В этом случае компилятор
сам вычисляет размер массива:
int f[][2] = {2, 4, 6, 1};
// массив f[2][2]
int a[][2][2] = {1, 2, 3, 4, 5, 6, 7, 8}; //массив a[2][2][2]
МНОГОМЕРНЫЕ МАССИВЫ.
Инициализирующее выражение может иметь вид, отражающий факт, что массив
является, например, двумерным:
int c[2][3]={{1, 7},{-5, 3}};
В этом случае первая и вторая строки инициализируются не до конца. Элементы
c[0][2] и c[1][2](3-й столбец), - инициализируется нулями.
1
7
0
-5
3
0
МНОГОМЕРНЫЕ МАССИВЫ.
Трехмерный массив можно рассматривать как 3 страницы 2х2:
int massiv[3][2][2] = {{1,2,3},{5,6,7,8},{9,10,11,12}};
на месте 4 будет 0
int massiv[3][2][2] = {{0},{5,6,7,8},{9,10,11,12}};
пустые скобки оставлять нельзя.
ПРИМЕРЫ
Для примера – вывод одномерного массива
int massiv[] = {1,2,3,4,5,6,7,8,9,10,11,12};
for(int i=0;i<12; i++)
{
cout << massiv[i] << "\t";
}
cout << "\n";
ПРИМЕРЫ
Вывод всех элементов для двумерного массива:
int massiv[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
for(int i=0;i<3; i++)
{
for(int j=0;j<4; j++)
cout << massiv[i][j] << "\t";
cout << "\n";
}
ПРИМЕРЫ
Если массив трехмерный
int massiv[3][2][2] = {1,2,3,4,5,6,7,8,9,10,11,12};
for(int k = 0; k < 3; k++)
{
for(int i = 0; i < 2; i++)
{
for(int j = 0; j < 2; j++)
cout << massiv[k][i][j] << "\t";
cout << "\n";
}
cout << "*******************\n";
}
ПРИМЕРЫ
Подсчет суммы элементов
int massiv[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
int sum = 0;
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 4; j++)
sum += massiv[i][j];
}
cout << sum << "\n";
ПРИМЕРЫ
Подсчет суммы по строкам
int massiv[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
int sum;
for(int i = 0; i < 3; i++)
{
sum = 0;
for(int j = 0; j < 4; j++)
sum += massiv[i][j];
cout << sum << "\n";
}
ПРИМЕРЫ
Поиск максимума
int massiv[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
int max = massiv[0][0];
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 4; j++)
if (max < massiv[i][j])
max = massiv[i][j];
}
cout << max << "\n";
ПРИМЕРЫ
вывод главной диагонали
int massiv[3][3] = {1,2,3,4,5,6,7,8,9};
for(int i = 0; i < 3; i++)
{
cout << massiv[i][i];
}
СТРОКОВЫЕ МАССИВЫ
В C отсутствует тип данных для символьных строк. Строка представляется в виде
массива символов (char). Каждый символ хранится в отдельной ячейке массива, а в
последней ячейке содержится null-символ ‘\0’.
Длина массива должна учитывать ‘\0’.
char s[5];
s[0] = 's';
s[1]='h';
s[2] = 'i';
s[3]='p';
s[4]='\0';
СТРОКОВЫЕ МАССИВЫ
Или сразу
char s[5] = {'s','h','i','p','\0'};
char s[] = {'s','h','i','p','\0'};
// можно не задавать размер
СТРОКОВЫЕ МАССИВЫ
Можно проще:
char s[5] = "ship"; // в этом случае null-символ будем
добавлен автоматически
Или
char s[] = "ship";
СТРОКОВЫЕ МАССИВЫ
Массив можно заполнить при помощи функции scanf:
scanf("%s",s);
Обратите внимание, что взятие адреса (&) здесь не требуется.
Имя массива – это адрес самого первого элемента. Это константа,
которая не может быть изменена программным путем.
СТРОКОВЫЕ МАССИВЫ
Для работы со строками используются специальные функции:
gets(stroka);
получает символы из стандартного устройства в/в (клавиатура) и
помещает их в строковый массив.
Когда будет нажата клавиша ENTER, в функцию передается символ
перевода строки. Функция заменяет его на null-символ.
Это гарантирует, что в символьном массиве окажется именно строка, а
не просто набор значений.
Никаких проверок размера массива не производится.
СТРОКОВЫЕ МАССИВЫ
Функция
puts(stroka);
отображает строку на экране. При этом null-символ заменяется на
символ перевода строки.
Эти функции симметричны.
СТРОКОВЫЕ МАССИВЫ
При использовании функции fgets() можно задавать максимальное число
вводимых символов. Функция прекращает считывание из заданного файлового
потока в тот момент, когда число считанных символов на 1 меньше заданного
аргумента. fputs() выводит строку, не добавляя никаких переводов строки.
char stroka[20];
fgets(stroka,20,stdin);
fputs(stroka,stdout);
qwerty
qwerty
123456789012345678901234567890
1234567890123456789
СТРОКОВЫЕ МАССИВЫ
Функция sprintf() – помещает форматированный вывод в строку.
char str1[4] = "one";
char str2[10];
sprintf(str2,"***%s***",str1);
СТРОКОВЫЕ МАССИВЫ
Множество функций для работы со строками содержит библиотека
<string.h>.
strcpy(str1,"Good "); // длина строки не контролируется
strcat(str1," morning!");
int i = strlen(str1); //без учета null-символа
СТРОКОВЫЕ МАССИВЫ
strncmp()
char szstringA[] = "Вита", szstringB[]= "Вика";
int istringA_length, iresult = 0;
istringA_length = strlen(szstringA);
if(strlen(szstringB) >= strlen(szstringA))
iresult = strncmp(szstringA, szstringB, istringA_length);
printf ("Строки %s совпадают",, iresult == 0 ? "" : "не ");
return(0); }
УКАЗАТЕЛИ
УКАЗАТЕЛИ
Указатель (pointer) - это переменная, содержащая адрес другой переменной,
функции или объекта.
Точнее - адрес первого байта.
Это дает возможность косвенного доступа к этому объекту через указатель.
int *pa, *pb, *pc; // объявление нескольких указателей одного типа
Тип указателя должен совпадать с типом переменной, адрес которой он
хранит.
УКАЗАТЕЛИ
Контроль за типом указателя в С более строгий, чем за типом
переменной:
int a = 5;
float b = 3.4;
a = b; // это возможно
-----------------------------------------------
int *pa;
float *pb;
...
pa = pb; // ошибка
УКАЗАТЕЛИ
Нельзя использовать в программе указатель, значение которого не
определено (но ошибки это не вызовет). Можно проинициализировать
при объявлении:
double *px = 0; // это указатель в никуда
double *px = NULL; // тоже самое, но более грамотно
double y;
double *py = &y; // здесь адрес y
УКАЗАТЕЛИ
Операция & применима только к адресным выражениям
(ℓ-выражения), так что конструкции вида &(x-1) и &3 незаконны.
УКАЗАТЕЛИ
Унарная операция * называется операцией разыменования
(разадресации, операцией разрешения адреса). Эта операция
извлекает значение по указанному адресу.
z = *px + *py;
ОПЕРАЦИИ С УКАЗАТЕЛЯМИ.
Название
Взятие адреса
Разыменование
Присваивание
Инкремент
Декремент
Сложение
Знак
&
*
=
++
-+
Сложение с замещением
Вычитание
+=
–
Вычитание с замещением –=
Пояснение
Получить адрес переменной
Получить значение переменной по адресу
Присвоить указателю адрес переменной или 0
Увеличить указатель на целое значение (на след. элемент массива)
Уменьшить указатель на целое значение (на пред. элемент массива)
Увеличить указатель на целое значение и присвоить другому
указателю
Увеличить существующий указатель на целое значение
Уменьшить указатель на целое значение или на значение другого
указателя, если оба указывают на один и тот же массив, и
присвоить третьему указателю.
Уменьшить указатель на целое значение.
ОПЕРАЦИИ С УКАЗАТЕЛЯМИ.
Название
Отношения
Выделение памяти
Освобождение
памяти
Знак
== !=
< <=
> >=
calloc,
malloc,
new
free,
delete
Пояснение
Сравнение указателей – истина или ложь
Получить указатель на начало выделенного блока памяти
Освободить выделенный
недоступным
блок
памяти
и
сделать
указатель
УКАЗАТЕЛИ И МАССИВЫ
Имя одномерного массива само по себе является указателем.
int x[]= {1,2,3,4,5,6,7,8,9,10,11,12};
cout << x; // выведет какой-то адрес 0x0012FF50
int *px1 = &x[0];
int *px2 = x;
// берем адрес первого элемента
// не требуется использовать &
УКАЗАТЕЛИ И МАССИВЫ
Но это работает только с одномерными массивами
int y[][2]= {1,2,3,4,5,6,7,8,9,10,11,12};
int *py = y; // так делать нельзя
А вот так можно:
int *py1 = &y[0][0];
int *py2 = y[0];
А это уже другой адрес:
int y[][2]= {1,2,3,4,5,6,7,8,9,10,11,12};
int *py3 = y[1];
А что вернет *py3?
(3) – нулевой элемент первой строки
УКАЗАТЕЛЬ МОЖНО СКЛАДЫВАТЬ С ЦЕЛЫМ
int x[]= {1,2,3,4,5,6,7,8,9,10,11,12};
cout << x + 5 << &x[5]; // один и тот же адрес
cout << x[5] << *(x+5); // увидим 6
px++;
// следующий элемент x
0x0012FF78
py--;
// предыдущий элемент y 0x0012FF88
ОПЕРАЦИИ С УКАЗАТЕЛЯМИ
Указатели можно сравнивать.
px = &x[9];
py = &x[4];
px > py
Указатели можно вычитать.
px – py даст 5
py – px даст - 5
РАБОТА С МНОГОМЕРНЫМИ МАССИВАМИ
int x[][3][2]= {1,2,3,4,5,6,7,8,9,10,11,12};
int *px = &x[0][0][0]; // вывод всех элементов массива
for (int i = 0; i < 12; i++)
cout << *(px++) << "\t";
РАБОТА СО СТРОКАМИ
Строки интерпретируются как обычные
массивы с размером каждого элемента в 1
символ (1 байт). Имя строки – это адрес.
Вывод обычного массива:
int s[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = s;
int i = 10;
while (i--)
cout << *(p++) << endl;
1
2
3
4
5
6
7
8
9
10
РАБОТА СО СТРОКАМИ
Вывод такого же строкового
массива:
i
char s[10] = "irina";
r
char *p = s;
i
while (*p)
n
cout << *(p++) << endl;
a
РАБОТА СО СТРОКАМИ
Но если убрать разыменование:
char s[10] = "irina";
irina
char *p = s;
rina
while (*p)
ina
cout << p++ << endl;
na
a
УКАЗАТЕЛИ НА УКАЗАТЕЛИ
В языке С есть возможность описать переменные-указатели, указывающие на другие
указатели.
int i = 10;
int *pi = &i;
int **ppi = π
int ***pppi = &ppi;
УКАЗАТЕЛИ НА УКАЗАТЕЛИ
Но нельзя написать
&&i
&(&i)
Взять адрес можно только у переменной, а &i не является переменной.
УКАЗАТЕЛИ НА КОНСТАНТУ
Допустим, в программе имеется константа
const double PI = 3.1415;
Ясно дело, ее нельзя менять – на то и константа.
Но что, если
double *pPI = Π // так нельзя
++ *pPI;
Отследить такое любому компилятору непросто.
УКАЗАТЕЛИ НА КОНСТАНТУ
Поэтому компилятор запрещает присваивать адреса констант
обычным указателям.
const double *pPI = Π //указатель на константу
обратите внимание – const стоит перед типом double (именно
значение double нельзя менять)
*pPI = 3.14159; // ошибка
УКАЗАТЕЛИ НА КОНСТАНТУ
Но сам указатель – не константа, его можно менять.
const double E = 2.71828;
pPI = &E;
double g = 9.8; // можно и без const
pPI = &g;
УКАЗАТЕЛИ НА КОНСТАНТУ
Адрес константного объекта присваивается только указателю на
константу. Вместе с тем, такому указателю может быть присвоен и
адрес обычной переменной:
Хотя g в примере выше и не является константой, компилятор не
допустит изменения переменной g через pPI. (Опять-таки потому, что
он не в состоянии определить, адрес какого объекта может содержать
указатель в произвольный момент выполнения программы.)
КОНСТАНТНЫЕ УКАЗАТЕЛИ
Существуют и константные указатели. (Обратите внимание на разницу
между константным указателем и указателем на константу!).
Константный указатель может адресовать как константу, так и
переменную. Например:
int Nomer = 0;
int *const pNomer = &Nomer;
Здесь pNomer – константный указатель на неконстантный объект. Это
значит, что мы не можем присвоить ему адрес другого объекта, хотя
сам объект допускает модификацию. (Квалификатор const стоит
перед именем pNomer – именно эту переменную нельзя изменять)
КОНСТАНТНЫЕ УКАЗАТЕЛИ
Вот как мог бы быть использован указатель pNomer:
pNomer = Π // ошибка – нельзя изменять сам указатель
*pNomer = 100; // все в порядке – можно изменять значение по адресу
КОНСТАНТНЫЙ УКАЗАТЕЛЬ НА КОНСТАНТУ
Константный указатель на константу является объединением двух
рассмотренных случаев.
const double *const pi_ptr = π
Ни значение объекта, на который указывает pi_ptr, ни значение
самого указателя не может быть изменено в программе.
ПРИМЕРЫ
int i;
int j = -1;
const int I1;
const int J1 = j;
J1 = 5;
int * const pI1;
int * const pJ2 = &j;
int * const pJ1 = &J1;
pJ2 = &i;
*pJ2 = 5;
ПРИМЕРЫ
int i;
int j = -1;
// переменная i
// переменная j проинициализир.
const int I1;
// ошибка!! константа – нужно инициализировать
const int J1 = j; // ошибка!! константа иниц. переменной
J1 = 5;
//ошибка!! константу менять нельзя
int * const pI1; // ошибка!!константный указатель- нет инициал.
int * const pJ2 = &j; // иниц.адресом обычной переменной
int * const pJ1 = &J1; //ошибка!! иниц.адресом константы (константа - только сам указатель)
pJ2 = &i;
// ошибка!! сам указатель менять нельзя
*pJ2 = 5;
// а значение по нему - менять можно
ПРИМЕРЫ
const int *pJ3;
pJ3 = &J1;
pJ3 = &i;
*pJ3 = 8;
const int * const pI4;
const int * const pI5= &i;
const int * const pJ4= &J1;
pJ4 = &J1;
*pJ4 = 8;
ПРИМЕРЫ
const int *pJ3;
// указатель на константу – можно
инициализировать позже
pJ3 = &J1;
// сам указатель можно менять
pJ3 = &i;
// не обязательно на адрес константы
*pJ3 = 8;
//ошибка! значение по указателю менять нельзя
(даже если это просто i)
const int * const pI4; // ошибка! конст. указатель на константу
(нужно инициализ.)
const int * const pI5= &i; // ошибка! и не чем-нибудь, а адресом
константы
const int * const pJ4= &J1; //
pJ4 = &J1; // ошибка! менять нельзя
*pJ4 = 8; // ошибка! тоже нельзя
УКАЗАТЕЛИ НА VOID
void *pv = NULL;
int a = 5,*pa =&a;
pa = pv; // ошибка
pv = pa;
pv++; // ошибка
cout << pa;
//адрес
cout << pv;
// адрес
cout << *pa;
//5
cout << *pv;
// ошибка
ДИНАМИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ ПАМЯТИ
При компиляции программы на С память компьютера, выделенная
программе, разбивается на 4 области:
•
область кода;
•
область глобальных данных;
•
область стека (stack);
•
динамическая область (heap – куча).
ДИНАМИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ ПАМЯТИ
Когда объявляются локальные переменные, они создаются в стеке, и при этом
указатель стека двигается вниз. Когда область действия переменных заканчивается
(выход из функции - локальная переменная больше не нужна), память
автоматически освобождается путем смещения указателя стека вверх.
Размер стековой памяти должен быть известен при компиляции. Стек занимает
нижнюю часть области программы и растет вниз.
ДИНАМИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ ПАМЯТИ
В программе возможно существование переменных, размер которых при компиляции
неизвестен. Для них необходимо самостоятельно выделять память из свободной
области. Свободная область занимает верхнюю часть памяти программы и растет
вверх.
Эти данные не освобождаются автоматически (вопрос – видны ли указатели?).
Поэтому не забывайте освобождать ненужную динамическую память.
ДИНАМИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ ПАМЯТИ
Функции С по захвату и освобождению памяти
void *calloc(size_t nmemb, size_t size);
void *malloc(size_t size);
calloc() распределяет память для массива размером nmemb,
каждый элемент которого равен size байтов, и возвращает
указатель на распределенную память. Память при этом
"очищается".
malloc() распределяет size байтов и возвращает указатель
на распределенную память. Память при этом не "очищается".
ДИНАМИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ ПАМЯТИ
Функции С по захвату и освобождению памяти
void free(void *ptr);
free() освобождает место в памяти, на которое указывает ptr,
возвращенный, по-видимому, предшествующим вызовом функций
malloc(), calloc(). Иначе (или если уже вызывался free(ptr))
дальнейший ход событий непредсказуем. Если ptr равен NULL, то не
выполняется никаких действий.
ДИНАМИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ ПАМЯТИ
Функции С++:
c помощью операции new :
int *n = new int;
int *m = new int (10);
float *pf = new float (-3.5);
Освобождение памяти:
delete pf;
ДИНАМИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ ПАМЯТИ
Становится возможным создавать массивы, размер которых
становится известен лишь в процессе выполнения программы.
указатель = new тип_массива [размер];
delete [] указатель;
int i = 10;
int m[i]; // так нельзя
int *pm;
pm = new int[i]; // так можно
delete [] pm; // когда массив уже не нужен
ДИНАМИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ ПАМЯТИ
//пример работы с динамическим массивом
int size; cout << "Введите размер массива "; cin >> size;
int *pmassiv = new int[size];
for (int i=0; i < size; i++)
{
cout << "Введите элемент ";
cin >> pmassiv[i];
}
ФУНКЦИИ
СТАНДАРТНЫЕ МАТЕМАТИЧЕСКИЕ ФУНКЦИИ
Подключение мат. библиотеки
#define _USE_MATH_DEFINES // for C++
#include <cmath>
#define _USE_MATH_DEFINES // for C
#include <math.h>
ИСПОЛЬЗОВАНИЕ КОНСТАНТ
#define _USE_MATH_DEFINES // for C
#include <math.h>
#include <iostream>
using namespace std;
void main()
{
cout << M_PI << endl;
cout << M_E << endl;
cout << M_SQRT2 << endl;
double r = 1.;
cout << 4./3 * M_PI * pow(r,3) << endl;
}
ПОНЯТИЕ ФУНКЦИИ
Функция —это поименованная часть программы, которая может
вызываться из других частей программы столько раз, сколько
необходимо. Функция может возвращать некоторое значение.
Функции – это основные единицы построения программ при
процедурном программировании на языке Си++.
Функции используются для того, чтобы организовать программу в виде
совокупности небольших и не зависящих друг от друга частей. Она
инкапсулирует алгоритм или набор алгоритмов, применяемых к
некоторому набору данных.
ПОНЯТИЕ ФУНКЦИИ
В языке "C" функции эквивалентны подпрограммам или процедурам в
других языках программирования. Функции дают удобный способ
заключения некоторой части вычислений в черный ящик, который в
дальнейшем можно использовать, не интересуясь его внутренним
содержанием. Использование функций является фактически
единственным способом справиться с потенциальной сложностью
больших программ.
ПОНЯТИЕ ФУНКЦИИ
Если функции организованы должным образом, то можно
игнорировать то, как делается работа; достаточно знание того, что
делается. Язык "C" разработан таким образом, чтобы сделать
использование функций легким, удобным и эффективным. Вам будут
часто встречаться функции длиной всего в несколько строчек,
вызываемые только один раз, и они используются только потому, что
это проясняет некоторую часть программы.
ПОНЯТИЕ ФУНКЦИИ
Функция не может быть определена в другой функции.
ПОНЯТИЕ ФУНКЦИИ
С использованием функции связаны 3 понятия - определение
функции, объявление функции (прототип) и вызов функции.
ПРОТОТИП ФУНКЦИИ
Прежде всего, функцию необходимо объявить. Объявление функции, аналогично
объявлению переменной, определяет имя функции и ее тип – типы и количество
ее аргументов и тип возвращаемого значения.
double sqrt(double x); //функция sqrt с аргументом – double и
результатом double
int sum(int a, int b, int c); // функция от 3 целых аргументов
возвращает целое
ПРОТОТИП
Имена параметров в прототипе функции не обязательны, их область
видимости – только сам прототип:
double sqrt(double); //функция sqrt с аргументом – double и
результатом double
int sum(int, int, int); // функция от 3 целых аргументов
возвращает целое
ПРОТОТИП
Прототип функции аналогичен заголовку (первой строке в
определении), но заканчивается точкой с запятой.
После того, как функция объявлена, ее можно использовать в
выражениях.
Разные компиляторы по-разному относятся к необходимости
прототипов. Часто в литературе можно встретить утверждение, что
функцию ОБЯЗАТЕЛЬНО нужно объявлять. Но на практике часто этого
не делают.
ПРОТОТИП
Если функция описана после вызова, ее нужно предварительно
объявить (обычно, в начале программы).
Если функция описана раньше – можно обойтись без объявления.
Однако, хорошее правило – объявлять все используемые функции в
начале. (Речь пока идет об одномодульной программе).
ОПРЕДЕЛЕНИЕ И ВЫЗОВ ФУНКЦИИ
тип имя ( список описаний аргументов ){ операторы }
Здесь имя - это имя функции (придуманный вами идентификатор);
тип - тип возвращаемого функцией значения;
операторы в фигурных скобках { } – тело функции.
Аргументы в списке описаний называют формальными параметрами.
ОПРЕДЕЛЕНИЕ
Например, функция, находящая и возвращающая максимальное значение из двух
целых величин a и b определяется так:
int max(int a, int b)
{
return(a >= b) ? a : b;
}
Это определение говорит о том, что функция с именем max имеет два целых
аргумента и возвращает целое значение. Если функция действительно должна
возвращать значение какого-либо типа, то в ее теле обязательно должен
присутствовать оператор return выражение; при выполнении этого оператора
выполнение функции прекращается, управление передается в функцию,
вызывающую данную функцию, а значением функции будет значение выражения.
ОПРЕДЕЛЕНИЕ
int max(int a, int b)
{
return (a >= b) ? a : b;
}
void main( )
{
int i = 2, j = 3;
int c = max( i, j );
cout<<" max= "<< c<< "\n";
c = max(i * i, j) * max(5, i - j);
cout << " max= " << c << "\n";
}
В этой программе приведено определение функции max и 3 обращения к ней. При
обращении указывается имя функции и в круглых скобках список фактических
параметров.
ОПРЕДЕЛЕНИЕ
Если у функции нет формальных параметров, то она определяется,
например, так:
double f(void){тело функции};
или, эквивалентно,
double f() {тело функции};
Обращаются в программе к этой функции, например, так (скобки
обязательны):
a = b*f() + c;
ФУНКЦИЯ, НЕ ВОЗВРАЩАЮЩАЯ ЗНАЧЕНИЕ
Функция может и не возвращать никакого значения. В этом случае ее
определение таково:
void имя (список описаний аргументов){операторы}
Вызов такой функции имеет вид:
имя (список фактических аргументов);
ФУНКЦИЯ, НЕ ВОЗВРАЩАЮЩАЯ ЗНАЧЕНИЕ
Выполнение функции, не возвращающей никакого значения,
прекращается оператором return без следующего за ним выражения.
Выполнение такой функции и возврат из нее в вызывающую функцию
происходит также и в случае, если при выполнении тела функции
произошел переход на самую последнюю закрывающую фигурную
скобку этой функции.
ФУНКЦИЯ, НЕ ВОЗВРАЩАЮЩАЯ ЗНАЧЕНИЕ
В качестве примера приведем функцию печати целого числа.
void pr (int a)
{
printf("a = %d",a);
}
void main()
{
int i = 5;
pr(i);
}
Download