ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 21.12.2021

Просмотров: 979

Скачиваний: 3

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

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

Синтаксис оголошення прототипу функції наступний:


<тип> <ім’я функції> (параметр1, параметр2,…);


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

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


Приклад 7. Використання прототипу функції без вказання імен змінних у прототипі.

#include <stdio.h>


int function(int, int, int); /*оголошення прототипу функції*/


int main(void) /*головна програма*/

{

printf ("suma = %d \n", function (4, 5, 6));

return 0;

}


int function(int a, int b, int c) /*визначення функції*/

{

return a+b+c;

}


Приклад 7. Використання прототипів функцій.

#include <stdio.h>

#include <stdlib.h>


void nap(int a); /*оголошення прототипу функції nap*/

void Henap(int a); /*оголошення прототипу функції Henap*/


int main ()

{

int i;

do {

printf( " BBegiTb 4iCJlO(Buxig - 0): ");

scanf ("%i",& i);

nap(i);

} while (i!=0);

system("PAUSE");

return 0;

}


void nap (int a) /*визначення функції nap*/

{

if ((a%2)!=0)

printf( "napHe.\n ");

else Henap(a);

}


void Henap (int a) /*визначення функції Henap*/

{

if ((a%2)==0)

printf( "HenapHe.\n ");

else nap(a);

}


Результат роботи програми:


У прикладі 7.1 показано випадок у якому необхідно використовувати прототипи функцій.

На початку програми оголошуються прототипи функцій nap і Henap:


void nap (int a);

void Henap (int a);


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


Рисунок 6.5 – Cтруктура програми з прототипом функції та без нього


У програмі з прикладу 7.1 неможливо обійтись без прототипів, так як існує потреба взаємного виклику двох функцій (функція nap викликає функцію Henap, яка в свою чергу викливає функцію nap). Якщо не використовувати прототипи, то компілятор видаватиме помилку, так як функція описана в програмі першою, викликатиме функцію, яка не описувалась в програмі раніше.


8. РЯДКИ В С


Рядки є одним з найбільш корисних та важливих типів даних мови С. Символьний рядок - це масив символів, замкнений у лапки. Він має тип char. Нульовий символ ‘\0’ автоматично додається останнім байтом символьного рядка та виконує роль ознаки його кінця.


Приклад 1. Символьний рядок.

#include <string>

int main (){

char myString[] = "string!";

printf("%s\n", myString);

system("pause");

return 0;

}


Результат роботи програми:


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



4.1 Прототипи


Прототипи всіх функцій, що працюють з рядками символів, містяться у файлі string.h. Всі функції працюють з рядками, що закінчуються нульовим символом. Деякі з них дивіться у таблиці 4.1.


Таблиця 4.1 – Прототипи функцій

Ім’я функції

Виконання

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

Лексикографічне порівняння рядків. Повертає < 0 якщо перша стрічка менша за другу; > 0 якщо друга стрічка більша за першу і 0, кщо вони рівні.

int strcat( char *, char *)

З'єднати два рядки(об’єднаний рядок записується у першу змінну, а друга змінна залишається без змін);

int strcpy(char *, char *)

Копіювати рядок s2 у рядок s1(перша змінна дорівнює другій, а друга залишається незмінною);

int strlen(char *)

Визначити довжину рядку (кількість символів без нульового символа).


Приклад 2. Використання прототипів функцій.

#include <string.h>

#include <stdio.h>

#include <stdlib.h>


int main(){

char s1[]="Hello, ";

char s2[]={'w','o','r','l','d','!','\0'};

strcat(s1, s2);

printf("s1:%s s2:%s\n", s1, s2);/*на екран буде виведено:

s1:Hello, world! s2: world!*/

printf("%c\n", s1[4]);//на екран буде виведено літеру o

/*зверніть увагу, що в даному випадку використано %с

замість %s так як виводиться лише 1 символ*/

int len=strlen(s1);/*довжина першого рядка тепер дорівнює

13*/

printf("%i\n", strcmp(s1, s2));/*програма виведе від’ємне

число, так як s1<s2 за

лексикографічним порівнянням.*/

strcpy(s1, s2);

printf("%s %s %i\n", s1, s2, len); /*на екран буде виведено:

world! world! 13*/

system("pause");

}


Результат роботи програми:



4.2 Функції перетворення буферів


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

memcpy() - копіювання символів з одного буфера у другий, поки не буде скопійований заданий символ або не буде скопійовано визначену кількість символів


memcmp() - порівнює вказану кількість символів з двох буферів



4.3 Функції перевірки літер


У файлі ctype.h описано прототипи функцій, що призначені для перевірки літер. Ці функції повертають ненульове значення (істина), коли її аргумент задовольняє заданій умові або належить вказаному класу літер, та нуль в іншому випадку(табл 4.2).


Таблиця 4.2 – Функції перевірки літер

int islower(int с)

символ с є малою літерою;

int isupper(int c)

символ с є великою літерою;

int isalnum(int c)

символ с є буквою або цифрою;

int isalpha(int c)

символ с є буквою;

int tolower(int c)

перетворення літери у нижній регістр;

int strtol(int c)

перетворення рядка у довге ціле число;



4.4 Рядок символів


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

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


Приклад 3. Рядок символів.

S=”ПИРІЖОК=)”;


Рисунок 4.1 – Сприйняття комп’ютером символьного рядка.


Так сприймає рядок комп’ютер. Тому, S[0]==’П’, S[8]==’)’.



4.5 Операції з рядками:


а) Рядки можна ініціалізувати при декларуванні.


Приклад 4. Ініціалізація даних.

char S1[10] = ”123456789”, S2[] = ”Srting”, S3[] = {‘1’,‘2’,‘3’,‘\0’};


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

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


Приклад 5. Введення масиву.

char TeMa [20];

printf(“Введіть тему лабораторної роботи:\n”);

scanf(“%s”, TeMa);



4.6 Символьні константи


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


Приклад 6. Змінні типу char.

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

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

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


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


Рисунок 4.2 – Символ і рядок



9. ПОКАЖЧИКИ


5.1 Визначення та ініціалізація покажчиків


Пам'ять комп'ютера можна представити у вигляді послідовності комірок, кожна з мінімальним розміром один байт. Комірки нумеруються послідовно (рис. 5.1).

Рисунок 5.1 - Послідовна нумерація комірок пам’яті


Покажчик – це зміна, що зберігає адресу іншої змінної.


Синтаксис визначення покажчика наступний:


<тип> *<ім’я покажчика>


де тип – це тип даних на який вказує покажчик;

ім’я покажчика – ідентифікатор.


Синтаксис виклику функції наступний:



Приклад 1. Присвоєння адреси змінній(рис. 5.2).

pointer = &variable; /*якщо «&» перед ініціалізатором, то це операція взяття адреси присвоєння адреси variable змінній pointer */


тобто pointer «вказує» на variable:


Рисунок 5.2 – Присвоєння адреси змінній



Розглянемо ще один фрагмент:


Приклад 2. Змінна та її адреса(рис. 5.3).

variable = 13;

varsecond = variable;

pointer = &variable;



Рисунок 5.3 – Змінна та її адреса.


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

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


Під ініціалі затором розуміється якась адреса зміної(тоді використовуємо взяття адреси змінної &) або вже існуючий покажчик, але не вираз типу &41.



5.2 Визначення покажчиків:


Приклад 3. Синтаксис покажчика.

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

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

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


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


Приклад 4. Використання операції ”&”.

#include <iostream>

#include <stdlib.h>

int main(){

int a = 8;

int *p = &a;

printf(" p=%i, &p=%i, a=%i, &a=%i \n", p, &p, a, &a);

system("PAUSE");

return EXIT_SUCCESS;

}


Результат роботі:


Рисунок 5.4 - Схема даних в памяті:


Як бачимо в зміній а зберігається значення 8, в зміній р зберігається значення адреси а, тобто р вказує на комірку зі зміною а.


Якщо ж операція «*» використовується не при ініціалізації покажчика то ця операція має назву розіменовування:


Приклад 5. Розіменовування покажчика(рис. 5.5).

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

int *y =&x; /* покажчик на елемент даних типу int */

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


Рисунок 5.5 - Розіменовування покажчика


Z звертається до y, y це покажчик на x, що містить адресу. Присвоїти z значення y ми не можемо, тому що це різні типи даних: адреса і значення. Шляхом розіменовування ми отримуємо значення на яке вказує y і присвоюємо його змінній z.

Власне унарна операція «*» застосована до покажчика, забезпечує доступ до вмісту комірки пам'яті, на яку посилається покажчик. Наприклад, * y можна описати словами як "те, що міститься за адресою, на який вказує y".

Покажчики можуть використовуватися в виразах. Якщо. наприклад, зміна uk вказує на ціле x, то * y може у всіх випадках використовуватися замість x; так, * y +1 збільшує x на одиницю, а * y = 0 рівносильне x = 0. Два оператори присвоювання y = &x; z =*y; виконують те ж саме, що і один оператор z = x. Користь від застосування покажчиків в таких ситуаціях, м'яко висловлюючись, невелика.


Найбільш повні їх переваги при обробці масивів і, зокрема, символьних рядків. Покажчики та масиви тісно пов'язані один з одним. Перш ніж розглянути цей зв'язок детально, зазначимо, що якщо uk - деякий покажчик, то uk ++ збільшує його значення і він тепер вказує на наступний, сусідню адресу об'єкта. Значення uk використовується у виразі, а потім збільшується. Аналогічно визначаються операції uk--, + + uk,--uk. У загальному випадку покажчик uk можна складати з цілим числом i. Оператор uk + = i пересуває посилання на i елементів щодо поточного значення. Ці конструкції підпорядковуються правилам адресної арифметики.


Рисунок 5.6 - Адресна арифметика


Як бачимо є шість елементів – шість комірок, розміром по 4 байта(рис. 5.6). Для першого елемента масиву (що за одне й є покажчиком на масив) наприклад взято зміщення в памяті 1000 байт, там де нам було виділено її ОС в ОЗП. До речі , покажчик вказує й на тип, таким чином зміщення буде на відповідний розмір типу(в даному випадку для float – 4байта) і вказувати на дані що містяться в наступних 4-х байтах.

Таким чином, нехай покажчик (p) на перший елемент масиву (що за одне й є покажчиком на масив) має адресу (зміщення) 1000 і вказує на записані там дані , тобто 0,67. Використавши інкрементування р++ ми переходимо до наступного елемента в масиві (і зміщуємось в памяті на розмір типу – 4байта). Покажчик уже вказує на 2,56 з адресою 1004.

Обмежень по переміщенню в масиві немає, тому потрібно бути обережним.

Наприклад: у нас є 6 елементів в масиві, але це не означає, що покажчик не може виходити за його межі, він може вказувати на комірки з адресою 1024, 1028 і т.д. Отже, якщо ми перемістимо покажчик за межі масиву(операціями р++ чи р--), то він вказуватиме на значення типу float і що там буде за значення не відомо, так як там знаходитиметься «сміття», якийсь випадковий набір нулів та одиниць, що використовуватимуть правила переведення з двійкової системи в десяткову з плаваючою точкою, щоб відобразити зміст.

Для інших типів все ж так саме: int(зміщення 4 байти), double(зміщення 8 байти), long(зміщення 4 байти), unsigned int(зміщення 4 байти), byte(зміщення 1 байт) і т.д.. (рис. 5.7)


Рисунок 5.7 – Зміщення покажчика для різних типів даних.


5.3 Масиви


А тепер повернемося до масивів. Нехай є опис int a [5]; Він визначає масив розміром 5 елементів, тобто п'ять послідовних розташованих осередків пам'яті a [0], a [1], a [2], a [3], a [4]. Адреса i-го елемента масиву дорівнює сумі адреси початкового елемента масиву і зміщення цього елемента на i одиниць від початку масиву. Це досягається індексуванням: a [i] - i-й елемент масиву. Але доступ до будь-якого елементу масиву може бути виконаний і за допомогою покажчиків, причому, більш ефективно. Якщо uk-покажчик на ціле, описаний як int * uk, то uk після виконання операції uk = &a[0] -містить адресу a [0], а uk + i вказує на i-й елемент масиву. Таким чином, uk + i є адреса a [i]. Оскільки ім'я масиву в програмі ототожнюється з адресою його першого елемента, то вираз uk = & a [0] еквівалентно такому: uk = a. Тому значення a [i] можна записати як *(a+i). Застосувавши до цих двох елементів операцію взяття адреси, отримаємо, що &a[i] та a+i ідентичні.