C установка IDE C структура програми C змінні C типи даних С функція printf C константи C арифметичні операції C операції порівняння і логічні операції C порозрядні операції C операції присвоювання C перетворення типів C умовні конструкції C цикли С масиви і рядки С функція scanf C препроцесор. Директива #include C #define директива C макроси C умовна компіляція C функції. Визначення та опис функцій C функції. Передача параметрів в функцію C функції. Повернення результату з функції C функції. Рекурсивні функції C область видимості змінних C зовнішні об'єкти C вказівники C вказівник. Операції з вказівниками C покажчики. Арифметика покажчиків C покажчики. Константи і покажчики C покажчики. Покажчики та масиви C покажчики. Масиви покажчиків, рядки і багаторівнева адресація C покажчики. Покажчики в параметрах функції C покажчики. Динамічна пам'ять C покажчики. Покажчик як результат функції C покажчики. Управління динамічної пам'яттю C покажчики. Покажчики на функцію C покажчики. Покажчики на функції як параметри і результати функцій C покажчики. Функції зі змінною кількістю параметрів C struct. Визначення структур C struct. Структури як елементи структур C struct. Покажчики на структури C struct. Масиви структур C struct. Структури і функції C struct. union об'єднання C struct. Бітові поля С file. Введення-виведення і робота з файлами C file. Читання і запис бінарних файлів C file. Читання і запис структур в файл C file. Читання і запис текстових файлів C file. Форматування вводу-виводу C file. Позиціонування в потоці C file. Консольне введення-виведення

C покажчики. Динамічна пам'ять


При створенні масиву з фіксованими розмірами під нього виділяється певна пам'ять. Наприклад, нехай у нас буде масив з п'ятьма елементами:

double numbers[5] = {1.0, 2.0, 3.0, 4.0, 5.0};

Для такого масиву виділяється пам'ять 5 * 8 (розмір типу double) = 40 байт. Таким чином, ми точно знаємо, скільки в масиві елементів і скільки він займає пам'яті. Однак це не завжди зручно. Іноді буває необхідно, щоб кількість елементів і відповідно розмір виділеної пам'яті для масиву визначалися динамічно в залежності від деяких умов. Наприклад, користувач сам може вводити розмір масиву. І в цьому випадку для створення масиву ми можемо використовувати динамічне виділення пам'яті.

Для управління динамічним виділенням пам'яті використовується ряд функцій, які визначені в заголовки stdlib.h :

malloc() . має прототип

void *malloc(unsigned s);

Виділяє пам'ять довжиною в s байт і повертає покажчик на початок виділеної пам'яті. У разі невдалого виконання повертає NULL calloc() . має прототип

void *calloc(unsigned n, unsigned m);

Виділяє пам'ять для n елементів по m байт кожен і повертає покажчик на початок виділеної пам'яті. У разі невдалого виконання повертає NULL realloc() . має прототип

void *realloc(void *bl, unsigned ns);

Змінює розмір раніше виділеного блоку пам'яті, на початок якого вказує покажчик bl, до розміру в ns байт. Якщо покажчик bl має значення NULL , тобто пам'ять не виділялася, то дія функції аналогічно дії malloc free () . має прототип

void *free(void *bl);

Звільняє раніше виділений блок пам'яті, на початок якого вказує покажчик bl. Якщо ми не використовуємо цю функцію, то динамічна пам'ять все одно звільниться автоматично при завершенні роботи програми. Однак все ж гарним вибором виклик функції free() , який дозволяє якомога раніше звільнити пам'ять.

Розглянемо застосування функцій на простого завдання. Довжина масиву невідома і вводиться під час виконання програми користувачем, і також значення всіх елементів вводяться користувачем:

#include <stdio.h>
#include <stdlib.h>
 
int main(void)
{   
    int *block; // указатель для блока памяти
    int n;      // число элементов массива
    // ввод числа элементов
    printf("Size of array=");
    scanf("%d", &n);
     
    // выделяем память для массива
    // функция malloc возвращает указатель типа void*
    // который автоматически преобразуется в тип int*
    block = malloc(n * sizeof(int));
     
    // вводим числа в массив
    for(int i=0;i<n; i++)
    {
        printf("block[%d]=", i);
        scanf("%d", &block[i]);
    }
    printf("\n");
     
    // вывод введенных чисел на консоль
    for(int i=0;i<n; i++)
    {
        printf("%d \t", block[i]);
    }
     
    // освобождаем память
    free(block);
    return 0;
}

Консольний висновок програми:

block = malloc(n * sizeof(int));

Перш за все треба відзначити, що всі три вище згадані функції для універсальності повертається в якості результату повертають покажчик типу void * . Але в нашому випадку створюється масив типу int, для управління яким використовується покажчик типу int * , тому виконується неявне приведення результату функції malloc до типу int * .

В саму функцію malloc передається кількість байтів для виділяється блоку. Це кількість підрахувати досить просто: достатньо помножити кількість елементів на розмір одного елемента n * sizeof(int) .

Після виконання всіх дій пам'ять звільняється за допомогою функції free () :

free(block);

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

free(block);
for(int i=0;i<n; i++)
{
    printf("%d \t", block[i]);
}

І якщо ми спробуємо це зробити, то отримаємо невизначені значення.

Замість функції malloc аналогічним чином ми могли б використовувати функцію calloc () , яка приймає кількість елементів і розмір одного елемента:

block = calloc(n, sizeof(int));

Або також можна було б використовувати функцію realloc () :

int *block = NULL;
block = realloc (block, n * sizeof(int));

При використанні realloc бажано (в деяких середовищах, наприклад, в Visual Studio, обов'язково) форматувати покажчик хоча б значенням NULL.

Але в цілому все три виклики в даному випадку мали б аналогічну дію:

block = malloc(n * sizeof(int));
block = calloc(n, sizeof(int));
block = realloc (block, n * sizeof(int));

Тепер розглянемо більш складну задачу - динамічне виділення пам'яті для двовимірного масиву:

#include <stdio.h>
#include <stdlib.h>
 
int main(void)
{
    int **table;    // указатель для блока памяти для массива указателей
    int *rows;      // указатель для блока памяти для хранения информации по строкам
 
    int rowscount;  // количество строк
    int d;      // вводимое число
     
    // ввод количества строк
    printf("Rows count=");
    scanf("%d", &rowscount);
 
    // выделяем память для двухмерного массива
    table = calloc(rowscount, sizeof(int*));
    rows = malloc(sizeof(int)*rowscount);
    // цикл по строкам
    for (int i = 0; i<rowscount; i++)
    {
        printf("\nColumns count for row %d=", i);
        scanf("%d", &rows[i]);
        table[i] = calloc(rows[i], sizeof(int));
 
        for (int j = 0; j<rows[i]; j++)
        {
            printf("table[%d][%d]=", i, j);
            scanf("%d", &d);
            table[i][j] = d;
        }
    }
    printf("\n");
 
    // вывод введенных чисел на консоль
    for (int i = 0; i<rowscount; i++)
    {
        printf("\n");
 
        for (int j = 0; j<rows[i]; j++)
        {
            printf("%d \t", table[i][j]);
        }
        // освобождение памяти для одной строки
        free(table[i]);
    }
     
    // освобождение памяти
    free(table);
    free(rows);
 
    return 0;
}

Мінлива table представляє покажчик на масив покажчиків типу int * . Кожен покажчик table [i] в цьому масиві являє покажчик на подмассів елементів типу int , тобто окремі рядки таблиці. А змінна table фактично являє покажчик на масив покажчиків на рядки таблиці.

Для зберігання кількості елементів в кожному підмасиві визначається покажчик rows типу int . Фактично він зберігає кількість стовпців для кожного рядка таблиці.

Спочатку вводиться кількість рядків в змінну rowscount . Кількість рядків - це додаткове ознакування міста в масиві, на який вказує покажчик table . І крім того, кількість рядків - це кількість елементів в динамічному масиві, на який вказує покажчик rows . Тому спочатку необхідно для всіх цих масивів виділити пам'ять:

table = calloc(rowscount, sizeof(int*));
rows = malloc(sizeof(int)*rowscount);

Далі в циклі здійснюється введення кількості стовпців для кожного рядка. Введене значення потрапляє в масив rows. І відповідно до введеного значенням для кожного рядка виділяється необхідний розмір пам'яті:

scanf("%d", &rows[i]);
table[i] = calloc(rows[i], sizeof(int));

Потім проводиться введення елементів для кожного рядка.

В кінці роботи програми при виведенні відбувається звільнення пам'яті. У програмі пам'ять виділяється для рядків таблиці, тому цю пам'ять треба звільнити:

free(table[i]);

І крім того, звільняється пам'ять, виділена для покажчиків table і rows:

free(table);
free(rows);

Консольний висновок програми:

Рядки Count = 2

Стовпці розраховувати на 1 = 3
Таблиця [0] [0] = 1
Таблиця [0] [1] = 2
Таблиця [0] [2] = 3

Стовпці розраховувати на 2 = 2
Таблиця [1] [0] = 4
Таблиця [2] [1] = 5

1 2 3
4 5

Наш партнер:
beta test mp3 playlist downloader