Рефераты

Дипломная работа: Мова програмування С++


5. Вказівники та операц над ними

5.1 ПОНЯТТЯ ВКАЗІВНИКА

Кожна змінна у програмі - це об’єкт, який володіє ім’ям і значенням. Після визначення змінної з ініціалізацією всі звернення у програмі до неї за іменем замінюються компілятором на адресу іменованої област оперативної пам’яті, в якій зберігається значення змінної (Рис. 5.1). Програміст може визначити власні змінні для збереження адрес областей пам’яті. Такі змінн називають вказівниками.

int a=10;


Рис. 5.1.

Вказівник визначається наступним чином:

<тип> *< ідентифікатор> <ініціалі затор>;

Приклад 1. Визначення вказівників

int* pa=&a;// вказівник ра містить значення адреси змінної а

float *ptr (NULL); // Нульовий вказівник на об’єкт типу float

char*p; // Неініціалізований вказівник на об’єкт типу char

Значення адреси змінної одержується за допомогою унарної операції ”&”.

Для доступу до комірки пам’яті, виділеної під змінну через вказівник до останнього, слід застосувати унарну операцію розіменування ”*”.

Приклад 2. Непряма адресація через вказівник

int x=2; //змінна типу int

int *y =&x;// вказівник на елемент даних типу int

*y=1; // через вказівник до поля x вноситься значення 1,

//тобто x=1

p=new char(12);

В останньому операторі прикладу 2 неініціалізований вказівник р, описаний у прикладі 1, асоціюється з ділянкою у динамічній пам’яті під змінну типу char, до якої заноситься значення 12.

5.2 ДІЇ НАД ВКАЗІВНИКАМИ

Приклад 3: Дії над вказівниками

int a=5;

int *p=&a, *p2, *p2; p2=p1=p;

++p1; p2+=2;

cout<<“a=”<<a;

cout<<” p=”<<*p<< p=”<<p<<” p1=”<<p1<<” p2=”<<p2;

Результат виконання:

a=5, *p=5, p=FFC8, p1=FFCC, p2=FFD0.

Конкретні значення адрес залежать від низки причин: архітектури комп’ютера, типу і розміру оперативної пам’яті тощо.

З арифметичних операцій між вказівниками дозволена лише операція віднімання.

Різницею двох вказівників одного типу відстань між двома областями пам’яті, кратна довжині (в байтах) об’єкта того типу, якому відповідає вказівник. Різниця однотипних вказівників, що адресують суміжн об’єкти, за абсолютною величиною рівна одиниці. Адреси змінних позначаються цілочисельними 16-ковими константами.

Ті змінні, визначення яких розміщен в програмі поруч, займають суміжні ділянки пам’яті, проте розміщення об’єктів у пам’яті є оберненим в порівнянні з їх взаємним розташуванням у визначеннях тексту програми.

До вказівника дозволено додавати і віднімати цілочисельну константу (k). При цьому він пересувається між ділянками пам’яті на величину k*(sizeof(type)).


6. Робота з одновимірними масивами

6.1 СТАТИЧНІ ТА ДИНАМІЧНІ МАСИВИ

Масив – це кінцева послідовність даних одного типу. Кожен елемент масиву має однакове ім’я – ім’я масиву, і відрізняється ндексом (ціле число), за яким здійснюється доступ до елемента масиву. Індекси масивів у С/С++ починаються з 0. У програмі одновимірний масив оголошується наступним чином:

<тип> <ім’я масиву> [розмір] <ініціалізація>; ,

де розмір – кількість елементів одновимірного масиву.

Після визначення ім’я масиву стає вказівником-константою, значення якого незмінним і становить адресу першого (нульового) елемента масиву.

За способом розміщення масиви поділяються на статичні та динамічні. Розмір статичного масиву можна задавати константою або константним виразом. Оскільки ділянка у оперативній пам’яті під масив задається на етапі компіляції і її розмір визначається типом елементів масиву та їх кількістю, розмірність масиву повинна бути визначена у тексті програми, а не підчас її виконання. Для визначення масиву змінного розміру використовується механізм динамічного виділення пам’яті.

Наприклад:

- оголошення одновимірного масиву з п’яти елементів цілого типу:

int a[5];

- динамічне виділення пам’яті під 10 цілочисельних елементів:

int*m=new int [10];

Враховуючи те, що ім¢я масиву є вказівником, зрозумілим стає зміст останньої операції: вказівников на int m присвоюється початкова адреса ділянки пам¢яті, виділеної у динамічній області під 10 цілочисельних елементів.

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

Натомість ініціалізацію статичних масивів можна здійснювати при їх визначенні. При цьому слід зауважити, що у С/С++ не перевіряється вихід індексу за межі масиву.

Наприклад:

double d[] = {1, 2, 3, 4, 5};

float f[10]={1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.0};

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

Для звернення до елементів масивів використовуються два способи, наприклад до 4-го (по порядку) елемента масиву a можна звернутися як a[3] або *(a+3). Останнє звернення базується на факті, що ім’я масиву є одночасно вказівником на його нульовий елемент, а зміщення вказівника у пам’яті відбувається на величину індексу помножену на розмір типу елементів масиву у байтах.

Пам’ять, зарезервовану під динамічний масив за допомогою new[ ], потрібно звільняти оператором delete [] <ім¢я массиву>.

Отже, якщо кількість елементів масиву відома до виконання програми, краще використовувати статичний масив, розмірність якого позначити як значення іменованої константи. Якщо розмірність масиву необхідно задавати в процесі виконання програми (до введення його елементів), то доцільно створювати динамічний масив.

6.2 РЯДКИ, ЯК ОДНОВИМІРНІ МАСИВИ СИМВОЛІВ

У мовах С/С++ немає окремого типу даних “рядок символів”, подібно до типу string у алгоритмічній мові PASCAL. Тому робота з рядками реалізована шляхом використання одновимірних масивів типу char. Рядок символів – це одновимірний масив типу char, останнім елементом якого є нульовий байт. Нульовий байт – це байт, кожен біт якого рівний нулю, при цьому для нульового байта визначена символьная константа ¢\0¢ (ознака закінчення рядка або нуль-термінатор). Тому, якщо рядок містить k символів, в описі масиву потрібно вказати розмірність k+1.

Так, для збереження у масиві рядково константи “Лабораторна робота з рядками”, необхідно описати масив char s[29]. В кінці рядкової константи символ ´ \0¢ вказувати не потрібно, оскільки це зробить компілятор мови С.

Рядки можна

а) ініціалізувати при декларуванні.

Наприклад:

charS1[10]=”123456789”,S2[]=”abcdefg”,S3[]={‘1’,‘2’,‘3’,‘\0’};,

де в двох останніх випадках розмір рядків буде встановлений за кількістю символів;

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

Наприклад:

char Tоріс[20];

cout<<”Введіть тему лабораторно роботи:\n”; cin>>Tоріс;


7. Двовимірні масиви

Крім одновимірних масивів у С/С++ можливо працювати з багатовимірними масивами, а найчастіше з двовимірними матрицями. Двовимірний масив – це масив, що складається з окремих одновимірних масивів у вигляді рядків, розмірність яких рівна кількості стовпців матриці. Для цього при визначенні двовимірного масиву у квадратних дужках вказується дві розмірності. Оператор опису двовимірного масиву має вигляд:

<тип> <ім’я> [<розмір1>][<розмір2>];

Наприклад:

int a[3][5]; // Цілочисельна матриця з 3 рядків і 5 стовпців

Масив зберігається у неперервній област пам’яті по рядках, які зберігаються послідовно один за одним, а не у вигляді звично з матики матриці:

a00 a01 a02 a03 a04 a10 a11 a12 a13 a14 a20 a21 a22 a23 a24

| -0-ий рядок- - |-- 1-ий рядок - |- - 2-ий рядок- - |

Для доступу до окремого елемента двовимірного масиву використовується конструкція, яка має вигляд a[i][j], де i – номер рядка, j – номер стовпчика. Кожен індекс може змінюватися від 0 до значення, яке на одиницю менше за значення відповідно розмірності.

Як і для одновимірного масиву, при опис двовимірного масиву можна задавати початкові значення його елементів. Їх записують у фігурних дужках. Елементи масиву ініціалізуються в порядку їх розміщення у пам’яті.

Наприклад: оператор

int с[3][4] = {1,2,34,2,3,4,1,3,4,1,2};

визначає матрицю з наступними значеннями елементів: 1 2 3 4

2 3 4 1

3 4 1 2

Можна задавати початкові значення не для всіх елементів масиву. Для цього список значень для кожного рядка береться додатково у фігурні дужки. Наприклад:

int d[3][4] = {{0,1,2},{9,-2},{-7,1,6,8}};

В даному випадку розмірність, що познача кількість рядків, можна не вказувати.

Для створення динамічного двовимірного масиву необхідно вказати в операції new всі його розмірності, причому ліва розмірність (кількість рядків) може бути змінною:

int nriad=5;

int **m=(int**) new int[nriad][10];

Більш ефективний і безпечний спосіб виділення пам’яті під двовимірний масив, коли обидві його розмірності вказуються на етап виконання програми, тобто є змінними, наведено нижче:

int nriad, nstp;

cout <<”Введіть кількість рядків та стовпців:”;

cin >> nriad >> nstp;

int**a = new int *[nriad];// 1

for (int i=0; i < nriad; i++)// 2

a[i] = new int[nstp];// 3

В операторі 1 оголошується змінна типу вказівник на вказівник на int” і виділяється пам’ять під масив вказівників на рядки масиву (nriad – кількість рядків). В операторі 2 організовано цикл для виділення пам’яті під кожен рядок масиву. В операторі 3 кожному елементу масиву вказівників на рядки присвоюється адреса початку ділянки пам’яті, виділеної під рядок двовимірного масиву з кількістю елементів типу int у ній, рівною nstp


8. Символьна інформація та рядки

8.1 ЗБЕРЕЖЕННЯ СИМВОЛЬНОЇ ІНФОРМАЦІЇ

Для символьних даних в C/С++ введено тип char. Для представлення символьної інформації використовуються символи, символьн змінні і текстові константи. Приклади:

const char c=’c’; //символ-константа займає один байт

char a,b; // символьні змінні, займають по одному байту

const char *s=“Приклад рядка\n” ; // рядкова константа

Рядок в С++ - це масив символів, що закінчується нуль-символом ‘\0’. За місцезнаходженням цього символу визначається фактична довжина рядка. Кількість елементів у такому масиві на 1 більше за зображення рядка (рис. 8.1).

A \0 A

“A”

рядок (2 байти)

‘A’

символ (1байт)

Рис. 8.1.

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

Приклади:

char s1[10]="string1"; // масив символів з десяти елементів

char s3[]={‘s’,’t’,’a’,‘r’,’t’,’\0’}; // масив з 6 елементів типу char

сhar *s4="string4"; // вказівник-змінна на рядок

char *s=”String5”; // виділяється 8 байтів для рядка

char *sss=new char[10]; /* виділяється динамічна пам’ять

під 10 елементів типу char*/

strcpy(sss,”Thanks”);// у цю область пам’яті копіюється рядок.


8.2 ФУНКЦІЇ ВВОДУ/ВИВОДУ ПРИ РОБОТІ З РЯДКАМИ

Для вводу і виводу символьних даних у бібліотеці мови С (файл <stdio.h>) визначені наступні функції:

int getchar() - здійснює введення одного символу з вхідного потоку і повертає один байт нформації (символ) у вигляді значення типу int. Це робиться для розпізнавання ситуації, коли при зчитуванні буде досягнуто кінець файлу.

int putchar (int c) – розміщує в стандартний вихідний потік символ c.

Приклад:

# include <stdio.h>

void main()

{char c, d;

c=getchar(); putchar(c);

d=getchar(); putchar(d);

}

char* gets(char*s) – зчитує рядок s із стандартного потоку до появи символу ‘\n’, сам символ \n’ у рядок не заноситься. Повертає вказівник на цей рядок.

int puts(const char* s) – записує рядок у стандартний потік виводу, додаючи в кінці рядка символ ‘\n’, у випадку вдалого завершення повертає значення більше або рівне 0 і від’ємне значення у випадку помилки.

Також для вводу/виводу рядка можна використовувати функції scanf/printf, відповідно, задавши специфікатор формату %s:

# include <stdio.h>

void main(){

const int n=10;

char s[n];

scanf(“%s”, s); printf(“%s”, s);}

Ім’я масиву є вказівником-константою на початок рядка, тому не слід застосовувати операцію взяття адреси (&), що зазвичай використовується з функцією вводу scanf. Ввід буде здійснюватися до першого символу пропуску. Для того щоб ввести рядок, який складається з декількох слів, використовується специфікатор %c (символи) із зазначенням у ньому максимально кількості символів, що вводяться, наприклад:

scanf(“%10c”, s);

При виводі рядка на екран можна в специфікац %s зазначити кількість символів, які відводяться під рядок:

printf(“%15s”, s);

Функції вводу/виводу мови С++, описан у заголовному файлі <iostream.h>:

cin>>s; //ввід рядка зі стандартного потоку до появи першого пропуску.

При введенні, наприклад, рядка “Ваня Іванов” в рядок s буде записано лише перше слово рядка, а саме “Ваня\0”.

cout<<s; //вивід рядка в стандартний потік до першого пропуску.

Якщо потрібно ввести рядок, який складається з декількох слів, в одну рядкову змінну, використовують методи getline або get класу istream, об’єктом якого є cin. Виклик цього методу здійснюється наступним чином: після імені об’єкту cin ставиться крапка, за якою записується м’я методу:

#include<iostream.h>

int main()

{const in n=80;

char s[n];

cin.getline(s, n); cout<<s<<endl;

cin.get(s, n); cout<< s}

Метод getline зчитує з вхідного потоку n-1 символів або менше (якщо символ переводу рядка зустрінеться раніше) записує їх у рядкову змінну s. Символ переводу рядка також зчитується (видаляється) з вхідного потоку, але не записується у рядкову змінну, замість нього розміщується завершальний ’\0’. Якщо в рядку вихідних даних більше за n-1 символів, наступне введення буде виконуватися з того ж рядка, починаючи з першого символу, що не був зчитаний.

Метод get працює аналогічно, але залишає в потоці символ переводу рядка. До рядкової змінної додається завершальний \0’.

8.3 СПЕЦІАЛЬНІ ФУНКЦІЇ ДЛЯ РОБОТИ З РЯДКАМИ ТА СИМВОЛАМИ

Для рядків не визначено операцій присвоювання, додавання, порівняння, оскільки рядок не є основним типом даних. Для роботи з рядками використовуються спеціальні бібліотечні функції, опис яких міститься у файлі <string.h>. Деякі з цих функцій наведено у таблиці 8.1:

Таблиця 8.1 Функції стандартної бібліотеки для роботи з рядками – файл <string.h>

Прототип функції Короткий опис та використання Пояснення

unsigned strlen(const char*s);

Обчислює довжину рядка s.

strlen(s);

Повертає фактичну довжину рядка, не враховуючи нуль-символ

int strcmp(const char*s1, const char *s2);

Порівнює рядки s1 і s2.

strcmp(s1, s2);

Якщо s1<s2, тоді результат від’ємний, якщо s1= =s2, тоді результат рівний 0, якщо s2>s1 результат додатний.

int strncmp(const char*s1, const char *s2, n);

Порівнює перш n символів рядків s1 і s2.

strncmp( s1, s2, n);

Якщо n(s1)<n(s2), тоді результат від’ємний, якщо s1= =s2, тоді результат рівний 0, якщо s2>s1 результат додатний.

char*strcpy(char*s1, const char*s2);

Копіює символи рядка s2 у рядок s1. strcpy(s1, s2); Нуль-символ при цьому теж включається

char*strncpy(char*s1, const char*s2, int n);

Копіює n символів рядка s2 у рядок s1.

strncpy(s1,s2,n);

Кінець рядка відкидається. Якщо нуль-символ у вихідному рядку зустрінеться раніше, копіювання припиняється, а решта символів рядка доповнюються ‘\0’-ми.

char*strcat(char*s1, const char*s2);

Дописує рядок s2 до рядка s1.

strcat(s1, s2);

Перший символ s2 записується на місце нуль-символу рядка s1. До результуючого s1 додається ‘\0’.

char*strncat(char*s1, const char*s2, size_t n);

Дописуються перш n символів рядка s2 до рядка s1.

strncat(s1,s2,5);

n-символів рядка s2 записується до s1, починаючи з місця нуль-символу s1.

char* strсhr(char*s, int ch)

Шукає символ ch у рядку s.

strchr(s, ch);

Повертає вказівник на перше входження символу в рядок справа. Якщо його немає – повертається NULL

char* strrev(char *s1)

Змінює порядок символів у рядку s1 на протилежний.

strrev(s1, s2);

Дзеркальне відображення рядка s1.

char *strstr(char*s1, char*s2)

Шукає підрядок у рядку.

strstr(s1, s2);

Пошук першого входження s2 у s1. В разі вдалого пошуку повертається вказівник на елемент з s1, з якого починається s2, інакше – NULL.

char *strtok(char*s1, char*s2)

Розбиває рядок на лексеми.

strtok(s1, s2);

Функція поверта вказівник на лексему в s1, відокремлену символом з набору s2 (пробілами або розділовими знаками).

Для роботи з символами у файлі <ctype.h> стандартної бібліотеки визначено функції, наведені в таблиці 8.2:

Таблиця 8.2 Функції стандартної бібліотеки для роботи з символами-файл <ctype.h>

Прототип функції Короткий опис та використання Пояснення

int isalnum(int ch)

Перевіряє чи символ ch буквою або цифрою (A-Z, a-z, 0-9).

isalnum(ch);

Повертається true, якщо ch є буквою або цифрою, інакше false

int isalpha(int ch)

Перевіряє чи символ ch буквою (A-Z, a-z). isalpha(ch); Повертається true, якщо ch є буквою, інакше false

int isspace(int ch)

Перевіряє чи символ ch пропуском (пробіл, табуляція, символ нового рядка, нової сторінки). isspace(ch); Повертається true, якщо ch є узагальненим пробілом, інакше false

int isdigit(int ch)

Перевіряє чи символ цифрою (0-9).

isdigit(ch);

Повертається true, якщо ch є цифрою, інакше false

int islower(int ch)

Перевіряє чи символ буквою нижнього регістру (a-z).

islower(ch);

Повертається true, якщо ch є буквою нижнього регістру, інакше false

int isupper(int ch)

Перевіряє чи символ буквою верхнього регістру (A-Z).

isupper(ch);

Повертається true, якщо ch є буквою верхнього регістру, інакше false

int ispunct(int ch)

Перевіряє чи символ символом пунктуації (. , : ; ? ! тощо).

ispunct(ch);

Повертається true, якщо ch є символом пунктуації, інакше false

int tolower (int ch)

Повертає символ у нижньому регістрі.

tolower (int ch);

Одержує символ ch і повертає його у нижньому регістрі

int toupper(int ch)

Повертає символ у верхньому регістрі.

toupper(int ch);

Одержує символ ch і повертає його у верхньому регістрі

Приклад 1:

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

#include <iostream.h>

#include <string.h>

#include <ctype.h>

void main()

{const int n=250;// розмірність рядкового масиву

char s[n], // вихідний рядок

w[25], // проміжний масив для збереження слова з рядка

*mas[10];// масив вказівників для збереження слів з рядка

cout<<“\nBведiть рядок:\n”;

cin.getline(s, n);

int k=0, t=0, i, len, j;

len=strlen(s);

while(t<len)

{ for(j=0,i=t; isspace(s[i])==0; i++,j++)

w[j]=s[i]; // виокремлюємо слово до пробілу

w[j]=’\0’;// формуємо кінець слова

strcpy(mas[k],w);// копіюємо слово у масив

k++;// збільшуємо лічильник слів у рядку

t=i+1;// перехід через пробіл до наступного слова у

// вихідному рядку s

}

strcpy(s,” ”);// очищуємо вихідний рядок

for(t=0; t<k; t++)// заповнюємо рядок

if(isalpha(mas[t][0])!=0){// якщо перший символ не цифра

{strcat(s,mas[t]);// дописуємо слово в оновлений рядок

strcat(s,” “);// додаємо пробіл після слова

}

cout<<”\nНовий рядок:\n”<< s;// виводимо результат

}

Приклад 2:

Програма яка підраховує скільки разів задане слово зустрічається у тексті файлу. Наприклад, у англійській поговірці “Don’t trouble trouble until trouble troubles you” слово “trouble” у чистому вигляді зустрічається 3 рази.

#include <fstream.h>

#include <string.h>

# include<ctype.h>

void main()

{const int len=81;

char word[len], line [len];// масиви для слова і рядка

cout<< “Введiть слово для пошуку:”; cin>> word;

int_lword=strlen(word);// визначення довжини слова

ifstream fin (“text.txt”, ios:: in | ios:: nocreate);

if(!fin) {cout<< “Помилка відкриття файлу.”<<endl;return 1;}

int count=0;

// поки не досягнуто нуль-символу

while(fin.getline(line, len))

{char *p=line;// вказівникові присвоєно адресу рядка

while(p=strstr(p,word))/* якщо слово знайдено

вказівник стає на позицію

початку слова у рядку*/

isspace(*p)

}

cout << “Слово зустрічається в тексті ”<< count;

<<” разів”<<endl;

}

 


9. Функції користувача

9.1 ФУНКЦІЇ: ВИЗНАЧЕННЯ, ОПИС, ВИКЛИК

Функцію в С++ можна розглядати:

·   як один з похідних типів даних (поряд з масивами і вказівниками);

·   як мінімальний виконуваний модуль програми (підпрограму).

Всі функції мають однаковий формат визначення:

<тип><ім’я_функції>(<список_формальних_параметрів>)

<тіло_функції>,

де <тип> - тип результату, який повертається функцією; в разі, якщо функція не повертає ніякого значення, специфікують типом void і називають “порожньою”. Найчастіше, це функції, які виводять на екран повідомлення чи виконують деякі зміни параметрів, проте не можуть передати певний результат іншим змінним при виклику.

<ім’я_функції> - або main для головної функції, або довільний ідентифікатор;

<список_формальних_параметрів> - або порожній ( ), або список, кожен елемент якого записується як:

<тип> <ім’я_формального_параметру>

Наприклад:

(int k )

(char i, char j, int z)

<тіло_функції> - це набір операторів, що виконуються у фігурних дужках {} при виклику функції. Тіло функції може бути складовим оператором або блоком. Визначення функцій не можуть бути вкладеними.

Для передачі результату з функції у функцію, що її викликала, використовується оператор return. Його можна використовувати у двох формах:

1)         return 0; - завершує функцію, яка не повертає ніякого значення (тобто перед її іменем вказано тип void);

2)         return <вираз>; - повертає значення виразу, тип виразу повинен співпадати з типом функції.

Приклад 1:

int op (char c, int x, int y)

{switch (c)

{case ‘+’ : return x+y;

case ‘-’ : return x-y;

case ‘*’ : return x*y;

case ‘/’ : return x/y;

default: cout<<“\nОперація не визначена!”;

return 0;

}

}

Приклад 2.

float cube(float d)

{return d*d*d;}

Після визначення функцію можна багаторазово використовувати у програмі для виконання однотипних дій над різними змінними.

Виклик функції має наступний вигляд:

<ім’я_функції>(<список фактичних параметрів>);

<список фактичних параметрів - або сигнатура, є переліком виразів, кількість яких дорівнює кількост формальних параметрів функції. Між формальними і фактичними параметрами повинна бути відповідність за типами. В якості фактичних параметрів можна використовувати змінні, визначені та ініціалізовані у програмі, з типами, що відповідають типам формальних параметрів. Якщо функція повертає значення, її виклик можна використати у правій частині операції присвоювання з метою передачі результату функції змінній, тип якої співпадає з типом функції, що викликається.

Наприклад:,

void main(){float s, f=0.55; s=cube(f);…}

В якості фактичних параметрів також можуть виступати явно задані константні значення:

Наприклад, виклик функції з прикладу 1 має наступний вигляд:

c = op ( ‘+’, 5 ,4 );

Оскільки визначення функцій не можуть міститися всередині блоків та складових операторів, тобто в інших функціях, у програм вони можуть розміщуватися як до, так і після функції, яка їх викликає. В останньому випадку перед використанням функції у програмі необхідно розмістити її опис, або прототип, інакше виникатимуть проблеми. Компілятор передусім послідовно перевіря коректність виклику та використання об¢єктів у програмі, при виявленні функції, яка не була описана чи визначена раніше, видасть повідомлення про помилку і вказівку про те, що функція повинна містити прототип. Те саме повідомлення Ви побачите на екрані, якщо використаєте у програмі функцію зі стандартних бібліотек і не під¢єднаєте заголовний файл, у якому вона описана. Прототип функції користувача багато в чому нагадує заголовок функції і має наступний формат:

<тип><ім’я_функції>(<список_формальних_параметрів>);

Головною відмінністю є наявність в кінц опису крапки з комою.

Так, прототипи функцій з прикладів 1 та 2 матимуть вигляд:

cube(float);

op(char c, int, int);

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

9.2 ПЕРЕДАЧА МАСИВІВ У ФУНКЦІЮ

Якщо в якості параметру функції використовується позначення масиву, необхідно передати до функції його розмірність.

Приклад 3: Обчислення суми елементів масиву

int sum (int n, int a[] )

{ int i, s=0;

for( i=0; i<n; i++ )

s+=a[i];

return s;

}

void main()

{ int a[]={ 3, 5, 7, 9, 11, 13, 15 };

int s = sum( 7, a );

cout<<s;

}

Рядки в якості фактичних параметрів можуть визначатися або як одновимірні масиви типу char[], або як вказівники типу char*. На відміну від звичайних масивів, для рядків немає необхідності явно вказувати довжину рядка, оскільки будь-який рядок обмежується нуль-символом.

При передачі у функцію двовимірного масиву в якості параметру так само необхідно задавати кінцеві розміри масиву у заголовку функції. Робити це можна:

а) явним чином (а[3][4] тоді функція працюватиме з масивами лише заданої розмірності);

б) можна спробувати для квадратної матриц через додатковий параметр ввести розмірність (void matrix(double x[][], int n)), де n – порядок квадратної матриці, а double x[][] – спроба визначення двовимірного масиву зі заздалегідь невизначеними розмірами. В результаті на таку спробу компілятор відповість: Error…: Size of type is unknown or zero;

в) найзручнішим вважається спосіб представлення та передачі двовимірної матриці за допомогою допоміжних масивів вказівників на одновимірн масиви, якими в даному випадку виступають рядки двовимірного масиву. Всі дії виконуються в межах рядків, розмір яких передається у функцію за допомогою додаткового формального параметру або з використанням глобальних змінних.

Приклад 4.

# include<iostream.h>

//Функція транспонування квадратної матриці

void trans (int n, double*p[])

{double x;

for (int i=0; i<n-1; i++)

for(int j=i+1; j<n; j++)

{x=p[i][j];

p[i][j]=p[j][i];

p[j][i]=x;

}

}

void main(){

// Задано масив для транспонування

double A[4][4]={11, 12, 13, 14

21, 22, 23, 24

31, 32, 33, 34

41, 42, 43, 44};

// Допоміжний двовимірний масив вказівників

double * ptr[]={(double*)&A[0], (double*)&A[1],

(double*)&A[2], (double*)&A[3]};

// Виклик функції

int n=4;

trans(n, ptr);

for(int i=0; i<n;i++)

{cout<<”\n Рядок ”<<(i+1)<<”:”;

for(int j=0; j<n; j++)

cout<<”\t”<< A[i][j];

}

}

 

9.3 ПЕРЕВАНТАЖЕННЯ ФУНКЦІЙ У С++

Перевантаження полягає в тому, що функція з одним іменем по різному виконується і повертає значення різних типів при передач до неї фактичних параметрів у різній кількості та з різними типами. Для забезпечення перевантаження функцій необхідно для одного імені функції визначити заголовок набір операторів для всіх функцій, що пов’язані з ним.

Приклад 5:

#include <iostream.h>

int max_element ( int n, int a[])

// знаходить максимальний елемент для масиву типу int

{

int max=a[0];

for ( i=1; i<n; i++)

if (a[i]>max) max=a[i];

return max;

}

long max_element ( int n, long a[])

// знаходить максимальний елемент для масиву типу long

{

long max=a[0];

for ( i=1; i<n; i++)

if (a[i]>max) max=a[i];

return max;

}

double max_element ( int n, double a[])

// знаходить максимальний елемент для масиву типу double

{

double max=a[0];

for ( i=1; i<n; i++)

if (a[i]>max) max=a[i];

return max;

}

float max_element ( int n, float a[])

// знаходить максимальний елемент для масиву типу float

{

float max=a[0];

for ( i=1; i<n; i++)

if (a[i]>max) max=a[i];

return max;

}

void main ( )

{

int x[]={10, 20, 30, 40, 50, 60};

long y[]={12L, 44L, 22L, 37L,30L};

int m1=max_element(6, x );

long m2=max_element(5, y);

}

9.4 ФУНКЦІЇ ЗІ ЗМІННОЮ КІЛЬКІСТЮ ПАРАМЕТРІВ

У мовах С та С++ допускаються функції, кількість параметрів у яких при компіляц не визначена. Крім того, можуть бути невідомими і типи параметрів. Кількість і типи параметрів стають відомими лише в момент виклику функції, коли явно задається список фактичних параметрів. Формат заголовку функції зі змінним переліком параметрів ма вигляд:

<тип> <ім’я> (<специфікація_формальних_параметрів, >)

Проте, кожна функція зі змінним переліком параметрів повинна мати механізм визначення їх кількості або типу. Для цього використовують два основні підходи:

1) Передача у функцію значення реально кількості фактичних параметрів за допомогою одного або декількох обов’язкових параметрів;

2) Додавання до списку фактичних параметрів спеціального параметру-індикатора з унікальним значенням, яке буде сигналізувати про кінець списку.

Підхід (1) демонструє програма у приклад 6, яка містить функцію зі змінним списком параметрів, перший з яких визначає кількість фактичних параметрів, що використовуються при викликанні функції:

Приклад 6:

# include <iostream.h>

// Функція сумує значення параметрів типу int

long summa (int k,…)// k – кількість параметрів, що додаються

{int *pik=&k;// вказівник, що звертається до параметрів функції

long total = 0;

for (;k;k--)// поки не вичерпано список параметрів

total+=*(++pik);// відбувається додавання

return total;// повернення результату

}

void main()

{// додаватимуться два параметри

cout<<”\n summa(2, 6, 4)=”<< summa(2,6,4);

// кількість параметрів, які потрібно додати - 6

cout<<”\n summa(6, 1, 2,3,4,5,6)= << summa(6, 1,2,3,4,5,6);

}

Результат виконання:

summa(2,6,4)=10

summa(6,1,2,3,4,5,6)=21

Наступний приклад 7 ілюструє 2-й підхід до обмеження змінного списку параметрів. Програма містить функцію для визначення добутку змінної кількості параметрів. Ознакою кінця списку фактичних параметрів служить параметр з нульовим значенням.

Приклад 7:

# include <iostream.h>

double prod (doble arg, … )

{double aa=1.0;// початкове значення добутку

double *prt=&arg;// вказівник на перший елемент у списку

if (*ptr==0) return 0.0;// чи є перший елемент нулем?

for (; *ptr; ptr++) aa*=*ptr;// знаходження добутку

return aa;

}

void main()

{cout<< “\n prod(2.0, 4.0, 3.0, 0.0)=”<< prod (2.0, 4.0, 3.0, 0.0);

cout<<”“\n prod(1.4, 3.0, 0.0, 16.0)=”<< prod (1.4, 3.0, 0.0, 16.0);

cout<<“\n prod(0.0)=”<< prod (0.0);

}

Результат виконання:

рrod (2.0, 4.0, 3.0, 0.0)=24

рrod (1.4, 3.0, 0.0, 16.0)=4.2

рrod (0.0)=0.0


10. Структури

Структура об’єднує логічно зв’язан дані різних типів. При описі структури програміст створює новий тип, на основі базових або інших складових типів, за допомогою якого можна в подальшому описувати реальн об’єкти програми, які зберігатимуться у пам’яті. Склад цих об’єктів визначатиметься типом структури, за допомогою якої вони описуються. Структурний тип даних визначається описом:

struct <ім’я структури> {

<опис елементів>

};

Для виділення пам’яті під структуру необхідно визначити структурну змінну:

<ім’я структури> <ім’я змінної>;

Приклад 1.

struct lab{

int num;

char* name;

};// визначення структурного типу з іменем lab

lab Lаb_10;// опис конкретної структуриз менем Lab_10.

Можна одночасно визначати структурний тип і описувати за допомогою нього структуру:

Приклад 2.

struct gr // ім’я структурного типу

{ char [10];// елемент структури

int year, nomer;// однотипні елементи структури

} grupa1; // ім’я структурної змінної

Елементи структури називають полями (num, name). Поля можуть бути будь-якого базового чи похідного типу, наприклад, масивом, вказівником, об’єднанням або іншою структурою.

Для звернення до полів структури використовуються уточнені імена через операцію вибору: “крапка” (“.”) при зверненні через ім’я структури операцію непрямого доступу “->” при зверненні через вказівник.

Приклад 3.

Lab_10.num=10;

lab*ptrlab=&Lab_10;

ptrlab->name=”Структури”;

Ввід/вивід структур виконується поелементно (cin>>Lab_10.num;).

Структури одного типу можна копіювати.

Структури, пам’ять під які виділяється на етапі компіляції, можна ініціалізувати, перераховуючи значення їх елементів:

lab Lab10={10, “Структури”}.

Можна створювати масиви структур.

Приклад 4.

// структура для опису дати

struct date { int day,month,year;};

/* масив з 5-ти структур типу date , кожна з яких складається з 3-х елементів типу int, яким надаються початкові значення*/

date d[5]={ { 1,3,1980}, { 5,1,1990}, {1,1,2002}};

Приклад 5.

Програма, що демонструє використання структур для ведення обліку успішності студентів деякої академічної групи:

#include <conio.h>

#include <iostream.h>

struct Spysok {

char PIB[20];// масив для зберігання прізвищ студентів

char Grup[10];// номер групи

int Ot[3];// масив з трьох оцінок

float S_Bal;// середній бал

} *vid;// вказівник, на структуру

void Vvid(int nom, Spysok *vid)

{ cout << "\n Vvedit vidomosti \n" << (nom+1);

cout << "\n PIB - "; cin>>vid->PIB;

cout << " Nomer Gr - "; cin >> vid->Grup;

float s=0;

for(int i=0;i<3;i++) {

cout << "\n Otsinki - "; cin >> vid->Ot[i];

s+=vid->Ot[i];

}

vid->S_Bal=s/3;

}

void main()

{ struct Spysok Stud[50]; int i, N; char Litera;

clrscr();

cout << "\n Vvedit kilkist studentiv u grupi < 50 "; cin >> N;

for(i=0;i<N;i++) Vvid(i,&Stud[i]);

cout << "\n Spysok studentiv”;

for(i=0;i<N;i++)

cout<<"\n"<<Stud[i].PIB<<endl<<Stud[i].Grup<<endl<<Stud[i].S_Bal;

cout << "\n Poshuk vidomostey pro studentiv za pershoyu\ literoyu prizvyscha\n";

cin >> Litera;

if (islower(Litera)) toupper(Litera);

cout << "\n Vidomosti pro Students:";

int kod_p=0;

for(i=0;i<N;i++)

if(Stud[i].PIB[0]==Litera)

{ kod_p=1;

cout<<”\n"<<Stud[i].PIB

<<endl<<Stud[i].Grup<<endl<<Stud[i].S_Bal;

}

if(kod_p==0) cout << " Takyx nemae!";

getch();

}


Список використаної літератури

1.         Подбельский В.В. Язык СИ++: Уч. Пособие. –5-е издание. М.: Финансы и статистика, 2001. –560 с.

2.         Подбельский В. В., Фомин С. С. Программирование на языке Си: Учеб. пособие. –М.:Финансы и статистика, 1998. –600с.

3.         Павловская Т.А. С/С++. Программирование на языке высокого уровня: Учебник для ВУЗов. –СПб.:Питер, 2003. –461 с.

4.         Павловская Т.А., Щупак Ю.А. С/С++. Практикум. –СПб.: Питер, 2002. –204 с.

5.         Дейтел Х., Дейтел П. Как программировать на С++: Пер. с англ. –М.: Бином, 2000. –1024 с.

6.         Прата С. Язык программирования С++. Лекции и упражнения. Учебник: Пер. с англ. –СПб.: ДиаСофтЮП, 2003. –1104 с.

7.         Либерти Дж. Освой самостоятельно С++. –М.: Вильямс, 2001. –456 с.

8.         Культин Н. С/С++ в задачах и примерах. –СПб.:БХВ-Петербург, 2001. –288 с.


Страницы: 1, 2


© 2010 Рефераты