- •2. Основні елементи мови с
- •3.1 Алфавіт мови програмування
- •3.2Лексеми
- •3.3Ключові слова
- •3.4Ідентифікатори
- •3.5 Класифікація типів даних
- •3.6 Літерали
- •3.7 Оператори
- •3.8 Коментарі
- •3.9 Директиви препроцесора
- •3.10 Організація програми
- •3. Операції та вирази
- •4.1 Загальні відомості
- •4.2 Арифметичні операції
- •4.3 Операції приведення типів
- •(Тип) ідентифікатор,
- •4.4 Операції присвоєння
- •4.5 Операції інкремента і декремента
- •4.6 Операції порівняння
- •4.7 Операції зсуву
- •4.8 Порозрядні операції
- •4.9 Логічні операції
- •4.10 Операція sizeof
- •Sizeof ( Вираз ),
- •4.11 Операція послідовного обчислення
- •4.12 Операція умови (?:)
- •Операнд-1 ? Операнд-2 : Операнд-3
- •4.13 Адресні операції
- •Тип * ім'я_покажчика;
- •4. Прості типи даних
- •5.1 Оголошення змінних
- •[ Модифікатори ] тип ім’я_змінної;
- •5.2 Час існування та область видимості змінних
- •5.3 Цілі типи даних
- •5.4 Дійсні типи даних
- •5. Оператори керування
- •5.1 Оператор розгалуження if
- •If ( Вираз ) true-оператор;
- •5.2 Оператор розгалуження if-else
- •If ( Вираз ) true-оператор; else false-оператор;
- •5.3 Оператор множинного розгалуження switch
- •5.4 Оператор циклу for
- •For ( Вираз 1 ; Вираз 2 ; Вираз 3 ) тіло циклу
- •5.5 Оператор циклу while
- •While ( Вираз ) тіло циклу
- •5.6 Оператор циклу do while
- •Тіло циклу while ( Вираз ) ;
- •5.7 Оператор break
- •5.8 Оператор continue
- •6 Введення та виведення даних
- •7.1 Функція виведення printf
- •Int printf( стрічка форматування, змінна1, змінна2, ... );
- •7.2Функція введення scanf
- •Int scanf (стрічка форматування, адреса змінної1, адреса змінної2, ... );
- •9.1 Введення та виведення у файл
- •7 Масиви
- •7.1 Загальні поняття
- •7.2 Одновимірні масиви
- •Тип даних ім’я змінної [n];
- •7.3 Багатовимірні масиви
- •6. Функції
- •6.1 Основні поняття
- •{Тіло функції}
- •6.2 Область видимості
- •6.3 Порожній тип void
- •6.4 Передача аргументів у функцію
- •6.5 Рекурсивні функції
- •6.6 Прототипи функцій
- •8. Рядки в с
- •4.1 Прототипи
- •4.2 Функції перетворення буферів
- •4.3 Функції перевірки літер
- •4.4 Рядок символів
- •4.5 Операції з рядками:
- •4.6 Символьні константи
- •9. Покажчики
- •5.1 Визначення та ініціалізація покажчиків
- •5.2 Визначення покажчиків:
- •5.3 Масиви
- •5.6 Копіювання рядка
- •5.7 Посилання та оператор &
- •5.9 Посилання в якості результатів функції
- •5.10 Покажчики на функцію
- •Тип_функціі (* імя_покажчика) (специфікація_параметрів);
- •5.11 Покажчики на void
- •5.12 Арифметика покажчиків
- •10. Структури, об’єднання, перерахування
- •7.1 Структури
- •7.2 Ключове слово typedef
- •7.3 Об’єднання
- •7.4Перераховуваний тип (Enum)
- •Enum dataType
- •Enum day
- •11 Динамічне виділення пам'яті
5.11 Покажчики на void
Покажчики на void –це покажчики спеціального типу. Він являє собою відсутність типу, тобто вказує на зміну що не має типу(невідома довжина і опції розіменовування).
Це дозволяє їм вказувати на будь-які дані: від char до double. Але на заміну вони мають великі обмеження: дані на які він вказує неможуть безпосередньо роіменовуватись(що є логічним , так як ми не маємо типу), з цієї причини ми повинні вказувати тип перд тим як розіменувати дані на які вказує цей покажчик.
Приклад 26. Покажчик наvoid.
void increase (void* data, int psize)
{
if ( psize == sizeof(char) ) {
char* pchar; pchar=(char*)data; ++(*pchar);
}
else if (psize == sizeof(int) ){
int* pint; pint=(int*)data; ++(*pint); }
}
int main ()
{
char a = 'x';
int b = 1602;
increase (&a,sizeof(a));
increase (&b,sizeof(b));
printf("a= %c, b= %i \n", a, b);
}
Результати роботи програми:
Пояснення:
В функцію передаються по черзі адреса на зміну а(символьний тип) та на b (тип цілого числа). Функція приймає покажчик на void тому не важливо що ми передали з осовної програми. Але для роботи з переданими даними, нам потрібно розіменувати покажчик *data. Тут і виникає проблема. Так як ми не знаємо що за тип буле передано. Це вирішується конструкцією if…else де за допомого функції sizeof() ми можемо встановити розмір зміної за її покажчиком((!) Покажчик не залежить від типу! А лише від розрядності ОС, так для 32-х бітних він матиме розмір 4 байти, для 64-х бітних – 8 байтів) на комірку розміщення зміної. Таким чином маючи опції вибору (якщо розмір = 1 байт то це char, якщо =4 то це int)ми встановлюємо тип даних.
В Сі дозволяється створювати покажчики на покажчики, що вказують на дані чи інші покажчики. Для цього потрібно додавати при оголошені покажчик на покажчик ще один оператор «*»:
Приклад 27. Створення покажчика на покажчику.(рис. 5.12)
int x =0;
int *y = &x;
int **z = &y;
x = 'a';
y = &x;
z = &y;
Рисунок 5.12 – Покажчик на покажчику
Значення кожної змінної пишеться всередині кожної клітини, під клітинкою їх адреси в пам'яті.
Нове в цьому прикладі змінна с, яка може використовуватися в трьох різних представленнях, кожне з них буде мати інше значення:
Приклад 28. Різні види представлення.
z має тип символ ** і значення 1000
* z має тип символів * і значення 100
** z має тип символ і значення ‘a’
5.12 Арифметика покажчиків
Виконання арифметичних дій з покажчиками дещо відрізняється від дій з цілочисельними даними, даними з плаваючою точкою чи інших типів. Над ними можна виконувати дії додавання чи віднімання. Але ці дії мають різну поведінку з покажчиками залежно від розміру типу даних, до яких вони застосовуються.
Розглядаючи різні основні типи даних, ми бачили, що деякі займають більше або менше місця, ніж інші, у пам'яті. Наприклад char займає 1 байт, short займає 2 байта і long займає 4.
Приклад 29. Встановлення покажчиків на різні типи даних..
char * pchar;
short * pshort;
long * plong;
Нехай вони вказують на комірку 100.
Якщо ми виконаємо дії інкремента , то зміщення в памяті буде наступним:
Приклад 30. Здійснення інкременту.
pchar++; (аналог pchar = pchar + 1;)
pshort++; (аналог pshort = pshort + 1;)
plong++; (аналог plong = plong + 1;)
Рисунок 5.13 -
Таким чином при збільшені покажчик на один пункт ми зміщуємо його на відповідний розмір типу, тобто для char це 1 байт, для short це 2 байти, для long це 4 байти.
Приклад 31. Представлення операцій з покажчиками та результати їх використання.
#include <stdio.h>
#include <stdlib.h>
intmain (){
inta[] = {10,20,30,40,50 }, b;
int*p = a;
int*p2 = &a[3];
b = p2 - p;
printf(" 1) b = %i\n", b);
printf(" 2) *p = %i", *p);
printf(" *p++ = %d \n", *p++);
printf(" 3) *p = %i", *p);
printf(" *(++p) = %d \n", *(++p));
printf(" 4) *p = %i", *p);
printf(" *p+2 = %d \n", *p+2);
printf(" 5) *p = %i", *p);
printf(" *(p+2) = %d \n", *(p+2));
printf(" 6) *p = %i", *p);
printf(" *(p+8) = %d \n", *(p+8));
system("PAUSE");
returnEXIT_SUCCESS;
}
Результат роботи програми:
Розглянемо детальніше дії:
b у нас різниця між покажчиками р і р2. Так як р2 покажчик на третій елемент, а р покажчик на перший то різниця між ними = 3 (тобто 3 розміра типу int або 3*4=12 байт );
Виводить на екран значення на яке вказує покажчик р, а також демонструє реакцію покажчика на оператори * і ++ (операція * виконується першою, і лише потім збільшується значення покажчика);
Значення р було зміщено на одне значення і тепер воно вказує на другий елемент масиву. В другій дії значення р змінюється до розіменовування тому воно дорівнює наступному значенню в масиві, тобто послідовність дій наступна: спочатку йде інкременування (обов’язково перед р має бути ++ , інакше результат буде як в попередньому полі!), а потім розіменовування;
Тут лише виводиться значення на 2 більше ніж те ,на яке вказує покажчик.
В цій стрічці значення самого покажчика не змінюється, відбувається лише зміщення по масиву на 2 елемента від того на яке він вказує.
Тут вказано що вихід за границі масиву ніяк не контролюється, тому потрібно зберігати обережність при використані покажчиків!