Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Козак Н.В. Лекции Основы создания программ в Си...doc
Скачиваний:
32
Добавлен:
23.09.2019
Размер:
2.24 Mб
Скачать

Массивы и указатели

Массивы и указатели довольно тесно связаны между собой. Имя массива можно разыменовывать, как указатель. В свою очередь, указатель можно индексировать, как массив, если это имеет смысл.

Массивы

Массив по существу является совокупностью однотипных переменных (элементов массива), объединенных под одним именем и различающихся своими индексами. Массив объявляется подобно простой переменной, но после имени массива указывается число его элементов в квадратных скобках:

int myArray[8];

Массив, как и переменную, можно инициализировать при объявлении. Значения для последовательных элементов массива отделяются друг от друга запятыми и заключаются в фигурные скобки:

int iArray[8] = {7, 4, 3, 5, 0, 1, 2, 6};

Обращение к отдельным элементам массива производится путем указания индекса элемента в квадратных скобках, например:

myArray[3] = 11;

myArray[i] = iArray[7-i];

Индекс должен быть целым выражением, значение которого не выходит за пределы допустимого диапазона. Поскольку индексация массивов начинается в С всегда с нуля (т.е. первый элемент имеет индекс 0), то, если массив состоит из N элементов, индекс может принимать значения от 0 до N—1.

Указатели

Указатель — это переменная, которая содержит адрес другого объекта. Этим объектом может быть некоторая переменная, динамический объект или функция. Говорят, что указатель ссылается на соответствующий объект. Адрес объекта, это 32-битное целое число, определяющее положение объекта в виртуальной памяти программы, указатель является не просто целым числом, а специальным типом данных. Он «помнит», на какого рода данные ссылается. Объявление указателя выглядит так:

тип_указываемого_объекта *имя_указателя [= значение];

Вот примеры объявлений:

int *pIntVar; // Указатель на целое.

double doubleVar = 3.14159265;

double *pDouble = &doubleVar; // Инициализация указателя на double.

Чтобы получить доступ к объекту, на который указатель ссылается, последний разыменовывают, применяя операцию-звездочку. Например, *pDouble будет представлять значение переменной, на которую ссылается pDouble:

printf("значение указателя (адрес): %р", pDouble);

printf("число, на которое он ссылается: %f", *pDouble);

Указатели используются при обработке строк, а также для передачи функциям параметров, значения которых могут ими изменяться (передача по ссылке).

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

Объект создается в свободной области виртуальной памяти функцией malloc (). Вот пример (предполагается, что переменные объявлены так, как выше):

pDouble = malloc(sizeof(double)); //Динамическое выделение памяти.

*pDouble = doubleVar; //Присвоение значения

//динамическому объекту.

printf("Значение динамического объекта: %f", *pDouble);

free(pDouble); //Освобождение памяти.

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

Различия двух последних примеров - В первом из них указателю pDouble присваивается адрес переменной doubleVar. Во втором указателю присваивается адрес динамически созданного объекта типа double; после этого объекту, на который ссылается pDouble, присваивается значение переменной doubleVar. Т.о. создается новая динамическая копия значения переменной.

Функция malloc () возвращает значение типа void* — «пустой указатель». Это указатель, который может указывать на данные любого типа. Такой указатель нельзя разыменовывать, поскольку неизвестно, на что он указывает — сколько байтов занимает его объект и как их нужно интерпретировать. В данном случае операция присваивания автоматически приводит значение malloc () к типу double*. Можно было бы написать в явном виде:

pDouble = (double*)malloc(sizeof(double));

Если выделение памяти по какой-то причине невозможно, malloc () возвращает NULL, нулевой указатель. NULL определяется в Windows как символьная константа в stdlib.h

#define NULL OL // long double constant

Указатель на функцию

Можно объявить, инициализировать и использовать указатель на функцию. В вызовах API Windows часто применяют, например, «возвратно-вызываемые функции» - callback функции. В вызове API в качестве аргумента в этом случае употребляется указатель на соответствующую функцию.

/* некоторая функция */

void showString(char *s)

{

printf(s);

}

/*Главная функция*/

int main(void)

{

void (*pFunc)(char*); // Объявление указателя на функцию.

pFunc = showString; // Инициализация указателя адресом функции.

(*pFunc)("Calling a pointer to function!\n");

return 0;

}

Соотношение между указателями и функциями примерно такое же, как между указателями и массивами, о чем говорится в следующем разделе.

Отношения указателей и массивов

Между указателями и массивами в С существует тесная связь. Имя массива без индекса эквивалентно указателю на его первый элемент.

int iArray[4];

int *piArr;

piArr = iArray; // piArr указывает на начальный элемент iArray.

Последнее эквивалентно

piArr = &iArray[0];

Наоборот, указатель можно использовать подобно имени массива, т.е. индексировать его. Например, piArr [3] представляет четвертый элемент массива iArray[ ].

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