Вказівки, масиви і символьні рядки в мові C

Лабораторна робота Вказівки, масиви і символьні рядки в мові C програма вказівка масив символ рядок 1. Мета роботи

Метою лабораторної роботи є отримання практичних навиків в роботі з вказівниками і з адресною арифметикою в мові С.

2. Теоретичні відомості

В С++ існує надзвичайно потужний інструмент для роботи зі складними агрегатами даних, який надає загальний підхід до різних на перший погляд програмних об’єктів таких як масив та рядок. Цей і нструмент базується на широкому використанні вказівника.

Вказівник – це символічне представлення адреси. Він використовується для непрямої адресації змінних і об’єктів.

В мові С++ є операція визначення адреси – &;, за допомогою якої визначається адреса комірки пам’яті, що містить задану змінну. Наприклад, якщо vr – ім’я змінної, то &;vr – адреса цієї змінної.

В С++ також існують і змінні типу вказівник. Значенням змінної типу вказівник є адреса змінної або об’єкта. Нехай змінна типу вказівник має ім’я ptr, тоді в якості значення їй можна присвоїти адресу за допомогою наступного оператора:

Ptr=&;vr;

В мові С++ при роботі з вказівниками велике значення має операція непрямої адресації – *. Операція * дозволяє звертатися до змінної не напряму, а через вказівник, який містить адресу цієї змінної. Ця операція є одномісною і має асоціативність зліва направо. Цю операцію не слід плутати з бінарною операцією множення. Нехай ptr – вказівник, тоді *ptr – це значення змінної, на яку вказує ptr.

Опис змінних типу вказівник здійснюється за допомогою операторів наступної форми:

<тип> *<ім’я вказівника на змінну заданого типу>;

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

Оператор присвоювання ( = ) виконує зворотну дію: імені змінної ставиться у відповідність значення.

A=10;

Вираз &;a дозволяє одержати адресу ділянки пам’яті, виділеного змінній а. Операція &; застосовна тільки до об’єктів, що мають ім’я і розміщених у пам’яті.

Маючи можливість визначити адреси змінної за допомогою &;, треба мати можливість працювати з цією адресою: зберігати його, передавати, перетворювати.

Для цього вводиться поняття вказівника.

Вказівник – це змінна, значенням якої служить адреса об’єкта конкретного типу. Нульова адреса позначається константою NULL, що визначена в заголовному файлі stdio. h. Щоб визначити вказівник треба повідомити на об’єкт якого типу посилається цей вказівник.

Char *z;

Int *k,*i;

Float *f;

* – це операція разіменування. Операндом цієї операції завжди є вказівник. Результат операції – це той об’єкт, що адресує вказівник_операнд.

*z=’$ ‘;

*k=*i=0;

Приклад:

Int e, c, b, *m;

. . . . . . . . .

M = &;e ;

*m = c + b ;

Операції над вказівниками.

Присвоювання (=);

Одержання значення об’єкта, на який посилається вказівник (*);

Одержання адреси самого вказівника (&;).

Приклад:

Int date = 10;

Int *i, *k;;

I = &;date;

K = i;

Z = NULL;

Подібно будь-яким змінної змінна типу вказівник має ім’я, арес у пам’яті і значення.

За допомогою унарних операцій ++ і -і числові значення змінних типу вказівник міняються по різному, у залежності від типу даних, з яким зв’язані ці змінні.

Приклад:

Char *z;

Int *k,*i;

Float *f;

. . . . . . .

Z++; // значення змінюється на 1

І++; // значення змінюється на 2

F++; // значення змінюється на 4

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

2.1 Використання вказівників при роботі з масивами

Ім’я масиву без індексу є вказівником-константою, тобто адресою першого елемента масиву (a[0]).

A

*a = = a[0] ;

*(a+1) = = a[1];

. . . . . . . . .

*(a+і) = =a[і];

Відповідно до синтаксису в С існують тільки одномірні масиви, але їх елементами, у свою чергу, теж можуть бути масиви.

Int a[5][5];

Для двовимірного масиву:

A[m][n] = = *(a[m]+n) = = *(*(a+m)+n);

Приклад1. Опис вказівників.

Int *ptri; //вказівник на змінну цілого типу

Char *ptrc; //вказівник на змінну символьного типу

Float *ptrf; //вказівник на змінну з плаваючою точкою

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

Вказівники використовуються для роботи з масивами. розглянемо оголошення двовимірного масиву:

Int mas[4][2];

Int *ptr;

Тоді вираз ptr=mas вказує на першу колонку першого рядка матриці. Записи mas і &;mаs[0][0] рівносильні. Вираз ptr+1 вказує на mas[0][1], далі йдуть елементи: mas[1][0], mas[1][1], mas[2][0] і т. д.; ptr+5 вказує на mas[2][1].

Двовимірні масиви розташовані в пам’яті так само, як і одновимірні масиви, займаючи послідовні комірки пам’яті

Ptr Ptr+1 Ptr+2 Ptr+3 Ptr+4 Ptr+5
Mas[0][0] Mas[0][1] Mas[1][0] Mas[1][1] Mas[2][0] Mas[2][1]

Розміщення двовимірного масиву в пам’яті

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

В С++ для роботи з динамічними об’єктами використовують спеціальні операції new і delete. За допомогою операції new виділяється пам’ять під динамічний об’єкт (який створюється в процесі виконання програми), а за допомогою операції delete створений об’єкт видаляється з пам’яті.

Приклад. Виділення пам’яті під динамічний масив.

Нехай розмірність динамічного масиву вводиться з клавіатури. Спочатку необхідно виділити пам’ять під цей масив, а потім створений динамічний масив треба видалити.

Int n;

Scanf(n; // n – розмірність масиву

Int *mas=new int[n]; // виділення пам’яті під масив

Delete [] mas; // звільнення пам’яті

В цьому прикладі mas є вказівником на масив з n елементів. Оператор int *mas=new int[n] виконує дві дії: оголошується змінна типу вказівник, далі вказівнику надається адреса виділеної області пам’яті у відповідності з заданим типом об’єкта.

Для цього ж прикладу можна задати наступну еквівалентну послідовність операторів:

Int n, *mas;

Scanf(n;// n – розмірність масиву

Mas=new int[n];// виділення пам’яті під масив

Delete [] mas;// звільнення пам’яті

Якщо за допомогою операції new неможливо виділити потрібний об’єм пам’яті, то результатом операції new є 0.

Іноді при програмуванні виникає необхідність створення багатовимірних динамічних об’єктів. Програмісти-початківці за аналогією з поданим способом створення одновимірних динамічних масивів для двовимірного динамічного масиву розмірності n*k запишуть наступне

Mas=new int[n][k]; // Невірно! Помилка!

Такий спосіб виділення пам’яті не дасть вірного результату. Наведемо приклад створення двовимірного масиву.

#include<iostream. h>

#include<conio. h>

Int main()

{

Int n;const m=5;

Printf(“input the number”;

Scanf(&;n);

Int** a; //a – вказівник на масив вказівників на рядки

A=new int* [n]; //виділення пам’яті для масиву вказівників на n рядків

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

A[i]=new int [m]; //виділення пам’яті для кожного рядка масиву розмірністю nxm

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

{for(int j=0;j<m;j++)

Printf(a[i][j]);

}

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

Delete [] a[i]; //звільнення пам’яті від кожного рядка

Delete [] a; //звільнення пам’яті від масиву вказівників

Getch();

Return 0;

}

2.2 Використання вказівників при роботі з рядками

Символьна константа складається з одного символа ASCII між апострофами (”). Приклади спеціальних символів:

Новий рядок ‘\n’
Горизонтальна табуляція ‘\t’
Повернення каретки ‘\r’
Апостроф ‘\”
Лапки ‘\”‘
Нульовий символ ‘{prjdata}’
Зворотний слеш ‘\’

Символьні дані в С предствляють у вигляді стрингів. Стринги є одним з найбільш корисних та важливих типів даних мови С. Символьний рядок (стринг ) – це масив символів, що закінчується у лапки (“). Він має тип char. Нульовий символ ({prjdata}) автоматично додається останнім байтом символьного рядка та виконує роль ознаки його кінця. Кількість елементів у масиві дорівнює кількості символів у стрингу плюс один, оскільки нульовий символ також є елементом масива. Кожна стрингова константа, навіть у випадку, коли вона ідентична іншій стринговій константі, зберігається у окремому місці пам’яті. Якщо необхідно ввести у рядок символ лапок (“), то перед ним треба поставити символ зворотного слешу (\). У стринг можуть бути введені будь-які спеціальні символьні константи, перед якими стоїть символ \.

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

Int strcat( char *, char *) – з’єднати два стринги;

Int strcpy(char *s1, char *s2) – копіювати рядок s2 у рядок s1;

Int strlen(char *s) – визначити довжину рядку (кількість символів без нульового символа).

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

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

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

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

Int islower(int с) – символ с є малою літерою;

Int isupper(int c) – символ с є великою літерою;

Int isalnum(int c) – символ с є буквою або цифрою;

Int isalpha(int c) – символ с є буквою;

Int tolower(int c) – перетворення літери у нижній регістр;

Int strtol(int c) – перетворення стрингу у довге ціле число;

Приклад

Обчислити вираз з дужками, десятковими цифрами та знаками +, -, *, /.

#include <ctype. h>

#include <math. h>

Isdig(char c) {return ((isdigit(c)) || ‘с’==’.’);}

Double val(char **);

Double getdig(char ** s)

{

double res;

if(**s=='(‘) res=val(++*s);

else {res=atof(*s); while(isdig(*++*s));};

return res;

}

Double multi(char** s)

{

double res=getdig(s);

while((**s==’*’) || (**s==’/’))

if(**s==’*’) res*=getdig(++*s);

else res/=getdig(++*s);

}

Double val(char** s)

{

double res=multi(s);

while((**s!=’)’) &;&; (**s!='{prjdata}’))

if(**s==’+’) res+=multi(++*s);

else res-=*(++*s);

if(**s==’)’) ++*s;

return res;

}

Double getsvalue(char* s) {char** l; *l=s;

Return val(l);}

Main()

{

char* s=”2-4/2+25/(2+3)/5+(-1)*(2*4/8-2)*(-1)+5″;

cout << getsvalue(s);

}

3.Порядок виконання роботи

Проаналізувати умову задачі.

Розробити алгоритм та створити програму розв’язання задачі згідно з номером варіанту.

Результати роботи оформити протоколом


Вказівки, масиви і символьні рядки в мові C