Указательные выражения. Адресная арифметика. 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Указательные выражения. Адресная арифметика.



Указательные выражения К указателям можно применить операцию присваивания. Указатели одного и того же типа могут использоваться в операции присваивания, как и любые другие переменные.Пример:               {                                    int x = 10;                                    int *p, *g; // объявление указателя                                    p = &x; // установка указателя p на переменную x                                    g = p; // указатель g тоже указывает на x                                    printf("p = %d", p); // вывод на экран содержимого p (вывод адреса)                                    printf("\n g = %d", g); // вывод адреса                                    printf("\n x = %d", x, "*g = %d", *g); /* вывод на экран величины x и величины, находящейся по адресу указателя g */               }               В языке С допустимо прмсвоить указателю любой адрес памяти. Однако, если объявлен указатель на целое число (int *p;), а по адресу, которому присвоен данному указателю, находится переменная x типа float, то при компиляции программы будет выдано сообщение об ошибке в строке p = &x. Эту ошибку можно исправить, преобразовав указатель на int к типу указателя на float явным преобразованием типа:                p = (int*)&x;но при этом теряется информация о том, на какой тип указывал исходный указатель. Адресная арифметика Над указателями можно выполнять унарные операции: инкремент и декремент. При выполнении операций ++ и -- значение указателя увеличивается или уменьшается на длину типа, на который ссылается используемый указатель. Пример:                int *ptr, a[10];               ptr=&a[5];               ptr++; // равно адресу элемента a[6]               ptr--; // равно адресу элемента a[5]В бинарных операциях сложения и вычитания могут участвовать указатель и величина типа int. При этом результатом операции будет указатель на исходный тип, а его значение будет на указанное число элементов больше или меньше исходного. Пример:                int *ptr1, *ptr2, a[10];               int i=2;               ptr1=a+(i+4); // равно адресу элемента a[6]                ptr2=ptr1-i; // равно адресу элемента a[4] В операции вычитания могут участвовать два указателя на один и тот же тип. Результат такой операции имеет тип int и равен числу элементов исходного типа между уменьшаемым и вычитаемым, причем если первый адрес младше, то результат имеет отрицательное значение. Пример:                int *ptr1, *ptr2, a[10];               int i;               ptr1=a+4;               ptr2=a+9;               i=ptr1-ptr2;               // равно 5                i=ptr2-ptr1;               // равно -5 Значения двух указателей на одинаковые типы можно сравнивать в операциях ==,!=, <, <=, >, >= при этом значения указателей рассматриваются просто как целые числа, а результат сравнения равен 0 (ложь) или 1 (истина). Пример:                int *ptr1, *ptr2, a[10];               ptr1=a+5;               ptr2=a+7;               if (prt1>ptr2) a[3]=4; В данном примере значение ptr1 меньше значения ptr2 и поэтому оператор a[3]=4 не будет выполнен.

 

24. Связь массивов и указателей Принято, что имя массива – адрес памяти, начиная с которого расположен массив, т.е. адрес первого элемента массива.Идентификатор массива является константным указателем на его нулевой элемент. Например, для массива имя – это тоже самое, что &b[0], а к i-тому элементу массива можно обратиться, используя выражение *(b+i).Для того, чтобы получить значение 6 элемента массива а, можно записать a[5] или *(a+5). Можно описать указатель, присвоить ему адрес начала массива и работать с массивом через указатель.Следующий фрагмент программы копирует все элементы массива a в массив b:               int a[100], b[100];               int *pa = a; // или int *p = &a[0]               int *pb = b;               for (int i=0; i<100; i++)               *pb++ = *pa++; // или pb[i] = pa[i]Для доступа к элементу многомерного массива указываются все его индексы *(matr[i]+j) или *(*(matr[i])+j). Это возможно, поскольку matr[i] является адресом начала строки массива.

Связь массивов и указателей

В языке Си имя массива трактуется как указатель-константа на массив. Пусть в программе объявлен массив:

int X[10];

В таком случае Х является указателем на нулевой элемент массива в памяти компьютера. В связи с этим является истинным отношение

X==&X[0]

Следовательно, для доступа к элементам массива кроме индексированных имен можно использовать разадресованные указатели по принципу:

Имя[индекс] тождественно *(имя+индекс)

Например, для описанного выше массива Х взаимозаменяемы следующие обозначения элементов:

X[5], или *(Х+5), или *(5+Х).

В языке Си операция [ играет роль знака операции сложения адреса массива с индексом элемента.


 

25. Функции динамического распределения памяти.

Calloc

Функция Выделяет оперативную память.

Синтаксис #include <stdlib.h

void * calloc(size_t nitems, size_t size);

Файл, содержащий stdlib.h,alloc.

прототип

Описание calloc обеспечвает доступ к динамической области памяти. Динамическая область памяти доступна для динамического распределения блоков памяти переменной длины. Многие структуры данных, например, деревья и списки, используют распределение динамической области памяти.

 calloc выделяет блок памяти размером nitems x size.

Блок обнуляется. Если вы хотите выделить блок, размер которого превышает 64К то нужно использовать функцию farcalloc.

Возвращаемое calloc возвращает указатель на выделенный блок.

значение calloc возвращает NULL, если недостаочно памяти для выделения нового блока, или nitems или size равны 0.

Переносимость Поддерживается в системах UNIX и определена в ANSI

C. Описана в Керниган и Риччи. Смотрите также faralloc, free, malloc, realloc.Пример:

#include<stdio.h>

#include<alloc.h>

#include<string.h>

int main(void)

{

 char *str = NULL;

 /* выделить память для строки */

 str = calloc(10,sizeof(char));

 if(str)

 {

 /* скопировать в строку "Hello" */

 strcopy(str,"Hello");

 /* вывести строку */

 printf("Строка: %s\n",str);

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

 free(str);

 }

 else

 printf("Недостаточно памяти\n");

 }

return(0);

}

Malloc

Функция.Выделяет память.

Синтаксис #include<stdlib.h>

#include<alloc.h>

void *malloc(size_t size);

Файл, содержащий stdlib.h и alloc.h прототип

Описание

Функция обеспечивают выделение блока памяти, размером size байт. Она позволяет программе выделять память по необходимости и столько, сколько нужно.

Динамическая область памяти доступна для динамического размещения блоков памяти переменной длины. Многие структуры данных, такие как "деревья" или "списки" – естественным образом используют динамическое распределение памяти. В малых моделях памяти, вся область памяти между концом сегмента данных и вершиной программного стека доступна для использования, за исключением 256-байтовой границы, находящейся непосредственно перед вершиной стека. Данная граница предназначена для наращивания стека прикладной программы (плюс небольшое количество, требуемое для DOS). В больших моделях памяти все пространство после программного стека до конца физической памяти доступно для динамической области.

Возвращаемое

 Функция malloc возвращает указатель на блок выде значени ленной памяти. Если для размещения блока недостаточно памяти, функция malloc возвращает NULL.

Содержимое блока остается неизменным. Если аргумент size равен 0, то функция возвращает NULL.

Переносимость Функция доступна в системах UNIX и поддерживается стандартом ANSI C.

Смотрите также allocmem, calloc, coreleft, farcalloc, farmalloc, free, realloc.

Версия, которая будет написана здесь, не имеет ограничений: вызовы malloc и free могут выполняться в любом порядке: malloc делает запрос в операционную систему на выделение памяти тогда, когда она требуется. Эти программы иллюстрируют приемы, позволяющие получать машинно-зависимый код сравнительно машинно-независимым способом, и, кроме того, они могут служить примером применения таких средств языка, как структуры, объединения и typedef.

Никакого ранее скомпилированного массива фиксированного размера, из которого выделяются куски памяти, не будет. Функция malloc запрашивает память у операционной системы по мере надобности. Поскольку и другие действия программы могут вызывать запросы памяти, которые удовлетворяются независимо от этого распределителя памяти, пространство, которым заведует malloc, необязательно представляет собой связный кусок памяти. Поэтому свободная память хранится в виде списка блоков. Каждый блок содержит размер, указатель на следующий блок и само пространство. Блоки в списке хранятся в порядке возрастания адресов памяти, при этом последний блок (с самым большим адресом) указывает на первый.

При возникновении запроса на память просматривается список свободных блоков, пока не обнаружится достаточно большой блок. Такой алгоритм называется "поиском первого подходящего" в отличие от алгоритма "поиска наилучшего подходящего", который ищет наименьший блок из числа удовлетворяющих запросу. Если размер блока в точности соответствует требованиям, то такой блок исключается из списка и отдается в пользование. Если размер блока больше, чем требуется, от него отрезается нужная часть - она отдается пользователю, а ненужная оставляется в списке свободных блоков. Если блока достаточного размера не оказалось, то у операционной системы запрашивается еще один большой кусок памяти, который присоединяется к списку свободных блоков.

Процедура освобождения сопряжена с прохождением по списку свободных блоков, поскольку нужно найти подходящее место для освобождаемого блока. Если подлежащий освобождению блок примыкает с какой-то стороны к одному из свободных блоков, то он объединяется с ним в один блок большего размера, чтобы по возможности уменьшить раздробленность (фрагментацию) памяти. Выполнение проверки, примыкают ли блоки друг к другу, не составляет труда, поскольку список свободных блоков всегда упорядочен по возрастанию адресов.

 

Существует проблема, состоящая в том, что память, выдаваемая функцией malloc, должна быть соответствующим образом выровнена с учетом объектов, которые будут в ней храниться. Хотя машины и отличаются друг от друга, но для каждой из них существует тип, предъявляющий самые большие требования на выравнивание, и, если по некоему адресу допускается размещение объекта этого типа, то по нему можно разместить и объекты всех других типов. На некоторых машинах таким самым "требовательным" типом является double, на других это может быть int или long.

 

Свободный блок содержит указатель на следующий блок в списке, свой размер и собственно свободное пространство. Указатель и размер представляют собой управляющую информацию и образуют так называемый "заголовок". Чтобы упростить выравнивание, все блоки создаются кратными размеру заголовка, а заголовок соответствующим образом выравнивается. Этого можно достичь, сконструировав дддобъединение, которое будет содержать соответствующую заголовку структуру и самый требовательный в отношении выравнивания тип. Для конкретности выберем тип long.


 



Поделиться:


Последнее изменение этой страницы: 2021-08-16; просмотров: 41; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.139.97.157 (0.014 с.)