Смекни!
smekni.com

Особливості мови програмування С (стр. 3 из 6)

...

}

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 Приклади

2.1.2.1 Приклад 1

Програма використовує вказівник на символьний рядок усередині функції show_string для виведення змісту рядка по одному символу за один раз:

#include <iostream.h>

void show_string(char *string)

{

while (*string != '&bsol;0')

{

cout << *string;

string++;

}

}

void main(void)

{

show_string( "Учимся программировать на языке C++!");

}

Реакція ЕОМ:

Учимся программировать на языке C++!

Умова while (*string != '&bsol;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 != '&bsol;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 Практикум

2.1.3.1 Лабораторна робота

Мета роботи: зрозуміти концепцію вказівників

- вивчити застосування вказівників для передачі аргументів у виклику функції по посиланню

- зрозуміти зв'язок між вказівниками, масивами та рядками

- навчитися використати та обновляти масиви рядків

Завдання до лабораторної роботи

1. Ознайомитися з теоретичним матеріалом роботи.

2. Перевірити свою теоретичну підготовку за контрольними питаннями.

3. Скласти програму мовою С++ розв’язання завдання з використанням вказівників. До звіту включити текст програми, блок-схему алгоритму, реакцію ЕОМ.

4. Зробити висновки.

Варіанти завдань

1. Розробити програму перемножування двох матриць

та
розмірності
. Обидві матриці розміщаються в оперативній пам'яті динамічно, а значення
вводиться по запиті із клавіатури.

2. Розробити програму сортування (упорядочивания) матриці розмірності

так, щоб елементи в кожному рядку відсортованої матриці розташовувалися по зростанню та жоден елемент в
-му рядку не був більше будь-якого елемента в
-му рядку. Сортування виконувати над одномірним масивом з
елементів, що "накладається" на вихідну матрицю.

3. Розробити програму, що у матриці розмірності

міняє місцями рядок, що містить елемент із найбільшим значенням, зі стовпцем, що містить елемент із найменшим значенням. Матриця повинна розмішатися в оперативній пам'яті динамічно, а значення
вводитися по запиті із клавіатури.

4.Розробити програму обчислення значення багаточлена

у цілочисленній крапці
. При цьому значення коефіцієнтів
уводяться із клавіатури та динамічно розміщаються в пам'яті або у формі масиву, або у формі стека.