Выражения с указателями
Можно присваивать один указатель другому, но эта операция имеет смысл только в том случае, если оба указателя адресуют один и тот же тип, например
{ int i=123 *pi1=&i, *pi2;
// присваиваем указателю pi2 значение указателя pi1
pi2=pi1;
//выведем значения указателей:
printf(“ %p %p”, pi1, pi2);
}
В результате мы получим одно и то же значение адреса.
Указатели могут встречаться в выражениях, например:
int x=5, y, *px=&x;
y = *px + 5; // y получит значение 10
Приоритет у унарной операции разадресации * выше, чем у бинарных арифметических операций, поэтому при вычислении выражения *px + 5 вначале будет извлечено содержимое по адресу, хранящемуся в р ( то есть получено значение 5), а затем будет выполнена бинарная операция сложения.
Операции с указателями
С указателями можно использовать только следующие операции:
++ инкремента
- - декремента
+, - сложения и вычитания
Если к указателю применяются операции ++ или --, то указатель увеличивается или уменьшается на размер объекта, который он адресует:
тип *ptr;
ptr ++ = значение ptr + sizeof (тип)
ptr -- = значение ptr - sizeof (тип)
Например:
int i, *pi = &i;
float a, *pa =&a;
pi++; //значение указателя pi увеличивается на 2 байта, так как он адресует объект
//типа int, указатель сдвигается вправо на 2 байта
pa++; // значение указателя pa увеличивается на 4 байта, так как он адресует объект
//типа float, указатель сдвигается вправо на 4 байта
Одним из операндов операции сложения может быть указатель, другим должно быть целое число. Целое число складывается с указателем следующим образом:
тип *ptr;
int n;
ptr + n = значение ptr + n*размер типа
Например,
float a, *pa = &a;
pa = pa+3; // значение указателя pa будет увеличено на 12 байт (сдвиг указателя вправо)
Левым операндом операции вычитания может быть указатель, а правым операндом должно быть целое число:
тип *ptr;
int n;
ptr - n = значение ptr - n*размер типа
Например:
int i, *pi = &i;
pi=pi-5; // уменьшение значения указателя на 10 байт (сдвиг указателя влево)
Пример 3.Использование указателей в выражениях
#include <stdio.h>
void main (void)
{
int x=5,y,*px=&x; //указатель px инициализирован адресом переменной x
y=*px+5;
printf("y=%d значение указателя=%p\n",y,px);
y=*px++; //изменение значения указателя после его использования
printf("y=%d значение указателя=%p\n",y,px);
px=&x;
y=(*px)++; //изменение содержимого по адресу, хранящемуся в px
printf("y=%d значение указателя=%p значение, адресуемое указателем *px= %d\n",y,px,*px);
y=++*px; //изменение содержимого по адресу px на единицу
printf("y=%d значение указателя=%p\n",y,px);
}
Результаты работы этой программы имеют вид:
y=10 значение указателя=1561:1000
y=5 значение указателя=1561:1002
y=5 значение указателя=1561:1000 значение, адресуемое указателем*px= 6
y=7 значение указателя=1561:1000
УКАЗАТЕЛИ И МАССИВЫ
Между указателями и массивами существует прямая связь. Когда объявляется массив, например:
int arr [5];
то идентификатор массива arr определяется как константный указатель на первый (с индексом 0) элемент массива. Это означает, что имя массива содержит адрес элемента массива с индексом 0:
arr = &arr[0];
Так как идентификатор массива содержит адрес, то можно, например, записать:
int arr[ 5]; //объявление массива целых
int *parr; //объявление указателя на данное целого типа
parr = arr; // присваивавание указателю адреса первого элемента массива, то же, что
// и parr = &arr[0];
Связь между именем массива arr и указателем parr можно изобразить следующим образом:
arr[0] | arr[1] | arr[2] | arr[3] | arr[4] |
Адрес элемента массива с индексом i может быть записан как
&arr[i], что эквивалентно записи parr+i
Значение элемента массива с индексом i может быть записано как
arr[i], что эквивалентно записи *(parr+i) или *(arr+i)
Все индексные выражения вида arr[i] компилятор преобразует к адресному представлению в соответствии с правилами выполнения операции сложения указателя с целой величиной:
arr[i]=*(arr+i) =* (arr + i* sizeof(int))
ВАЖНО.
Указатель – это переменная, и ее значение можно изменять в программе, например, можно записать:
parr++;
Операция parr++ означает сдвиг указателя на следующий элемент массива arr++, то есть на 2 байта. Если, например, до выполнения операции parr++ указатель был установлен на первый элемент массива, то в результате выполнения этой операции он окажется установленным на второй элемент:
arr[0] | arr[1] | arr[2] | arr[3] | arr[4] |
Имя массива – константа. Значение адреса, которое хранит имя массива, изменять нельзя, поэтому ошибочной является следующая запись:
arr++;
Пример 4. Программа, в которой демонстрируется использование указателей на массив.
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <time.h>
#define N 100 //максимально допустимое количество элементов в массиве
int main(void)
{
int a[N]; /* резервирование памяти под N элементов массива */
int i; //переменная, управляющая циклом
int *pa=a; //объявление указателя на данное целого типа
//и инициализация указателя адресом массива а
clrscr();
randomize();
//цикл заполнения массива случайными числами
for(i=0; i<N; i++)
*(pa+i) = random(101)-50; // присваивание элементу массива значения случайного
// числа из диапазона от -50 до 50
//цикл вывода элементов массива
printf("\n\n Массив выведен по 10 элементов в строке \n\n");
for(i=0; i<N; i++)
{
printf("%6d ", *(pa+i)); // вывод массива
if(i%10==9)
printf("\n");
}
getch();
return 0;
}
Пример 4. Программа, в которой организован ввод элементов массива в диалоговом режиме и вывод полученного массива. Используется указатель на массив.
#include <stdio.h>
#include <conio.h>
void main(void)
{
int i;
int a[10]; //объявление массива целых из десяти элементов
int *pa = a; //объявление указателя на данное целого типа и инициализация указателя
// адресом массива
clrscr();
//цикл ввода элементов массива в диалоговом режиме
for(i=0; i<10; i++)
{
printf("\nВведите элемент массива с индексом %d ",i);
scanf("%d", pa+i); //функция scanf получает адрес элемента массива
}
printf("\n\n Вы ввели массив \n\n");
for(i=0; i<10; i++)
printf("%4d ", *(pa+i)); // вывод массива
getch();
}
Пример 5.Программа, в которой используются указатели на строки.
#include <stdio.h>
void main(void)
{
char s[]="строка как массив";
char *ps=s; //указатель ps инициализирован значением адреса строки s
char *ps1,*ps2,*ps3; //объявление указателей на строки
//присвоение указателю ps1 адреса строковой константы
ps1="В языке си в одну\n"
"строку можно записать\n"
"целое стихотворение\n";
//присвоение указателю ps2 адреса строковой константы
ps2="предложение не будет\
перенесено на следующую строку";
//присвоение указателю ps3 адреса строковой константы
ps3="одна"
" строка";
//цикл вычисления длины строки s, адрес которой хранит указатель ps
// выполнять, пока не найден конец строки
while(*ps!='\0')
ps++;
printf("длина строки= %d\n",ps-s);
puts(ps1);
puts(ps2);
puts(ps3);
}