Чтение онлайн

ЖАНРЫ

Linux программирование в примерах
Шрифт:

Наконец, упомянем потоки (threads), которые представляют несколько цепочек исполнения в рамках единственного адресного пространства. Обычно у каждого потока имеется свой собственный стек, а также способ получения локальных данных потока, т.е. динамически выделяемых данных для персонального использования этим потоком. Мы больше не будем рассматривать в данной книге потоки, поскольку это является продвинутой темой.

3.2. Выделение памяти

Четыре библиотечные функции образуют основу управления динамической памятью С Мы опишем сначала их,

затем последуют описания двух системных вызовов, поверх которых построены эти библиотечные функции. Библиотечные функции С, в свою очередь, обычно используются для реализации других выделяющих память библиотечных функций и операторов C++
new
и
delete
.

Наконец, мы обсудим функцию, которую часто используют, но которую мы не рекомендуем использовать.

3.2.1. Библиотечные вызовы:

malloc
,
calloc
,
realloc
,
free

Динамическую память выделяют с помощью функций

malloc
или
calloc
. Эти функции возвращают указатели на выделенную память. Когда у вас есть блок памяти определенного первоначального размера, вы можете изменить его размер с помощью функции
realloc
. Динамическая память освобождается функцией
free
.

Отладка использования динамической памяти сама по себе является важной темой. Инструменты для этой цели мы обсудим в разделе 15.5.2 «Отладчики выделения памяти».

3.2.1.1. Исследование подробностей на языке С

Вот объявления функций из темы справки GNU/Linux malloc(3):

#include <stdlib.h> /* ISO С */

void *calloc(size_t nmemb, size_t size);

 /* Выделить и инициализировать нулями */

void *malloc(size_t size);

 /* Выделить без инициализации */

void free(void *ptr);

 /* Освободить память */

void *realloc(void *ptr, size_t size);

 /* Изменить размер выделенной памяти */

Функции выделения памяти возвращают тип

void*
. Это бестиповый или общий указатель, все, что с ним можно делать — это привести его к другому типу и назначить типизированному указателю. Примеры впереди.

Тип

size_t
является беззнаковым целым типом, который представляет размер памяти. Он используется для динамического выделения памяти, и далее в книге мы увидим множество примеров его использования. На большинстве современных систем
size
_t является
unsigned long
, но лучше явно использовать
size_t
вместо простого целого типа
unsigned
.

Тип

ptrdiff_t
используется для вычисления адреса в арифметике указателей, как в случае вычисления указателя в массиве:

#define MAXBUF ...

char *p;

char buf[MAXBUF];

ptrdiff_t where;

p = buf;

while (/* некоторое условие */) {

 ...

 p += something;

 ...

 where = p - buf; /*
какой у нас индекс? */

}

Заголовочный файл

<stdlib.h>
объявляет множество стандартных библиотечных функций С и типов (таких, как
size_t
), он определяет также константу препроцессора
NULL
, которая представляет «нуль» или недействительный указатель. (Это нулевое значение, такое, как 0 или '
((void*)0)
'. Явное использование 0 относится к стилю С++; в С, однако,
NULL
является предпочтительным, мы находим его гораздо более читабельным для кода С.)

3.2.1.2. Начальное выделение памяти:

malloc

Сначала память выделяется с помощью

malloc
. Передаваемое функции значение является общим числом затребованных байтов. Возвращаемое значение является указателем на вновь выделенную область памяти или
NULL
, если память выделить невозможно. В последнем случае для обозначения ошибки будет установлен
errno
. (errno является специальной переменной, которую системные вызовы и библиотечные функции устанавливают для указания произошедшей ошибки. Она описывается в разделе 4.3 «Определение ошибок».) Например, предположим, что мы хотим выделить переменное число некоторых структур. Код выглядит примерно так:

struct coord { /* 3D координаты */

 int x, y, z;

} *coordinates;

unsigned int count; /* сколько нам нужно */

size_t amount; /* общий размер памяти */

/* ... как-нибудь определить нужное число... */

amount = count * sizeof(struct coord); /* сколько байт выделить */

coordinates = (struct coord*)malloc(amount); /* выделить память */

if (coordinates == NULL) {

 /* сообщить об ошибке, восстановить или прервать */

}

/* ... использовать координаты... */

Представленные здесь шаги являются стереотипными. Порядок следующий:

1. Объявить указатель соответствующего типа для выделенной памяти.

2. Вычислить размер выделяемой памяти в байтах. Для этого нужно умножить число нужных объектов на размер каждого из них. Последний получается с помощью оператора С

sizeof
, который для этой цели и существует (наряду с другими). Таким образом, хотя размер определенной структуры среди различных компиляторов и архитектур может различаться,
sizeof
всегда возвращает верное значение, а исходный код остается правильным и переносимым.

При выделении массивов для строк символов или других данных типа

char
нет необходимости умножения на
sizeof(char)
, поскольку последнее по определению всегда равно 1. Но в любом случае это не повредит.

3. Выделить память с помощью

malloc
, присвоив возвращаемое функцией значение переменной указателя. Хорошей практикой является приведение возвращаемого
malloc
значения к типу переменной, которой это значение присваивается. В С этого не требуется (хотя компилятор может выдать предупреждение). Мы настоятельно рекомендуем всегда приводить возвращаемое значение.

Поделиться с друзьями: