Версия:
Аннотация
Указатели. Зачем это? Модификация переданных функции переменных. Индексация переменных. Статические и динамические массивы.
Предварительный вариант!
Указатель на переменную дает возможность изменить её значение миную прямого обращения к самой переменной. Важны при вызове функций, когда хотим чтобы была возможность изменить "аргумент"функции.
Сначала будет показано как создать указатель и как его проинициализировать значение, т.е. присвоить значение.
Объявление указателя Указатель модификатор типа данных, т.е. он является некой добавкой к основному типу. Его пишут до объявляемой переменной. Получается как бы что если звездочки нет, то это обычная переменная указанного типа, а если он есть, то переменная превращается в указатель на отмеченный тип.
Указатель можно объявить на любой тип данных, например, на указатель на int:
Указатель на переменную В переменную типа указатель можно присвоить соответствующую числовую характеристику. Для формирования такой числовой характеристики используется операция языка Си – амперсанд (&). Для переменной он возвращает указатель на ячейку памяти, который позволит обращаться к данной ячейке памяти минуя саму переменную.
Подчеркну, что указатель можно вычислить только для ячеек памяти, например, для переменных. Арифметические выражения не являются ячейками памяти, и поэтому для них указатель не существует (см. 4 и 6). Попытка взятия указателя приведет к ошибки компилирования.
Указатель должен быть совместимого типа.
Указатели на разные тип данных разные не совместимы. За этим нужно тщательно следить.
Указатель является специальной числовой характеристикой и его можно распечатать использую соответствующий режим печати (%p):
Упражнение. Посоздавай переменных различного типа данных. Напечатай их значение. Можно ли увидеть какую-то закономерность.
По аналогии можно вычислить указатель на указатель:
В стр. 5 показан ошибочный код. Так, в нем тип у указателей не совместим, а именно – у переменной g тип указатель на int, а у выражения &b – тип указатель на указатель на int. Подчеркну, что даже если оба выражения являются указателями на указатели, но базовый тип разный, то они все равно не совместимы (стр. 6).
Разыменование – доступ к переменной Указатель на переменную позволяет получить доступ к переменной, т.е. считать значение и записать новое. Данная операция языка Си называется разыменование. Она обозначается * и применяется к уже созданной переменной (в общем случае к адресу):
Зная указатель можно изменить значение искомой переменной. Даже указатель, теперь возможна следующий код:
В обоих случая в переменную b будет присвоен указатель на переменную a.
В функциях находит сильное применение.
Изменение аргументов Применение указателей в качестве аргументов функций позволяет изменять значения аргументов:
Упражнение. Известно, что наибольший делитель чисел и можно представить в виде их линейной комбинации: , где , . Напишите программу, которой передаются два числа и , и которая возвращает соответствующие значения (т.е. и ) через аргумент функции. Значение самой функции будет равно наибольшему делителю.
Возвращаемое значение В случае, если измененное значение передается как аргумент, то возвращаемому значению придается определенный смысл. А именно согласно принятым правилам, отрицательное значение возвращается в случае ошибки и является её кодом (идентификатором). Неотрицательные значения обозначают успех, где само значение может трактоваться по разному. Например, в функции scanf оно равнялось количеству успешно считанных переменных.
Возвращаемое значение может быть и указателем. В такой случае, если он не равен нулю (NULL), то ошибки нет. Иначе считаем, что произошла ошибка.
Иногда хочется чтобы была возможность обращаться к переменной по её номеру. Допустим нам нужно посчитать количество четных и нечетных чисел:
Слишком мудрено. В данной программе объявлены две переменные, к которым идет обращение в теле цикла.
Для упрощения программы можно воспользоваться возможностью индексации переменных, а именно – создать статический (т.е. имеющий фиксированный размер, в момент компиляции) массив. Статический массив тоже является модификатором типа, т.е. может быть применен к любому существующему типу. Использую его программу можно модифицировать так:
Все стало намного лаконичнее. Так, массив позволил тело цикла сократить до одной строчки (16).
И я отвечу тебе, что программирование.
И ты уйдешь, так и не узнав, что ты для меня на нулевом месте.
Крайне важно, что нумерация переменных (элементов) в массиве начинается с 0, а не с 1.
Упражнение. Сколько целых чисел на отрезке , где ?
Таким образом, массив начинается с переменной имеющей нулевой индекс, а завершается переменной с индексом на единицу меньше чем размер массива. В таком случае их общее количество как раз будет равно .
Статический массив можно проинициализировать:
Более того, в последнем случае размер массива можно и не указывать:
В последнем случае компилятор автоматически создал массив нужно размера. В случае, если размер указан, но он больше списка инициализации, то только первые элементы будут проинициализированы.
Операции на массивом К элементам массива можно получить доступ не только через индексацию. Так, имя массива само по себе является переменной и как к переменной к ней можно применять арифметическую операция сложения и разыменование:
Переменных за последним элементом не существуют. Выход за последний элемент массива запрещен. При запуске приведет к ошибке программы и принудительному завершению.
Двумерные массивы будут рассказаны в отдельной заметке.
Массивы можно создавать динамически в процессе работы программы:
Для этого доступна вспомогательная (см. стр. 2) функция malloc, которая выделяет массив запрашиваемого (например, n * sizeof(double)) размера и возвращает указатель на его начало (см. стр. 10). Память является ресурсом системы, поэтому, при завершении работы с массивом соответствующую ранее выделенную память необходимо освободить (см. стр. 28) посредством функции free.
Упражнение. Как сделать так, чтобы числа печатались через запятую, а последним была точка?
Операции Над переменной указатель также доступны арифметические операции сложение и вычитания (умножения и деления нет!).