• Название:

    Lab 8 11


  • Размер: 0.14 Мб
  • Формат: RTF
  • или
  • Сообщить о нарушении / Abuse

    Осталось ждать: 10 сек.

Установите безопасный браузер



Предпросмотр документа

Лабораторна робота № 8

Тема роботи: Дослідження використання масивів і вказівників у мові програмування С.

Мета роботи: Дослідження способів програмування на мові C з використанням масивів і змінних типу вказівник та взаємозв’язку між ними.

Попередні відомості

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

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

[] [];

[] [];

[] [][]...; - для багатомірних масивів.

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

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

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

Для доступу до окремого елементу масиву використовується індексний вираз, який обчислюється шляхом додавання до адреси масиву цілого числа і отримання значення за новою адресою. Наприклад, індексний вираз line[0] є значенням першого елементу масиву, а line[5] отримує шостий елемент масиву. Для багатовимірних масивів індексний вираз обчислюється зліва направо. Багатовимірний масив інтерпретується мовою С як масив, елементами якого є масиви. Наприклад, елементами трьохмірного масиву є двомірні масиви, і декларація:

int prop[3][4][6];

означає, що масив з іменем prop містить 3 елементи, кожен з яких є двомірним масивом.

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

int masyv1 [] = {{0,1,2,3,4,5}};

тут розмір масиву не вказаний, але компілятор сам визначить його за кількістю ініційованих елементів;

float masyv2 [6] = {{1.,1.,1.,2.,2.,2.}};

тут вказаний розмір масиву 6 і компілятор при ініціалізації перевірить, чи кількість ініційованих елементів не перевищує вказану розмірність i якщо перевищує, видасть повідомлення про помилку.

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

float masyv3 [3][2] = {{{{100,200}}, {{300,400}}, {{500,600}}}};

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

Int masyv4 []4[6]={{{{100}}, {{200}}, {{300}}, {{400}}}};

Важливим прикладом масиву в мові С є рядок string, який представляється одновимірним масивом типу char, тобто послідовністю символів, наприклад:

char masyv4 [] = “How are You?”;

тут компілятор визначає кількість потрібних комірок пам’яті автоматично і виділяє 13 байтів - 12 під видимі символи і тринадцятий під невидимий символ кінця рядка ('\\0').

Для символьного масиву завжди слід виділяти на одну комірку пам’яті більше, ніж довжина рядка.

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

[< тип >] *

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

Змінна, оголошена як вказівник, зберігає адресу пам’яті. Розмір пам’яті, що необхідний для зберігання адреси, та формат цієї адреси залежить від типу комп’ютера і від реалізації компілятора, а також від обраної моделі пам’яті. Модифікатори near, far, huge також специфікують формат вказівника. Приклад:

int *ptr;

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

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

За виглядом оголошення вказівника символом * перед іменем змінної подібна на оператор визначення величини *, яка зберігається в комірці, на адресу якої вказує вказівник. Існує також оператор &, який повертає адресу операнда.

Приклад:

int *ptr;/* змінна ptr - вказівник на int*/

int it = 33;

ptr = ⁢/* змінн·й ptr присвоюється адреса it */

*ptr = *ptr + 1; /*змінній it присвоюється 34*/

Ім’я масиву є вказівником на перший елемент масиву, тобто містить його адресу. Це ім’я є константою і над ним не дозволені арифметичні операції (для того, щоб не можна було змінювати в пам’яті позицію розміщення початку масиву). Наприклад, якщо оголошено масив m3[], то вираз ++m3 , буде некоректним через спробу змінити значення m3. Для доступу до решти елементів масиву використовуються індексні вирази, (наприклад, m3+1), в яких застосовується адресна арифметика. Номер елементу масиву, до якого необхідно отримати доступ, є цілим числом. До значення вказівника, який є іменем масиву та містить адресу розміщення його першого елементу, додається ціле число, яке є добутком номера елемента масиву на розмір (у байтах) типу елементів, і таким чином обчислюється адреса в пам’яті шуканого елементу. Адресна арифметика застосовується в усіх арифметичних виразах щодо вказівників. Так, збільшення вказівника на одиницю означає зсув в пам’яті на кількість байтів, відповідного типу, на який він вказує.

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

1. Присвоєння.

Вказівнику присвоюється адреса (це може бути, наприклад, ім’я масиву, або результат операції отримання адреси змінної &: &a). Якщо a[], то a = = &a[0], a (або &a) є константою і її значення не може змінюватися.

2. Розкриття вказівника (посилання на вказівник).

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

Якщо ptr = &a, то val = *ptr те саме, що val = a.

3. Отримання адреси вказівника.

Змінна типу вказівник, як і всі решта, має адресу і значення. Операція & показує адресу, за якою знаходиться сам вказівник.

4. Операції збільшення і зменшення.

Ці операції здійснюються згідно правил адресної арифметики.

5. Різниця вказівників на елементи масиву показує, на якій відстані в масиві вони знаходяться.

Якщо оголошено масив a[], то зміст наступних виразів такий:

ptr=a;

*ptr - значення a[0]

*(ptr+i) - значення a[i]

ptr+і - адреса a[i] ( &a[i] )

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

Для символьних масивів існує декілька особливостей. Символьний масив (рядок) може бути оголошений за допомогою вказівника таким чином:

char *masyv5 = “Функція sin”;

У статичній пам’яті виділяється 12 байтів для рядка. Крім цього, виділяється ще кілька (2 або 4) байтів пам’яті для змінној masyv5, яка є вказівником. Спочатку ця змінна вказує на початок рядка, але її значення може змінюватися за правилами адресної арифметики (наприклад, можна писати ++masyv5).

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

char symb [3][5] = “abcd”,

“efgh”,

“i”;

то неповний третій рядок буде доповнений нулями і масив займе 15 байтів. Це незручно, оскільки неефективно використовується пам'ять, а також програміст повинен правильно вказати довжину рядків. Якщо ж застосувати масив вказівників на char:

char *menu [6] = “Sinus”, “Cosinus”, “Logarithm”, “Exponenta”, “Parabola 2”, “Parabola 3” ;

то компілятор сам підрахує необхідну кількість комірок пам’яті і розмістить усі рядки послідовно без зайвих пропусків. Тут оголошено масив із шести вказівників на char. Кожен елемент цього масиву вказує на початок відповідного рядка. За допомогою індексних виразів можна здійснювати доступ як до кожного рядка, так і до кожној літери. Так, операція menu[1] вкаже на початок другого рядка, а операція *(menu[1]+3) дасть значення четвертого елемента другого рядка, тобто літеру 's'.

ЗАВДАННЯ

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

2. Змодифікувати програму обчислення суми арифметичној прогресії, створеної у попередній роботі таким чином, щоб масив був оголошений не локально, а передавався за допомогою формальних параметрів у обидві функції.

2. Дослідити на прикладах особливості вказівників.

#include

#define PR(x) printf("x=%d,*x=%d,&x=%d\\n",x,*x,&x)

main()

static int arr[] = 100,200,300;

int *ptr1, *ptr2;

ptr1 = arr;

ptr2 = &arr[2];

PR(ptr1); ptr1++; PR(ptr1);

PR(ptr2); ++ptr2; PR(ptr2); printf("ptr2-ptr1 = %d\\n",ptr2-ptr1);

#include

#define PR(value) printf("value=%d\\t",(value)) #define NL putchar('\\n')

int a[] = 0,1,2,3,4;

main()

inti,*p;

/*1*/for(i=0;i...];

Тут - ім’я структурного типу (необов'язкове), }}