Проблемы экономической безопасности России в условиях перехода к рынку
Лабораторна робота
Вказівки, масиви і символьні рядки в мові 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];
Тоді
вираз 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'
|
Апостроф
|
'\''
|
'\"'
|
Нульовий символ
|
'\0'
|
Зворотний слеш
|
'\\'
|
Символьні
дані в С предствляють у вигляді стрингів. Стринги є одним з найбільш корисних
та важливих типів даних мови С. Символьний рядок (стринг) — це
масив символів, що закінчується у лапки (").
Він має тип char. Нульовий символ (\0)
автоматично додається останнім байтом символьного рядка та виконує роль ознаки
його кінця. Кількість елементів у масиві дорівнює кількості символів у стрингу
плюс один, оскільки нульовий символ також є елементом масива. Кожна стрингова
константа, навіть у випадку, коли вона ідентична іншій стринговій константі,
зберігається у окремому місці пам'яті. Якщо необхідно ввести у рядок символ
лапок ("), то перед ним треба поставити символ
зворотного слешу (\). У стринг можуть бути введені будь-які спеціальні
символьні константи, перед якими стоїть символ \.
Прототипи
всіх функцій, що працюють з рядками символів, містяться у файлі 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!='\0'))
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.Порядок виконання роботи
Проаналізувати
умову задачі.
Розробити
алгоритм та створити програму розв’язання задачі згідно з номером варіанту.
Результати
роботи оформити протоколом