Структуры и алгоритмы компьютерной обработки данных

Производные типы. Тип указатель: указатели на объекты

Разбить на страницы
Показывать лекцию целиком

Цель лекции: изучить классификацию производных типов, тип указатель и соотношения между именами, адресами и значениями переменных, научиться использовать указатели в программных кодах на языке C++.

В языке С++ разрешено наряду со стандартными использование производных типов, полученных на основе более простых базовых типов. Производные типы можно условно подразделить на две группы:

Непосредственно производные типы. Эти типы являются производными от некоторых существующих типов, реализуя типы указателей, ссылки, функции преобразования типов. В группу непосредственно производных типов входят:

  • массивы;
  • указатели;
  • ссылки;
  • перечисления.
  • Составные производные типы. В группу составных производных типов входят типы, являющиеся производными от различных существующих или ранее объявленных типов:

  • классы;
  • структуры;
  • объединения.
  • Переименование типов

    В некоторых программных кодах бывает удобно вводить новые обозначения имен отдельных используемых типов данных. Задавать новое имя типу можно с помощью ключевого слова typedef.

    Синтаксис:

    typedef Тип НовоеИмяТипа[Размерность];

    Например:

    typedef unsigned int UNIT;
    typedef char Msg[100];

    Такое имя можно затем использовать так же, как и стандартное имя типа:

    UNIT a,b,c;//переменные типа unsigned int
    Msg str[10];//массив из 10 строк по 100 символов

    Рассмотрим тип указатель. Указатели являются специальными объектами в программах на С++. Они предназначены для хранения адресов памяти.

    Рассмотрим пример (рис 4.1). Когда компилятор обрабатывает оператор определения переменной, например, int a=10;, то в памяти выделяется участок памяти в соответствии с типом переменной и записывается в этот участок указанное значение (размер типа int не менее 2 байтов и зависит от реализации). Все обращения к переменной a компилятор заменит на адрес области памяти, в которой хранится эта переменная. Операция a является операцией взятия адреса ее операнда.

    (рис 4.1) Адресация в С++

    Программист может определить собственные переменные для хранения адресов областей памяти. Такие переменные называются указателями.

    Указатель – именованный объект, предназначенный для хранения адреса области памяти (объекта, непоименованной области оперативной памяти либо точки входа в функцию).

    Указатель не является самостоятельным типом, он всегда связан с каким-то другим типом. Указатели делятся на две категории:

  • указатели на объекты;
  • указатели на функции.
  • Эти категории указателей отличаются друг от друга свойствами и правилами манипулирования. Каждый указатель имеет соответствующий тип.

    Указатели на объекты

    В общем случае синтаксис определения указателя на объект:

    Тип*Описатель;

    При определении указателя специфицируется имя указателя-переменной (в дальнейшем указатель) и тип объекта, на который он ссылается.

    Тип задает тип объекта, адрес которого будет содержать определяемая переменная и может соответствовать базовому, пустому (свободному, родовому, то есть типу void ), перечислению, структурному типу и типу объединения. Реально указатель на void ни на что не указывает, но обладает способностью указывать на область любого размера после его типизирования каким-либо объектом.

    Описатель – это идентификатор, определяющий имя объявляемой переменной типа указатель или конструкция, которая организует непосредственно доступ к памяти. Описателю обязательно должна предшествовать звездочка (*).

    Знак '*' является унарной операцией косвенной адресации, его операнд – указатель, а результат – адрес объекта, на который указывает операнд. Адресация является косвенной, так как обращение к области памяти осуществляется не напрямую по адресу (например, 1А2В), а через объект, которому в памяти соответствует определенный участок. Объем памяти, который выделяется для хранения данных, определяется типом данных и моделью памяти. Для приведенной на рисунке 2 модели памяти адресом переменной типа float с именем summa является 0012FF48, адресом переменной типа int с именем date является 0012FF54, адресом переменной типа char с именем ch является 0012FF63.

    (рис 4.2) Адресация типов в С++

    Примеры определения указателей:

    int *P;
      /*указатель Р может содержать адрес объекта типа int*/
    float *s;
      /*указатель s может содержать адрес объекта типа float*/

    Синтаксис объявления указателя на объект базового типа:

    Тип*ИмяУказателя;

    где ИмяУказателя – идентификатор.

    Например,

    char *s; //переменная s – указатель на объект типа char
    double *x; /*переменная х – указатель на объект типа 
                 double, вещественного числа с плавающей 
                 точкой удвоенной точности*/
    int *k, *ff; //k, ff – указатели на объекты целого типа
    int *p, y; /*р – указатель на объект типа int, 
    y – целочисленная переменная и не является
        указателем*/
    int x, *p; /*х – целочисленная переменная и не является
                     указателем,
    р – указатель на объект типа int*/

    Указатель может быть константой или переменной, а также указывать на константу или переменную.

    Например:

    int i;//целая переменная 
    const int ci=1; //целая константа
    
    int *pi; //указатель на целую переменную
    const int *pci; //указатель на целую константу
    
    int *const cpi; //указатель-константа на целую переменную
    const int *const cpc;
                    //указатель-константа на целую константу

    При объявлении указателя его можно сразу проинициализировать (задать значение):

    int *pi=i; //указатель на целую переменную
    const int *pci=ci; //указатель на целую константу
    
    int *const cpi=i; 
                //указатель-константа на целую переменную
    const int *const cpc=ci; 
               //указатель-константа на целую константу

    Если модификатор const относится к указателю (т.е. находится между именем указателя и *), то он запрещает изменение значения указателя, а если он находится слева от типа (т.е. слева от *), то он запрещает изменение значения, на которое указывает указатель.

    Способы инициализации указателя

  • Присваивание указателю адреса области памяти существующего объекта:
  • с помощью операции получения адреса:
    int a=5; 
    int *p=a;
  • с помощью проинициализированного указателя
    int *r=p;
  • Присваивание указателю адреса области памяти в явном виде:
    char *cp=(char*)0х В800 0000;
    где В800 0000 – шестнадцатеричная константа, ( char* ) – операция приведения типа.
  • Присваивание указателю пустого значения:
    int *N=NULL; или int *N=0;
  • Спецификатор указателя при форматированном выводе

    Если на экран необходимо вывести адрес, следует применять спецификатор %p.

    %pспецификатор указателя.

    Этот спецификатор формата заставляет функцию printf() выводить на экран адрес, формат которого совместим с типом адресации, принятой в компьютере.

    Операции с указателями

    С указателями можно выполнять следующие операции:

  • разыменование (*) – получение значения величины, адрес которой хранится в указателе;
  • взятие адреса ();
  • присваивание;
  • арифметические операции
  • сложение указателя только с константой,
  • вычитание: допускается разность указателей и разность указателя и константы,
  • инкремент (++) увеличивает значение указателя на величину sizeof(тип);
  • декремент (--) уменьшает значение указателя на величину sizeof(тип);
  • сравнение;
  • приведение типов.
  • Пример 1. Демонстрация ситуации, когда указатели различных типов указывают на одно и то же место в памяти. Однако при разыменовании получаются разные результаты.

    // Выбор данных из памяти с помощью разных указателей
    // Использование функций приведения типов
    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    int _tmain(int argc, _TCHAR* argv[]){
      unsigned long L=12345678;
      char *cp=(char*)L;
      int *ip=(int*)L;
      long *lp=(long*)L;
    
      cout <<"\nL = "<<L; 
      cout <<"\nL = "<<L;  
      cout <<"\n*cp = "<<*cp; 
      cout <<"\n*ip = "<<*ip;
      cout <<"\n*lp = "<<*lp;
      
      system("pause");
      return 0;
    }

    Пример 2.

    //Операции над указателями
    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[]){
      int a,c,b;
      int  *ca, *cb;
      int *sa, *sb, *sc;
      cout << "a = "; cin >> a;
      cout << "b = "; cin >> b;  
      c=a+b;
    
      sb=b;//инициализация указателей через взятие адреса
      sa=a;
      sc =c;
      //присваивание указателю значения выражения
      *sc = c+a-b;
      ca=sa;//присваивание указателю значения другого указателя
      cb=sb;
      *sa=12;//присваивание указателю значения константы
    
      cout << "\n*ca = " << *ca;
      cout << "\n*sa = " << *sa; 
      cout << "\n*cb = " << *cb; 
      cout << "\n*sb = " << *sb;
      cout << "\n*sc = " << *sc;
    
      cout << "\nca = " << ca; 
      cout << "\ncb = " << cb;
      cout << "\nsc = " << sc;
    
      cout << "\na = " << a;
      cout << "\nb = " << b;
      cout << "\nc = " << c;
    
      cout << "\na = " << a; 
      cout << "\nb = " << b;
      cout << "\nc = " << c;
    
      cout << "\n*a = " << *a;  
      
      cout << "\n*cb-*ca = " << *cb-*ca;
      cout << "\n*cb+*ca = " << *cb+*ca;
      *cb+=2; //сложение с константой
      cout << "\ncb = " << cb;
      cb++; //инкремент
      cout << "\ncb = " << cb;
      ca--; //декремент
      cout << "\ncа = " << ca;
      cout << "\ncb-ca = " << cb-ca; 
      system("pause");
      return 0;
    }

    Указатели одного и того же типа можно сравнивать с помощью стандартных операций сравнения. При этом сравниваются значения указателей, а не значения величин, на которые данные указатели ссылаются.

    Пример 3.

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    int _tmain(int argc, _TCHAR* argv[]){
      int x=10;
      int y=10;
      int *xptr=x;
      int *yptr=y;
      
    //сравниваем указатели
      if (xptr == yptr) 
        cout << "Указатели равны\n";
         else  
        cout << "Указатели неравны\n";
    
    //сравниваем значения, на которое указывает указатель
      if (*xptr == *yptr) {
        cout << "Значения равны\n";
        } else { 
        cout << "Значения неравны\n";}
      system("pause");
      return 0;
    }

    В приведенном примере результат первой операции сравнения будет ложным, а второй – истинным, поскольку переменные x и y имеют одно и то же значение.

    Ключевые термины

    Адрес объекта – это адрес области оперативной памяти, по которому хранится объект в соответствии с особенностями представления типа.

    Инициализация указателя – это определение значения указателя.

    Косвенная адресация – это обращение к области памяти не напрямую, по адресу, а через объект, которому в памяти соответствует определенный участок.

    Непосредственно производные типы – это типы, которые являются производными от некоторых существующих типов, реализуя типы указателей, ссылки, функции преобразования типов.

    Переименование типов – это задание нового имени для существующего типа.

    Производные типы данных – это типы, полученные на основе более простых базовых типов.

    Разыменование – это операция получения значения объекта, адрес которого хранится в указателе;

    Составные производные типы – это типы, являющиеся производными от различных существующих или ранее объявленных типов.

    Указатель – это именованный объект, предназначенный для хранения адреса области памяти.

    Указатель на константу – это указатель на такой объект, значение которого нельзя изменить в процессе выполнения программы.

    Указатель-константа – это указатель, значение которого нельзя изменить в процессе выполнения программы.

    Указатель-константа на константу – это указатель, для которого невозможно изменение как самого указателя, так и значения адресуемого объекта.

    Краткие итоги

  • В языке С++ производные типы данных классифицируют в зависимости от построения на производные и непосредственно производные.
  • Для экономии памяти и времени, затрачиваемого на обращение к данным, в программах используют указатели на объекты.
  • Указатель не является самостоятельным типом, он всегда связан с другим типом.
  • Указатель может быть константой или переменной, а также указывать на константу или переменную.
  • Указатель типа void указывает на область памяти любого размера. Разыменование такого указателя необходимо проводить с операцией приведения типов.
  • До первого использования в программе объявленный указатель необходимо проинициализировать.
  • С помощью указателей можно выполнять косвенную адресацию объектов.
  • Над указателями определены операции: разыменование, взятие адреса, декремент, инкремент, увеличение (уменьшение) на целую константу, разность, определение размера.
  • Над указателями определены операции сравнения.
  • Лабораторная работа 4. Производные типы. Тип указатель: указатели на объекты.

    Цель работы: изучить классификацию производных типов, тип указатель и соотношения между именами, адресами и значениями переменных, научиться использовать указатели в программных кодах на языке C++.

    При выполнении лабораторной работы для каждого задания требуется написать программу на языке С++, которая получает на входе данные различных типов (в соответствии с условиями задач), выполняет их обработку в соответствии с требованиями задания и выводит результат на экран. Ввод данных осуществляется с клавиатуры с учетом требований к входным данным, содержащихся в постановке задачи. Ограничениями на входные данные является допустимый диапазон значений используемых числовых типов в языке С++.

    Теоретические сведения.

    Ознакомьтесь с материалом лекции 4.

    Задания к лабораторной работе.

    Выполните приведенные ниже задания.

  • В программе определите и инициализируйте переменную типа double, указатель double * и указатель типа void *. Присвойте указателям адрес переменной. Напечатайте адрес переменной, значения указателей и значения, получаемые при разыменовании указателей. Чтобы продемонстрировать роли и последовательность выполнения унарных операций получения адреса и разыменования *, выведите на печать значение выражения *имя_переменной.
  • Задано натуральное число. Разместите в памяти последовательно все его цифры, используя указатели и операции над ними.
  • Определите и инициализируйте переменную типа double. Определите указатели char *, int *, double *, void *, инициализируйте их адресом переменной. Напечатайте значения указателей, их размеры и длины участков памяти, которые связаны с выражениями, разыменовывающими указатели.
  • Указания к выполнению работы.

    Каждое задание необходимо решить в соответствии с изученными методами работы с указателями в языке С++. В отчете следует пояснить различия в использовании указателей разных типов, адресующих один и тот же участок памяти, и отразить основные случаи использования в программе указателя типа void *.

    Следует реализовать каждое задание в соответствии с приведенными этапами:

  • изучить словесную постановку задачи, выделив при этом все виды данных;
  • сформулировать математическую постановку задачи;
  • выбрать метод решения задачи, если это необходимо;
  • разработать графическую схему алгоритма;
  • записать разработанный алгоритм на языке С++;
  • разработать контрольный тест к программе;
  • отладить программу;
  • представить отчет по работе.
  • Требования к отчету.

    Отчет по лабораторной работе должен соответствовать следующей структуре.

  • Титульный лист.
  • Словесная постановка задачи. В этом подразделе проводится полное описание задачи. Описывается суть задачи, анализ входящих в нее физических величин, область их допустимых значений, единицы их измерения, возможные ограничения, анализ условий при которых задача имеет решение (не имеет решения), анализ ожидаемых результатов.
  • Математическая модель. В этом подразделе вводятся математические описания физических величин и математическое описание их взаимодействий. Цель подраздела – представить решаемую задачу в математической формулировке.
  • Алгоритм решения задачи. В подразделе описывается разработка структуры алгоритма, обосновывается абстракция данных, задача разбивается на подзадачи. Схема алгоритма выполняется по ЕСПД (ГОСТ 19.003-80 и ГОСТ 19.002-80).
  • Листинг программы. Подраздел должен содержать текст программы на языке программирования С++, реализованный в среде MS Visual Studio 2010.
  • Контрольный тест. Подраздел содержит наборы исходных данных и полученные в ходе выполнения программы результаты.
  • Выводы по лабораторной работе.
  • Ответы на контрольные вопросы.
  • Контрольные вопросы

  • Почему указатель не может существовать как самостоятельный тип?
  • С какой целью в программе может быть использован указатель типа void?
  • Что будет являться результатом разыменования указателя типа void без приведения типов?
  • Как изменится значение указателя после применения к нему операции инкремента (декремента)?
  • Почему для указателей определены сложение и вычитание только с целыми константами?
  • В чем отличие указателя на константу от указателя-константы?
  • Два указателя разных типов указывают на одно и то же место в памяти. Сравните результаты операций разыменования и взятия адреса с такими указателям. Сравните значения указателей.
  • Если объект занимает в памяти несколько байтов, то какой адрес является значением указателя на этот объект?
  • Каким образом при разыменовании указателей становится известно, сколько байтов памяти доступно?
  • Страницы:

    Цель лекции: изучить классификацию производных типов, тип указатель и соотношения между именами, адресами и значениями переменных, научиться использовать указатели в программных кодах на языке C++.

    В языке С++ разрешено наряду со стандартными использование производных типов, полученных на основе более простых базовых типов. Производные типы можно условно подразделить на две группы:

    Непосредственно производные типы. Эти типы являются производными от некоторых существующих типов, реализуя типы указателей, ссылки, функции преобразования типов. В группу непосредственно производных типов входят:

  • массивы;
  • указатели;
  • ссылки;
  • перечисления.
  • Составные производные типы. В группу составных производных типов входят типы, являющиеся производными от различных существующих или ранее объявленных типов:

  • классы;
  • структуры;
  • объединения.
  • Переименование типов

    В некоторых программных кодах бывает удобно вводить новые обозначения имен отдельных используемых типов данных. Задавать новое имя типу можно с помощью ключевого слова typedef.

    Синтаксис:

    typedef Тип НовоеИмяТипа[Размерность];

    Например:

    typedef unsigned int UNIT;
    typedef char Msg[100];

    Такое имя можно затем использовать так же, как и стандартное имя типа:

    UNIT a,b,c;//переменные типа unsigned int
    Msg str[10];//массив из 10 строк по 100 символов

    Рассмотрим тип указатель. Указатели являются специальными объектами в программах на С++. Они предназначены для хранения адресов памяти.

    Рассмотрим пример (рис 4.1). Когда компилятор обрабатывает оператор определения переменной, например, int a=10;, то в памяти выделяется участок памяти в соответствии с типом переменной и записывается в этот участок указанное значение (размер типа int не менее 2 байтов и зависит от реализации). Все обращения к переменной a компилятор заменит на адрес области памяти, в которой хранится эта переменная. Операция a является операцией взятия адреса ее операнда.

    (рис 4.1) Адресация в С++

    Программист может определить собственные переменные для хранения адресов областей памяти. Такие переменные называются указателями.

    Указатель – именованный объект, предназначенный для хранения адреса области памяти (объекта, непоименованной области оперативной памяти либо точки входа в функцию).

    Указатель не является самостоятельным типом, он всегда связан с каким-то другим типом. Указатели делятся на две категории:

  • указатели на объекты;
  • указатели на функции.
  • Эти категории указателей отличаются друг от друга свойствами и правилами манипулирования. Каждый указатель имеет соответствующий тип.

    Указатели на объекты

    В общем случае синтаксис определения указателя на объект:

    Тип*Описатель;

    При определении указателя специфицируется имя указателя-переменной (в дальнейшем указатель) и тип объекта, на который он ссылается.

    Тип задает тип объекта, адрес которого будет содержать определяемая переменная и может соответствовать базовому, пустому (свободному, родовому, то есть типу void ), перечислению, структурному типу и типу объединения. Реально указатель на void ни на что не указывает, но обладает способностью указывать на область любого размера после его типизирования каким-либо объектом.

    Описатель – это идентификатор, определяющий имя объявляемой переменной типа указатель или конструкция, которая организует непосредственно доступ к памяти. Описателю обязательно должна предшествовать звездочка (*).

    Знак '*' является унарной операцией косвенной адресации, его операнд – указатель, а результат – адрес объекта, на который указывает операнд. Адресация является косвенной, так как обращение к области памяти осуществляется не напрямую по адресу (например, 1А2В), а через объект, которому в памяти соответствует определенный участок. Объем памяти, который выделяется для хранения данных, определяется типом данных и моделью памяти. Для приведенной на рисунке 2 модели памяти адресом переменной типа float с именем summa является 0012FF48, адресом переменной типа int с именем date является 0012FF54, адресом переменной типа char с именем ch является 0012FF63.

    (рис 4.2) Адресация типов в С++

    Примеры определения указателей:

    int *P;
      /*указатель Р может содержать адрес объекта типа int*/
    float *s;
      /*указатель s может содержать адрес объекта типа float*/

    Синтаксис объявления указателя на объект базового типа:

    Тип*ИмяУказателя;

    где ИмяУказателя – идентификатор.

    Например,

    char *s; //переменная s – указатель на объект типа char
    double *x; /*переменная х – указатель на объект типа 
                 double, вещественного числа с плавающей 
                 точкой удвоенной точности*/
    int *k, *ff; //k, ff – указатели на объекты целого типа
    int *p, y; /*р – указатель на объект типа int, 
    y – целочисленная переменная и не является
        указателем*/
    int x, *p; /*х – целочисленная переменная и не является
                     указателем,
    р – указатель на объект типа int*/

    Указатель может быть константой или переменной, а также указывать на константу или переменную.

    Например:

    int i;//целая переменная 
    const int ci=1; //целая константа
    
    int *pi; //указатель на целую переменную
    const int *pci; //указатель на целую константу
    
    int *const cpi; //указатель-константа на целую переменную
    const int *const cpc;
                    //указатель-константа на целую константу

    При объявлении указателя его можно сразу проинициализировать (задать значение):

    int *pi=i; //указатель на целую переменную
    const int *pci=ci; //указатель на целую константу
    
    int *const cpi=i; 
                //указатель-константа на целую переменную
    const int *const cpc=ci; 
               //указатель-константа на целую константу

    Если модификатор const относится к указателю (т.е. находится между именем указателя и *), то он запрещает изменение значения указателя, а если он находится слева от типа (т.е. слева от *), то он запрещает изменение значения, на которое указывает указатель.

    Способы инициализации указателя

  • Присваивание указателю адреса области памяти существующего объекта:
  • с помощью операции получения адреса:
    int a=5; 
    int *p=a;
  • с помощью проинициализированного указателя
    int *r=p;
  • Присваивание указателю адреса области памяти в явном виде:
    char *cp=(char*)0х В800 0000;
    где В800 0000 – шестнадцатеричная константа, ( char* ) – операция приведения типа.
  • Присваивание указателю пустого значения:
    int *N=NULL; или int *N=0;
  • Спецификатор указателя при форматированном выводе

    Если на экран необходимо вывести адрес, следует применять спецификатор %p.

    %pспецификатор указателя.

    Этот спецификатор формата заставляет функцию printf() выводить на экран адрес, формат которого совместим с типом адресации, принятой в компьютере.

    Операции с указателями

    С указателями можно выполнять следующие операции:

  • разыменование (*) – получение значения величины, адрес которой хранится в указателе;
  • взятие адреса ();
  • присваивание;
  • арифметические операции
  • сложение указателя только с константой,
  • вычитание: допускается разность указателей и разность указателя и константы,
  • инкремент (++) увеличивает значение указателя на величину sizeof(тип);
  • декремент (--) уменьшает значение указателя на величину sizeof(тип);
  • сравнение;
  • приведение типов.
  • Пример 1. Демонстрация ситуации, когда указатели различных типов указывают на одно и то же место в памяти. Однако при разыменовании получаются разные результаты.

    // Выбор данных из памяти с помощью разных указателей
    // Использование функций приведения типов
    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    int _tmain(int argc, _TCHAR* argv[]){
      unsigned long L=12345678;
      char *cp=(char*)L;
      int *ip=(int*)L;
      long *lp=(long*)L;
    
      cout <<"\nL = "<<L; 
      cout <<"\nL = "<<L;  
      cout <<"\n*cp = "<<*cp; 
      cout <<"\n*ip = "<<*ip;
      cout <<"\n*lp = "<<*lp;
      
      system("pause");
      return 0;
    }

    Пример 2.

    //Операции над указателями
    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[]){
      int a,c,b;
      int  *ca, *cb;
      int *sa, *sb, *sc;
      cout << "a = "; cin >> a;
      cout << "b = "; cin >> b;  
      c=a+b;
    
      sb=b;//инициализация указателей через взятие адреса
      sa=a;
      sc =c;
      //присваивание указателю значения выражения
      *sc = c+a-b;
      ca=sa;//присваивание указателю значения другого указателя
      cb=sb;
      *sa=12;//присваивание указателю значения константы
    
      cout << "\n*ca = " << *ca;
      cout << "\n*sa = " << *sa; 
      cout << "\n*cb = " << *cb; 
      cout << "\n*sb = " << *sb;
      cout << "\n*sc = " << *sc;
    
      cout << "\nca = " << ca; 
      cout << "\ncb = " << cb;
      cout << "\nsc = " << sc;
    
      cout << "\na = " << a;
      cout << "\nb = " << b;
      cout << "\nc = " << c;
    
      cout << "\na = " << a; 
      cout << "\nb = " << b;
      cout << "\nc = " << c;
    
      cout << "\n*a = " << *a;  
      
      cout << "\n*cb-*ca = " << *cb-*ca;
      cout << "\n*cb+*ca = " << *cb+*ca;
      *cb+=2; //сложение с константой
      cout << "\ncb = " << cb;
      cb++; //инкремент
      cout << "\ncb = " << cb;
      ca--; //декремент
      cout << "\ncа = " << ca;
      cout << "\ncb-ca = " << cb-ca; 
      system("pause");
      return 0;
    }

    Указатели одного и того же типа можно сравнивать с помощью стандартных операций сравнения. При этом сравниваются значения указателей, а не значения величин, на которые данные указатели ссылаются.

    Пример 3.

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    int _tmain(int argc, _TCHAR* argv[]){
      int x=10;
      int y=10;
      int *xptr=x;
      int *yptr=y;
      
    //сравниваем указатели
      if (xptr == yptr) 
        cout << "Указатели равны\n";
         else  
        cout << "Указатели неравны\n";
    
    //сравниваем значения, на которое указывает указатель
      if (*xptr == *yptr) {
        cout << "Значения равны\n";
        } else { 
        cout << "Значения неравны\n";}
      system("pause");
      return 0;
    }

    В приведенном примере результат первой операции сравнения будет ложным, а второй – истинным, поскольку переменные x и y имеют одно и то же значение.

    Ключевые термины

    Адрес объекта – это адрес области оперативной памяти, по которому хранится объект в соответствии с особенностями представления типа.

    Инициализация указателя – это определение значения указателя.

    Косвенная адресация – это обращение к области памяти не напрямую, по адресу, а через объект, которому в памяти соответствует определенный участок.

    Непосредственно производные типы – это типы, которые являются производными от некоторых существующих типов, реализуя типы указателей, ссылки, функции преобразования типов.

    Переименование типов – это задание нового имени для существующего типа.

    Производные типы данных – это типы, полученные на основе более простых базовых типов.

    Разыменование – это операция получения значения объекта, адрес которого хранится в указателе;

    Составные производные типы – это типы, являющиеся производными от различных существующих или ранее объявленных типов.

    Указатель – это именованный объект, предназначенный для хранения адреса области памяти.

    Указатель на константу – это указатель на такой объект, значение которого нельзя изменить в процессе выполнения программы.

    Указатель-константа – это указатель, значение которого нельзя изменить в процессе выполнения программы.

    Указатель-константа на константу – это указатель, для которого невозможно изменение как самого указателя, так и значения адресуемого объекта.

    Краткие итоги

  • В языке С++ производные типы данных классифицируют в зависимости от построения на производные и непосредственно производные.
  • Для экономии памяти и времени, затрачиваемого на обращение к данным, в программах используют указатели на объекты.
  • Указатель не является самостоятельным типом, он всегда связан с другим типом.
  • Указатель может быть константой или переменной, а также указывать на константу или переменную.
  • Указатель типа void указывает на область памяти любого размера. Разыменование такого указателя необходимо проводить с операцией приведения типов.
  • До первого использования в программе объявленный указатель необходимо проинициализировать.
  • С помощью указателей можно выполнять косвенную адресацию объектов.
  • Над указателями определены операции: разыменование, взятие адреса, декремент, инкремент, увеличение (уменьшение) на целую константу, разность, определение размера.
  • Над указателями определены операции сравнения.
  • Лабораторная работа 4. Производные типы. Тип указатель: указатели на объекты.

    Цель работы: изучить классификацию производных типов, тип указатель и соотношения между именами, адресами и значениями переменных, научиться использовать указатели в программных кодах на языке C++.

    При выполнении лабораторной работы для каждого задания требуется написать программу на языке С++, которая получает на входе данные различных типов (в соответствии с условиями задач), выполняет их обработку в соответствии с требованиями задания и выводит результат на экран. Ввод данных осуществляется с клавиатуры с учетом требований к входным данным, содержащихся в постановке задачи. Ограничениями на входные данные является допустимый диапазон значений используемых числовых типов в языке С++.

    Теоретические сведения.

    Ознакомьтесь с материалом лекции 4.

    Задания к лабораторной работе.

    Выполните приведенные ниже задания.

  • В программе определите и инициализируйте переменную типа double, указатель double * и указатель типа void *. Присвойте указателям адрес переменной. Напечатайте адрес переменной, значения указателей и значения, получаемые при разыменовании указателей. Чтобы продемонстрировать роли и последовательность выполнения унарных операций получения адреса и разыменования *, выведите на печать значение выражения *имя_переменной.
  • Задано натуральное число. Разместите в памяти последовательно все его цифры, используя указатели и операции над ними.
  • Определите и инициализируйте переменную типа double. Определите указатели char *, int *, double *, void *, инициализируйте их адресом переменной. Напечатайте значения указателей, их размеры и длины участков памяти, которые связаны с выражениями, разыменовывающими указатели.
  • Указания к выполнению работы.

    Каждое задание необходимо решить в соответствии с изученными методами работы с указателями в языке С++. В отчете следует пояснить различия в использовании указателей разных типов, адресующих один и тот же участок памяти, и отразить основные случаи использования в программе указателя типа void *.

    Следует реализовать каждое задание в соответствии с приведенными этапами:

  • изучить словесную постановку задачи, выделив при этом все виды данных;
  • сформулировать математическую постановку задачи;
  • выбрать метод решения задачи, если это необходимо;
  • разработать графическую схему алгоритма;
  • записать разработанный алгоритм на языке С++;
  • разработать контрольный тест к программе;
  • отладить программу;
  • представить отчет по работе.
  • Требования к отчету.

    Отчет по лабораторной работе должен соответствовать следующей структуре.

  • Титульный лист.
  • Словесная постановка задачи. В этом подразделе проводится полное описание задачи. Описывается суть задачи, анализ входящих в нее физических величин, область их допустимых значений, единицы их измерения, возможные ограничения, анализ условий при которых задача имеет решение (не имеет решения), анализ ожидаемых результатов.
  • Математическая модель. В этом подразделе вводятся математические описания физических величин и математическое описание их взаимодействий. Цель подраздела – представить решаемую задачу в математической формулировке.
  • Алгоритм решения задачи. В подразделе описывается разработка структуры алгоритма, обосновывается абстракция данных, задача разбивается на подзадачи. Схема алгоритма выполняется по ЕСПД (ГОСТ 19.003-80 и ГОСТ 19.002-80).
  • Листинг программы. Подраздел должен содержать текст программы на языке программирования С++, реализованный в среде MS Visual Studio 2010.
  • Контрольный тест. Подраздел содержит наборы исходных данных и полученные в ходе выполнения программы результаты.
  • Выводы по лабораторной работе.
  • Ответы на контрольные вопросы.
  • Контрольные вопросы

  • Почему указатель не может существовать как самостоятельный тип?
  • С какой целью в программе может быть использован указатель типа void?
  • Что будет являться результатом разыменования указателя типа void без приведения типов?
  • Как изменится значение указателя после применения к нему операции инкремента (декремента)?
  • Почему для указателей определены сложение и вычитание только с целыми константами?
  • В чем отличие указателя на константу от указателя-константы?
  • Два указателя разных типов указывают на одно и то же место в памяти. Сравните результаты операций разыменования и взятия адреса с такими указателям. Сравните значения указателей.
  • Если объект занимает в памяти несколько байтов, то какой адрес является значением указателя на этот объект?
  • Каким образом при разыменовании указателей становится известно, сколько байтов памяти доступно?
  • Вернуться к учебному плану