...
}
int b[7][4];
...
print(b);
Відзначимо, що інформація про другий розмір двовимірного масиву не втрачається і, більш того, істотно необхідна для інтерпретації усередині функції запису а[i]: для обчислення значення вираження a+i до вмісту вічка а у внутрішній виставі додається величина i*sizeof(int[4]). Саме з цієї причини жодного способу передавати у функції звичайні масиви із змінною другою розмірністю не існує.
При передачі у функцію динамічного двовимірного масиву подібних проблем не виникає:
void print_dyn(int** а, int n, int m)
{
for (int i=0; i<n; i++)
{
for (int j=0; j<m; j++)
cout<<a[i][j];
cout<<endl;
}
}
int** b=new int*[5];
for (int i=0; i<5; i++)
b[i]=new int[6];
print_dyn(b,5,6);
Тут при трактуванні запису а[i][j] усередині функції інформація про другий розмір масиву не використовується, що дозволяє передавати у функцію print_dyn динамічні масиви довільних розмірів. Відзначимо, що передавати звичайні двовимірні масиви у функцію print_dyn не можна [7].
2.1.2.1 Приклад 1
Програма використовує вказівник на символьний рядок усередині функції show_string для виведення змісту рядка по одному символу за один раз:
#include <iostream.h>
void show_string(char *string)
{
while (*string != '\0')
{
cout << *string;
string++;
}
}
void main(void)
{
show_string( "Учимся программировать на языке C++!");
}
Реакція ЕОМ:
Учимся программировать на языке C++!
Умова while (*string != '\0') перевіряє, чи немає поточний символ, що вказується за допомогою вказівника string, символом NULL, який визначає останній символ рядка.
Якщо символ не NULL, цикл виводить поточний символ за допомогою cout. Потім оператор string++; збільшує вказівник siring таким чином, що він вказує на наступний символ рядка.
Коли вказівник string вказує на символ NULL, функція вже вивела рядок і цикл завершується.
Наприклад, рядок, переданий у функцію, знаходиться в пам'яті комп'ютера за адресою 1000. Кожного разу, коли функція збільшує вказівник string, він вказує на наступний символ (адреса 1001,1002, 1003 і т. д.)
2.1.2.2 Приклад 2
Программа PTR_LEN.CPP використовує вказівник на строку у функції string_length для визначення кількості символів в рядку:
#include <iostream.h>
int string_length(char *string)
{
int length = 0;
while (*string != '\0')
{
length++;
string++;
}
return(length);
}
void main(void)
{
char title[] = "Учимся программировать на языке C++";
cout << title << " содержит " << string_length(title) << " символов";
}
Реакція ЕОМ:
Учимся программировать на языке C++ содержит 35 символов
Функція string_length сканує символи рядка до тих пір, поки не зустріне символ NULL.
Збільшення вказівника на символьний рядок
Коли програма передає масив у функцію, C++ передає адресу пам'яті першого елементу цього масиву. Використовуючи змінну-вказівник, функція може переміщатися по вмісту масиву, просто збільшуючи значення вказівника.
Наприклад, передбачимо, що програма передає у функцію символьний рядок "Привіт". Усередині функції змінна-вказівник спочатку вказує на ділянку пам'яті, яка містить букву 'П'.
Коли функція збільшує вказівник, то він далі вказує на ділянку пам'яті, яка містить букву 'р'. У міру збільшення функцією значення вказівника, він по черзі вказує на кожну букву в рядку і нарешті вказує на символ NULL.
2.1.2.3 Приклад 3
Наступна функція string_uppercase використовує вказівники для перетворення символів рядка в символи верхнього регистру:
char *string_uppercase(char* string)
{
char *starting_address = string; // адрес string[0];
while (*string)
{
if ((*string >= 'а') && (*string <= 'я')) *string = *string - 'a' + 'A';
string++;
}
return(starting_address);
}
Ця функція зберігає і повертає початкову адресу рядка, це дозволяє програмам використовувати функцію таким чином:
cout << Btring_uppercase("Привет, мир!") << endl;
Реакція ЕОМ:
ПРИВЕТ, МИР!
2.1.2.4 Приклад 4
Не дивлячись на те що вказівники широко використовуються з символьними рядками, ви можете використовувати вказівники з масивами інших типів. Наприклад, наступна програма PTRFLOAT.CPP використовує вказівник на масив типа float для виведення значень з плаваючою крапкою:
#include <iostream.h>
void show_float(float *array, int number_of_elements)
{
int i;
for (i = 0; i < number_of_elements; i++) cout << *array++ << endl;
}
void main(void)
{
float values[5] = {1.1, 2.2, 3.3, 4.4, 5.5);
show_float(values, 5);
}
Реакція ЕОМ:
1.1, 2.2, 3.3, 4.4, 5.5
усередині функції show_float цикл for використовує значення, що вказується за допомогою вказівника array, а потім збільшує цей вказівник до наступного значення. В даному випадку програма повинна передати параметр, який задає кількість елементів масиву, оскільки на відміну від символьних рядків масиви типа float (або int, long і т. д.) не використовують символ NULL для визначення останнього елементу.
2.1.2.5 Приклад 5
Сортування масиву з елементами довільного типа і критерієм порівняння, передаваним як параметр.
В данном примере реализована функция ssort, сортирующая методом пузырька массив данных произвольного типа. Количество элементов задается параметром n, размер каждого элемента – параметром sz, функция сравнения – параметром cmp. Поскольку типы элементов заранее неизвестны, указатель на первый элемент передается как void*. Внутри функции ssort он преобразуется к типу char* для возможности работы с адресной арифметикой. Функция memswap меняет местами две области памяти размера sz. В функцию сравнения передаются два указателя void* на сравниваемые элементы массива. Каждая конкретная функция сравнения вначале преобразует эти указатели к нужному типу и затем осуществляет собственно сравнение элементов. В программе, демонстрирующей варианты применения функция ssort, реализована сортировка целых чисел (по возрастанию и убыванию), текстовых строк, а также структур (по нескольким полям).
typedef int (*CMP)(const void*,const void*);
inline void swap(char& a, char& b)
{
char temp=a;
a=b;
b=temp;
}
void memswap(char* a, char* b, size_t sz) {
for (int k=0; k<sz; k++)
swap(*a++,*b++);
}
void ssort(void* base, size_t n, size_t sz, CMP cmp)
{
for (int i=1; i<n; i++)
for (int j=n-1; j>=i; j--)
{
char* bj=(char*)base + j*sz;
if (cmp(bj,bj-sz))
memswap(bj,bj-sz,sz);}}
struct database
{
char* name;
int age;
};
int less_int(const void* p,const void* q)
{ return *(int*)p<*(int*)q; }
int greater_int(const void* p,const void* q)
{ return *(int*)p>*(int*)q; }
int less_str(const void* p,const void* q)
{ return strcmp(*(char**)p,*(char**)q)<0; }
int less_age(const void* p,const void* q)
{ return ((database*)p)->age<((database*)q)->age; }
int less_name(const void* p,const void* q)
{ return strcmp(((database*)p)->name,
((database*)q)->name)<0; }
void print (int* mas, int n)
{
for (int i=0; i<n; i++)
cout<<mas[i]<<" ";
cout<<endl;
}
void print (char** mas, int n)
{
for (int i=0; i<n; i++)
cout<<mas[i]<<" ";
cout<<endl;}
void print (database* mas, int n)
{
for (int i=0; i<n; i++)
cout<<mas[i].name<<" "<<mas[i].age<<endl;
}
const n=10, m=5;
int mas[n]={1,5,2,6,3,7,12,-1,6,-3};
char* strmas[n]={"adg","dfgj","jk","asg","gjh",
"sdh","hj","sd","kfj","sdadgh"};
database d[m] ={{"Petrov",32},{"Ivanov",24},
{"Kozlov",21},{"Oslov",20},
{"Popov",18}};
void main()
{
ssort(mas,n,sizeof(int),less_int);
print(mas,n);
ssort(mas,n,sizeof(int),greater_int);
print(mas,n);
ssort(strmas,n,sizeof(char*),less_str);
print(strmas,n);
cout<<endl;
ssort(d,m,sizeof(database),less_age);
print(d,m);
cout<<endl;
ssort(d,m,sizeof(database),less_name);
print(d,m);
}
Реакція ЕОМ:
-3 -1 1 2 3 5 6 6 7 12
12 7 6 6 5 3 2 1 -1 -3
adg asg dfgj gjh hj jk kfj sd sdadgh sdh
Popov 18
Oslov 20
Kozlov 21
Ivanov 24
Petrov 32
Ivanov 24
Kozlov 21
Oslov 20
Petrov 32
Popov 18 [7].
2.1.3.1 Лабораторна робота
Мета роботи: зрозуміти концепцію вказівників
- вивчити застосування вказівників для передачі аргументів у виклику функції по посиланню
- зрозуміти зв'язок між вказівниками, масивами та рядками
- навчитися використати та обновляти масиви рядків
Завдання до лабораторної роботи
1. Ознайомитися з теоретичним матеріалом роботи.
2. Перевірити свою теоретичну підготовку за контрольними питаннями.
3. Скласти програму мовою С++ розв’язання завдання з використанням вказівників. До звіту включити текст програми, блок-схему алгоритму, реакцію ЕОМ.
4. Зробити висновки.
Варіанти завдань
1. Розробити програму перемножування двох матриць
та розмірності . Обидві матриці розміщаються в оперативній пам'яті динамічно, а значення вводиться по запиті із клавіатури.2. Розробити програму сортування (упорядочивания) матриці розмірності
так, щоб елементи в кожному рядку відсортованої матриці розташовувалися по зростанню та жоден елемент в -му рядку не був більше будь-якого елемента в -му рядку. Сортування виконувати над одномірним масивом з елементів, що "накладається" на вихідну матрицю.3. Розробити програму, що у матриці розмірності
міняє місцями рядок, що містить елемент із найбільшим значенням, зі стовпцем, що містить елемент із найменшим значенням. Матриця повинна розмішатися в оперативній пам'яті динамічно, а значення вводитися по запиті із клавіатури.4.Розробити програму обчислення значення багаточлена
у цілочисленній крапці . При цьому значення коефіцієнтів уводяться із клавіатури та динамічно розміщаються в пам'яті або у формі масиву, або у формі стека.