Создание динамических объектов
По стандарту аргументы функций malloc, calloc имеют тип возвращаемого объекта void*.
char *s = (char*)malloc(size);
unsigned size; /* объем памяти, который необходимо выделить */
char *s = (char *)calloc(nelem,elsize);
unsigned nelem; /* число элементов, для которых нужно выделить память */
unsigned elsize; /* объем памяти, который необходимо выделить для каждого элемента */
/* либо просто заменив char* на void* */
void* calloc(nelem, elsize);
unsigned nelem;
unsigned elsize;
Обе функции возвращают указатель на выделенную память. Для определения необходимого объема памяти можно использовать оператор sizeof:
Объем памяти, необходимый для хранения выражения:
Объем памяти, необходимый для хранения значений типа T.
Функции malloc и calloc возвращают указатель на созданный динамический объект. Фактически функции возвращают знаковые указатели, которые могут быть явно преобразованы к подходящему типу указателя. Значения, возвращенные функциями распределения памяти, используются для ссылок на динамические объекты. Например, с помощью оператора
pi = (int *) malloc(sizeof(int));
выделяется память для одного целого значения. Адрес этой области памяти присваивается переменной pi после его преобразования из типа char * (указатель на знак), с которым он возвращается функцией malloc, к типу int * (указатель на целое), т.е. типу переменной pi.
Доступ к динамическим объектам
Присваивание значения объекту, ссылка на который задана указателем pi, выполняется с помощью имени указателя *pi, например:
Одно и то же значение может быть присвоено более чем одной переменной-указателю. Таким образом, можно ссылаться надинамический объект с помощью более одного указателя. Про объект, к которому можно обращаться с использованием более чем одного указателя, говорят, что он имеет псевдоимена ( alias ). Например, в результате присваивания
и qi, и pi указывают на один и тот же объект, т.е. они являются псевдоименами. Неуправляемое использование псевдоимен может нанести ущерб пониманию текста программы, так как возможность доступа к одному и тому же объекту и его модификация с помощью различных псевдоимен не всегда очевидны при анализе части программы.
Время жизни динамического объекта
Память, занимаемая динамическими объектами, если она необходима для других целей, должна быть освобождена явным указанием. В противном случае эта память может быть потеряна, т.е. станет невозможным ее повторное использование. Явное освобождение выполняется использованием функции free, которая имеет следующую спецификацию:
Необходимо предпринимать меры предосторожности для избежания ошибок, связанных со ссылками на объект, память для которого уже освобождена - проблема висящей ссылки (Horowwitz, E. 1983. Fundamentals of Programming Languages. Computer Science Press).
Если реализация языка обеспечивает сборку мусора, то память, занимаемая объектами, к которым невозможен доступ, может быть автоматически подготовлена для повторного использования. Однако в языке Си, в отличие от языков Лисп и Снобол, такая возможность отсутствует.
Указание на заранее определенные объекты. Указатели могут обеспечивать ссылку на заранее определенные объекты. Адрестакого объекта может быть определен использованием оператора адресации & ( address of operator ). Например, рассмотрим переменные i и pi, определенные как
Присваивание
pi позволяет ссылаться на объект с именем i также с помощью указателя pi, используя обозначение *pi. Имена i и *pi -псевдоимена. Оператор & является также стандартным средством моделирования передачи параметров по ссылке. Однако его употребление может привести к проблеме висящей ссылки.
Указание на произвольную ячейку памяти. С помощью явных преобразований можно получить указатель на произвольную ячейку памяти. Например, предположим, что pt является указателем типа T*. Тогда указатель на ячейку памяти 0777000 можно получить с помощью следующей записи:
Обращение к конкретным ячейкам памяти часто бывает необходимо в программах, взаимодействующих с оборудованием, например в драйверах устройств, когда для управления устройствами нужно иметь доступ к таким ячейкам памяти, как регистры состояния или ячейки буфера устройства. Хотя такие возможности полезны и даже необходимы для некоторых приложений, пользоваться ими следует с осторожностью.
Связь между указателями и массивами
В языке Си массивы и указатели тесно связаны. Имя каждого массива может рассматриваться как указатель на первый элементмассива. Элемент массива a[i] есть элемент массива, на который указывает значение a+i, т.е. *(a+i), где значение а является адресом первого элемента массива а, а именно a[0]. Выражение a+i является примером арифметических действий с указателями - целое значение i складывается со значением указателя, адресом первого элемента массива а. Значение этого выражения есть а плюс объем памяти, занимаемый i элементами массива a. Предположим, что x - двумерный массив. Тогдассылка на подмассив x[i] является ссылкой на i-ю строку массива x. x[i] дает адрес первого элемента этой строки, т.е. *(x+i). Элементы каждой строки занимают непрерывную область памяти, так как массивы хранятся записанными по строкам, т.е. при записи элементов массива в память быстрее всех изменяется последний индекс.
Аналогично, ссылка на y[i], где y - n-мерный ( n>1 ) массив, является ссылкой на ( n-1 )-мерный подмассив с элементамиy[i, j2,j3,_jn], где значения jk соответствуют определению массива y. y[i] дает адрес первого элемента этого подмассива, т.е. *(y+i). Все элементы этого ( n-1 )-мерного подмассива занимают непрерывную область памяти.
|