В языке С понятия указателей, массивов и строк тесно связаны.
Очень часто при написании простых программ используется только один класс переменных, называемых статическими (static). В узком смысле статическими называют переменные, описанные в блоке исходного кода при объявлении переменных. При выполнении программы приложение не может ни получить дополнительных переменных, ни изменить положение переменных в памяти, нет возможности узнать адрес памяти каждой переменной или константы. Обратиться к реальной ячейке памяти очень просто — достаточно указать имя переменной. Например, если вы хотите увеличить на 10 значение целой переменной idecade, то к ней можно обратиться по имени:
idecade += 10;
Объявление переменных-указателей:
Для описания переменной-указателя pimemorycell_address, которая содержит адрес некоторой переменной типа int, нужно написать:
int *pimemorycell_address;
На самом деле это объявление состоит из двух частей. Типа переменной pimemorycell_address:
int *
и идентификатора переменной
pimemorycell_address
Звездочка, расположенная следом за типом int, означает "указывает на". То есть, этот тип данных представляет собой некоторую переменную-указатель, в которой хранится адрес переменной int:
int *
Указатель содержит адрес конкретного типа данных.
Пример:
char *pcaddress;
int *piaddress;
Тип указателя pcaddress совершенно отличен от типа указателя piaddress. Если описан некоторый указатель одного типа, который затем используется с данными другого типа, то во время выполнения программы возникнут ошибки, а при ее компиляции предупреждения. Нельзя назвать хорошим стилем программирования такой, когда указатель описывается одним образом, а затем используется совершенно по-другому. Для примера рассмотрим следующий фрагмент программы:
int *pi;
float real_value = 98.26;
pi = &real_value;
Здесь переменная pi имеет тип int *, это означает что в ней может содержаться адрес переменной типа int. В третьем операторе сделана попытка присвоить переменной pi адрес (&real_value) переменной типа float.
Указатели можно инициализировать при их описании. Например: следующие два оператора выделяют память для двух переменных iresult и piresult.
int iresult; int *piresult = &iresult;
Переменная iresult — это обычная целая переменная, a piresult — указатель на целое число. Указатель piresult при инициализации получает значение адреса переменной iresult. Эта запись может в чем-то запутать: инициализируется не значение *piresult (которое должно быть целым числом), а указатель piresult (который является адресом целой переменной). Второй из приведенных выше операторов можно записать при помощи двух следующих эквивалентных операторов: int *piresult; piresult = &iresult;
Следующий пример показывает, как объявить и инициализировать указатель на строку:
#include <stdio.h>
#include <string.h>
void main()
{
char *pszpalindrome="MADAM I'M ADAM"; /* "Мадам я Адам" */
int i;
for (i=strien(pszpalindrome)-1; i >= 0; i--)
printf("%c",pszpalindrome[i]);
printf("5s",pszpalindrome);
}
Компилятор запоминает адрес первого символа строки "MADAM I'M ADAM" в переменной pszpalindrome. При работе программы можно использовать переменную pszpalindrome так же, как и любые другие строки. Это объясняется тем, что компилятор С создает таблицу строк, в которой он хранит строковые константы программы.
Функция strlen(), имеющая прототип в файле string.h, определяет длину строки. Переменная-индекс / получает начальное значение, на единицу меньшее числа, возвращаемого функцией strlen(), поскольку в цикле for строка psz обрабатывается как символьный массив. В палиндроме 14 букв. Если строка psz рассматривается как массив символов, то индексы его элементов имеют значения от 0 до 13. Данный пример помогает прояснить не вполне очевидную связь между указателями на символьные строки и массивами символов.