Наиболее важные операции с указателями: операция обращения по адресу "*" (называется также разыменованием указателя, разадресацией, операцией косвенной адресации) и операция определения адреса "&". Операция обращения по адресу служит для доступа к данным описанного типа, т.е. для присвоения и считывания данных.
Пример:
int*x; int d; // не путать разадресацию со звёздочкой при объявлении указателя
int у = *х // у = 4;
В первой строке переменной-указателю х присваивается адрес, где хранится значение переменной d во второй строке переменной у присваивается разыменованное значение указателя х, т.е. значение 4. Если первую операцию написать без адреса, т.е. х = d (а х объявлен как int*x), то х будет указывать на ячейку памяти с адресом??:4 (?? - в зависимости от модели памяти) и значение переменной у будет равно значению,которое хранится по указанному адресу.
Операция определения адреса & возвращает адрес памяти своего операнда (переменной, константы, функции и т. д.). Формат получения адреса следующий:
<адрес> = & <переменная>;
Пример: int x, *y; y = &a; // указатель y содержит адрес х
Кроме того, указатели можно присваивать друг другу, если они одного типа.
Пример: int a, *x, *y; x =y;
Всем указателям для инициализации можно присваивать константу NULL, при этом гарантируется, что этот адрес не совпадает ни с каким другим адресом, зарезервированным системой. Операции с указателями будут также рассмотрены при работе с массивами.
Указатели и целые величины
Выражение целого типа может складываться и вычитаться из переменной типа указатель. Два указателя на объекты одного и того же типа могут вычитаться; в этом случае результат имеет целый тип.
|
char*a = "Slovo", c;
c = *(a+3); // c = 'v'
char*a, *b = "ABCDE";
a = b + 2; c = *(- - a;
char k = *a; int d = b-a)
Динамическое размещение указателей в памяти
Чтобы не вызвать конфликт в операционной системе или не нарушить работу приложения, нужно выделять место для указанного типа данных в допустимом пространстве памяти, которое называется «heap » или «куча».
Выделение (если есть возможность) памяти и присвоение её адреса указателю, по которому можно работать с описанным типом осуществляется библиотечной функцией malloc () или alloc(), (заголовочный файл <mem.h>), которую необходимо предварительно включить в программу директивой # include. Эти функции при последующих выделениях памяти предотвращают конфликты между указателями.
Формат использования данной функции:
<указатель> = (<тип_указателя>*) malloc (<размер_выделяемой_памяти_байт>);
Указатель на один тип может быть преобразован в указатель на другой тип. При этом обеспечивается возможность преобразования указателя на объект данного размера в указатель на объект меньшего размера в указатель на объект меньшего размера и обратного без изменений.
Например, функция динамического распределения памяти может воспринимать размер (в байтах) объекта, для которого выделяется память, и возвращать указатель на тип void
double*dp;
d = (double*) malloc (sizeof(double)); // функция malloc () выделяет память
free dp: *dp = 22. / 7.0 // освобождение динамических выделений памяти.
При использовании функции обеспечивается преобразование возвращаемого значения из указателя на тип void в указатель на тип double.
Пример
|
/* ЗАНЯТИЕ N 8
Выполнил студент группы......... Петров Ю.В.
Применение указателей при работе с переменными
различных типов, изменение значений переменных по адресу
и по ссылке. Примеры операций с указателями */
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <alloc.h>
int a; //Объявление глобальной переменной типа int
int main(void)
{char c1,c2,buf[20]; //buf-указатель на тип char
char *pc;
char *pst="\"slovo\"";//Объявление указателей на тип char
int *pi= &a;
float *pf, f=26.6; //Объявление указателя на тип float
double *pd, d; //Объявление указателя на тип double
double &sd= d; //Объявление ccылки на тип double
void *pv; //Объявление указателя на тип void
char *pchar=(char *)malloc(20);//Выделение памяти в куче
clrscr(); // Объявление функции void *malloc(size_t size);
pc=&c1; pf=&f; pd=&d;
printf(" Ввод переменной c1 типа char: ");
scanf("%c",pc); //Функция ввода данных, & - операция взятия
//адреса отсутствует
printf(" Вывод переменной c1 типа char: ");
printf("%c\n",*pc);
fflush(stdin);
pc=&c2;
printf(" Ввод переменной c2 типа char: ");
scanf("%c",pc); //Функция ввода данных, & - операция взятия
//адреса отсутствует
printf(" Вывод переменной c2 типа char: ");
printf("%c\n",*pc);
printf("\n Ввод переменной (a) типа int: ");
scanf("%d",pi);
a+=5; ++*pi; //Изменение а = а+5+1
printf(" \t Вывод (a) после изменения а=а+5+1\n");
printf(" Формат вывода (int): +6d #6o #8x\n");
printf("\t\t |%+6d|%#6o|%#8x|\n ",a,*pi,*pi);
printf("\n Вывод исходной cтроки: %s\n",pst);
pc=pst;
printf(" Вывод cтроки в цикле:\n");
while(*pc!='\0') { printf(" %s",pc); pc++; }
printf("\n Ввод cтроки в массив: ");
scanf("%s",buf);
pv=buf; //Использование указателя (pv) на тип void
printf(" Вывод cтроки из массива: %s\n",(char *)pv); //pv -void
|
printf(" Ввод cтроки в динамическую память: ");
scanf("%s",pchar);
printf(" Вывод cтроки из динамической памяти: %s\n",pchar);
printf(" Ввод переменных типа float and double (через пробел):\n");
printf("\t\t ");
scanf("%f %lf",pf,pd);
pv=pf; //Использование указателя (pv) на тип void
*pf=*(float *)pv*10; //f*=10;
*pd=sd+*(float *)pv; //Использование ccылки (sd) на тип double
pv=pd; //d+=f;
printf("\t Вывод результатов расчета f*=10 d+=f\n");
printf(" Формат вывода (float): 10.6f 10.6e +10.6g\n");
printf("\t\t |%10.6f|%10.6e|%+10.6g|\n",f,*pf,*pf);
printf(" Формат вывода (double): 10.8lf 10.8e 10.8g\n");
printf("\t\t |%10.8lf|%10.8e|%+10.8g|\n ",*pd,*(double *)pv,sd);
getche();
return 0;
}
/* Ввод переменной c1 типа char: w
Вывод переменной c1 типа char: w
Ввод переменной c2 типа char: t
Вывод переменной c2 типа char: t
Ввод переменной (a) типа int: 40
Вывод (a) после изменения а=а+5+1
Формат вывода (int): +6d #6o #8x
| +46| 056| 0x2e|
Вывод исходной cтроки: "slovo"
Вывод cтроки в цикле:
"slovo" slovo" lovo" ovo" vo" o" "
Ввод cтроки в массив: unsigned
Вывод cтроки из массива: unsigned
Ввод cтроки в динамическую память: dinamo
Вывод cтроки из динамической памяти: dinamo
Ввод переменных типа float and double (через пробел):
1.5 20.4
Вывод результатов расчета *pf=*pf*10; *pd=*pd+f;
Формат вывода (float): 10.6f 10.6e +10.6g
| 15.000000|1.500000e+01| +15|
Формат вывода (double): 10.8lf 10.8e 10.8g
|35.40000000|3.54000000e+01| +35.4|
*/
Ход работы
1 Изучить теоретические сведения.
2 В соответствии с индивидуальным заданием разработать алгоритм применения указателей.
3 Разработать программу, содержащую указатели на скалярные типы данных, показать использование указателей в арифметических операциях.
4 Набрать программу на компьютере и устранить ошибки.
5.Получить результат.
6 Оформить отчет и сделать выводы по работе.
7 Подготовиться к защите лабораторной работы, изучив контрольные вопросы по данной теме.
Индивидуальное задание к лабораторной работе №8
Присвоить раз именованному указателю на тип Р1 значение арифметического выражения АВ включающего указатели на типы Р2 и Р3. Арифметическое выражение реализовать в виде функции возвращающей указатель на тип Р1. Вывести на экран значение указателя Р2 и значение на которое он ссылается. Индивидуальные задания взять из таблицы 8.1.
Таблица 8.1 - индивидуальные задания
Вариант | Р1 | АВ | Р2 | Р3 |
long | (1/sin((р2)2))р3 | int | float | |
float | (abs(p3))1/p2 | long | double | |
double | tan((p3)2)p2/3 | int | long | |
float | (ln(p2)p3)p2 | char | unsigned long int | |
long double | sin(abs(p3)p2) | double | long int | |
long | sin(p2)/tan(p3) | int | float | |
unsigned long int | (++p3)/(--p2) | short int | int | |
long double | ((1+(++p2))/p3)p2 | long int | float | |
signed long int | (sin(--p2)-(p3))p3 | char | int | |
long int | (1/sin(p2))p3 | unsigned long int | int | |
double | sin(p3)1/p2 | double | float | |
double | cos(p2/p3) | int | double | |
int | (--p2)+(++p3) | unsigned int | short int | |
signed int | (sin(p2)/tan(p3))p3 | short int | char | |
long double | ln(--p2)1/p3 | float | double | |
double | 1.2*(10-(--p3))+p3 | double | short int | |
double | tan((p3)2)p2/3 | double | float | |
float | (ln(p2)p3)p2 | int | double | |
long double | sin(abs(p3)p2) | unsigned int | short int | |
long double | ((1+(++p2))/p3)p2 | double | long int | |
signed long int | (sin(--p2)-(p3))p3 | int | float | |
long int | (1/sin(p2))p3 | short int | int | |
long double | sin(abs(p3)p2) | char | int | |
long | sin(p2)/tan(p3) | unsigned long int | int | |
double | sin(p3)1/p2 | double | float | |
double | cos(p2/p3) | int | double | |
int | (--p2)+(++p3) | unsigned int | short int | |
long double | ((1+(++p2))/p3)p2 | short int | char | |
signed long int | (sin(--p2)-(p3))p3 | float | double | |
long int | (1/sin(p2))p3 | double | short int |