Вторник, 19.03.2024, 12:33
Приветствую Вас, Гость |
Меню сайта
Наш опрос
Нужен ли форум на этом сайте?
Всего ответов: 1296
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

Указатели в СИ

Указатель – это переменная, значением которой является адрес, по которому располагаются данные. Адрес – это номер ячейки памяти, в которой или с которой располагаются данные.
 Классифицировать указатели в СИ можно:
  •по типу данных;
  •по области доступа.

 По типу данных в СИ указатели делятся на:
  Типизированный указатель – указатель, содержащий адрес данных определенного типа (системного или пользовательского).
  Не типизированный указатель – указатель, содержащий адрес данных неопределенного типа (просто адрес).

 По области доступа указатели в СИ делятся на:
  Ближний указатель – указатель, содержащий только смещение, по которому располагаются данные.Сегмент в этом случае используется по умолчанию – текущий сегмент данных. Размер ближнего указателя в 16-разрядном реальном режиме работы процессора составляет 16 бит, а в 32-разрядном защищенном режиме – 32 бита.
  Дальний указатель – указатель, содержащий и сегмент и смещение. Размер дальнего указателя в 16-разрядном реальном режиме работы процессора составляет 32 бита (16 бит – сегмент, 16 бит - смещение), а в 32-разрядном защищённом режиме – 48 бит (16 бит – селектор, 32 бита - смещение).

Работа с указателями в языке СИ включает три действия, осуществляемых в следующем порядке:
  объявление указателя;
  установка указателя;
обращение к значению, расположенному по указателю. Объявление (описание) указателя в языке СИ имеет следующий вид:
  тип [near|far] *имя [=значение];

 В современной реализации языка СИ (стандарт C99), ориентированной под разработку программ для ОС Windows, вследствие используемой в ОС Windows модели памяти, используются исключительно ближние (near) указатели, поэтому при объявлении указателя (например, в среде разработки Pelles C) модификатор области доступа указывать не надо.

Указатель в СИ при объявлении можно инициализировать, указав через знак присвоения соответствующее значение. Данное значение должно быть адресом, записанном в одном из следующих виде:
  •нулевое значение (идентификатор NULL);
  •другой указатель;
  •адрес переменной (через операцию взятия адреса);
  •выражение, представляющее собой арифметику указателей;
  •адрес, являющийся результатом выделения динамической памяти.

 Операция взятия адреса – операция языка СИ, возвращающая адрес переменной. Данная операция имеет следующий синтаксис:
  &имя_переменной
 Например, в программе описаны следующие переменные:
  int a,b;
  double c;
 Описание указателей на эти переменные с инициализацией будет иметь вид:
  int *ptr_a = &a, *ptr_b = &b;
  double *ptr_c1 = &c, *ptr_c2 = ptr_c1;
 Пример объявления не типизированного указателя с инициализацией нулевым значением:
  void *ptr = NULL;
 Установка указателя - присвоение его значению адреса, по которому располагаются или будут располагаться данные. Для установки указателя используется оператор присвоения, в левой части которого указывается имя указателя, а в правой – одно из значений отличных от NULL, используемых при инициализации указателя. Пример установки указателей:
  int a = 10, *ptr = NULL;
  ptr = &a;

 Для обращения к значению, располагаемому по адресу, содержащемуся в указателе, используется операция разыменования указателя. Данная операция имеет следующий синтаксис:
  *имя_указателя

 Значение, полученное путём разыменования указателя, может рассматриваться в программе как LValue, так и RValue значение. Например:
  double x = 0.0, *ptr = NULL;
  ptr = &x;
  scanf("%lf”,ptr);
  *ptr += 1.5;
  printf("%lf\n”,*ptr);
 В языке СИ можно создавать константные указатели – значение, расположенное по этому указателю нельзя изменить. Создание такого указателя имеет следующий синтаксис:
  const тип *имя = инициализирующее значение;
 Например, следующий фрагмент программы на СИ приведет к ошибке компиляции:
  int a = 10;
  const int *ptr = &a;
 (*ptr)++;
 В СИ объявление указателя на массив имеет тот же синтаксис, что и объявление обычного указателя. Например, объявление указателя на вещественный массив типа double будет иметь вид:
 double *arrptr = NULL;
 Объявление целочисленного массива из десяти элементов с инициализацией нулевыми значениями, и объявление с инициализацией указателя на этот массив будут иметь вид:
  int arr[10] = {0}, *arrptr = arr;

 Фрагмент программы, в которой объявляется массив из 10 элементов целого типа, осуществляется ввод массива с вычислением суммы его элементов и вывод значения этой суммы с использованием указателей на массив и на переменную для хранения суммы:
  int array[10] = {0}, summa = 0;
  int *arrptr = array, *ptr = &summa;
  for(int i=0;i<10;i++)
  {
   scanf("%d",&arrptr[i]);
   *ptr += arrptr[i];
  }
  printf("Сумма: %d\n”,*ptr);

 Довольно часто встречаются случаи, когда необходимо работать с массивами указателей. Синтаксис объявления массива указателей следующий:
  тип *имя[размер];

 Например: вычисление суммы набора целых чисел через обращение к ним посредством массива указателей на целые числа:
  int arr[10], *ptrs[10], summa = 0;
  for(int i=0;i<10;i++) ptrs[i] = &arr[i];
  for(int i=0;i<10;i++)
  {
   scanf("%d”,ptrs[i]);
   summa+= *ptrs[i];
  }
  printf("Сумма: %d\n”,summa);

Строки и указатели в СИ

 Объявление указателя на строку имеет тот же синтаксис, что и объявление указателя на символьный тип данных языка СИ:
  char *имя;

 Так как в языке СИ строка является массивом символов, а имя массива есть указатель на этот массив, то установка указателя на строку осуществляется путём присвоения указателю имени этой строки.
Например:
  char str[] = "Моя строка!", *ptr = str;

 Работа со строкой как с массивом символов посредством указателя ничем не отличается от работы с массивом. Например, ниже приведён фрагмент программы вычисления длины строки str посредством обращения к ней через указатель ptr:
  char *ptr = str;
  int len = 0;
  while(ptr[len]!=0) len++;
  printf("Длина строки: %d\n”,len);

 Интересной является возможность объявления указателей на строки и их установка на строковые
константы. Например, возможно следующее:
  char *str = "Моя строка!";
  puts(str);

  Например, в следующем фрагменте программы на экран выводится сообщение "Положительное
значение", если значение целочисленной переменной a больше нуля, "Отрицательное значение" – если меньше и "Нулевое значение" – если ноль:
  int a = 0;
  printf("Введите число: "); scanf("%d",&a);
  char *str = NULL;
  if(a > 0) str = "Положительное значение”;
   else if(a < 0) str = "Отрицательное значение”;
    else str = "Нулевое значение”;
  puts(str);
Перечисления и указатели в СИ

 Работа с указателями на перечислимый тип данных (enum) ничем не отличается от работы с указателями на целочисленный тип данных, так как перечислимый тип данных является производным от целочисленного типа.
Структуры указатели в СИ

 Объявление указателя на структуры или объединения, а также установка указателя на структуры и объединения синтаксически не отличается от соответствующих действий с указателями на скалярные
типы данных. Например:
  typedef struct {double x,y;} Point2D;
  Point 2D pnt = {0.0,0.0}, *ptr = &pnt;

 Отличие заключается в обращении к полям структуры (объединения) через указатели на эти структуры (объединения). Возможны два варианта:
  •(*имя_указателя).имя_поля
  •имя_указателя->имя_поля
 Вычислить расстояние между двумя точками в двумерном пространстве (структура Point2D):
  Point2D pnt[2], *ptr1 = &pnt[0], *ptr2 = &pnt[1];
  printf("Введите первую точку: ");
  scanf("%lf %lf",&ptr1->x,&ptr1->y);
  printf("Введите вторую точку: ”);
  scanf("%lf %lf",&ptr2->x,&ptr2->y);
  double len = sqrt(
  pow(ptr1->x - ptr2->x, 2.0)+pow(ptr1->y - ptr2->y, 2.0));
  printf("Расстояние: %lf\n",len);

 Вычисление длины ломаной линии заданной массивом структур arr (структура Point2D) размера N через указатель ptr на этот массив:
  Point2D arr[N] = {...}, *ptr = arr;
  double len = 0.0;
  for(int i=1;i<N;i++)
   len += sqrt(pow(ptr[i].x - ptr[i-1].x, 2.0)+pow(ptr[i].y - ptr[i-1].y, 2.0));
  printf("Длина ломаной линии: %lf\n",len);

 Вычисление длины ломаной линии заданной массивом структур arr (структура Point2D) размера N через массив указателей ptr на на элементы исходного массива:
  Point2D arr[N] = {...}, *ptrs[N];
  for(int i=0;i<N;i++) ptrs[i] = &arr[i];
  double len = 0.0;
  for(int i=1;i<N;i++)
   len += sqrt(pow(ptrs[i]->x - ptrs[i-1]->x, 2.0)+pow(ptrs[i]->y - ptrs[i-1]->y, 2.0));
  printf("Длина ломаной линии: %lf\n",len);

 Арифметика указателей В языке СИ доступны некоторые арифметические действия над типизированными указателями в СИ.
 Доступны следующие виды выражений:
  •указатель++; ++указатель;
  •указатель--; --указатель;
  •указатель = указатель + (целочисленное выражение);
  •указатель += (целочисленное выражение);
  •указатель = указатель - (целочисленное выражение);
  •указатель -= (целочисленное выражение);

 Технически сложение (или вычитание) типизированного указателя и целого числа означает «сдвиг» указателя на определённое число байт (в зависимости от размера типа указателя) «вправо» (или «влево»).
Примеры:
  int *a, *b, *c; //Объявление указателей на int
  double *x, *y;
  ... //Установка указателей
  a++; //Сдвиг вправо на 4 байта (размер int)
  b-=3; //Сдвиг влево на 12 байт ( (размер int)*3 )
  c=a+2; //Смещение с относительно a на 8 байт (размер double)
  y = x--; //Х смещается влево на 8 байт (размер double)

 В языке СИ арифметика указателей наиболее часто применяется для доступа к элементам массивов. Например,вычисление суммы элементов целочисленного массива:
  int arr[10] = {...}, *ptr = NULL, summa = 0;
  ptr = arr; //или ptr = &arr[0];
  for(int i=0;i<10;i++,ptr++) summa +=*ptr;
   printf("Сумма: %d\n", summa);

 Цикл в последнем фрагменте программы можно записать и несколько иначе:
  for(int i=0;i<10;i++) summa += *(ptr+i);

 Такой подход не рекомендуется так как снижает читаемость программы. Еще одним вариантом арифметики указателей является вычитание указателя из другого указателя, в виде: целочисленная переменная = указатель №1 – указатель №2;

 Результатом вычитания указателей является целое значение равное расстоянию между адресами, содержащимися в указателях.
Например:
  int arr[10], *ptr1 = arr, *ptr2 = &arr[1], *ptr3 = &arr[4]; //объявление массива и указателей
  int dest1 = ptr2 - ptr1, dest2 = ptr2 - ptr3;
  printf("%d\n%d\n",dest1,dest2);

 На экране будет выведено:
  1
  -3
Далее статья о функциях в СИ.
Форма входа
Поиск
Мы в сети
Реклама
Для того чтобы не видеть рекламу в правом верхнем углу сайта пройдите простую процедуру регистрации
ФОРУМ
У нас наконецто появился форум! Добро пожаловать! Будьте первыми, задайте направление форуму! =)
--- Не стесняемся - заходим на форум! ---