Среда, 15.08.2018, 06:37
Discovery
Главная Регистрация Вход
Приветствую Вас, Гость · RSS
Меню сайта
Форма входа
Категории раздела
PHP [1]
Уроки по PHP для начинающих
C++ [18]
Статьи посвященные С++
XML [2]
Статьи по XML
Поиск
Друзья сайта
  • Официальный блог
  • Сообщество uCoz
  • FAQ по системе
  • Инструкции для uCoz
  • Статистика

    Онлайн всего: 1
    Гостей: 1
    Пользователей: 0
     Каталог статей
    -->
    Главная » Статьи » Программирование » C++

    Арифметические действия с указателями, указатели и массивы.

    Арифметические действия с указателями, указатели и массивы.

    Прежде чем мы перейдем изучению арифметических действий, я хочу несколько слов сказать, чем отличается код профессионального программиста от начинающего. Если профессионального программиста и начинающего программиста попросить написать одну и ту же программу, то у профессионального программиста она будет более короткой, более переносимой  и безопасной. Одна из проблем использования языка С++ в том, что он позволяет программисту делать все что он захочет. Например, ничего не помещает начинающему программисту написать программу, состоящую из ста строчек, хотя профессиональный программист напишет точно такую же программу, состоящую из пятидесяти строк. Иными словами никто не может помещать вам, написать громоздкий код, который будет очень трудным в сопровождении и очень не эффективным. Где же выход? Выход только один: чем лучше мы будем понимать, что происходит за кулисами нашей программы, тем более эффективней мы будем использовать те возможности, которые предоставляет нам С++.

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

    Для начала давайте разберемся, с какими типами данных возможно использование арифметических операций над указателями, а с какими нет. Если использовать их использовать не с тем типом данных, то результат будет непредсказуемым. И так давайте представим, что мы объявили две переменных типа char: test1 и test2. Как и для любой переменной, операционная система выделит в памяти место, необходимое для этих переменных, а значит, мы можем узнать номера ячеек (адрес), которые они занимают, через указатель. Но никто не даст гарантии, что в другой операционной системе они будут занимать те же самые ячейки, или то, что между ними будут располагаться то же самое количество ячеек. Эти две ячейки ни как не связаны. Единственным типом данных, в которых переменные связаны друг с другом, является массив. Язык С++ устроен так, что все переменные массива располагаются вместе, и следуют друг за другом. И даже больше того, когда мы присваиваем массив без индекса, указатель автоматически указывает на первый элемент массива. Чтоб убедиться в этом, давайте посмотрим на код следующей программы:

    #include <iostream>
    #include <windows.h>
    using namespace std;

    int main()
    {
        char *pointer;
        char str[25] = "Привет, Мир!";

        pointer = str;
        cout << *pointer;

        cout << "\n\n";
        system("PAUSE");
        return 0;
    }

    Как видим, наша программа вывела букву «П». С помощью указателя можно вывести строку на экран. Для этого давайте немного изменим нашу предыдущую программу:

    #include <iostream>
    #include <windows.h>
    using namespace std;

    int main()
    {
        char *pointer;
        char str[25] = "Привет, Мир!";

        pointer = str;

        while(*pointer)
        {
            cout << *pointer;
            pointer++;
        }

        cout << "\n\n";
        system("PAUSE");
        return 0;
    }

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

    #include <iostream>
    #include <windows.h>
    using namespace std;

    int main()
    {
        int *pointer;
        int number[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

        pointer = number;

        for(int i=0; i<10; i++)
        {
            cout << *pointer << " ";
            pointer++;
        }

        cout << "\n\n";
        system("PAUSE");
        return 0;
    }

    Как мы помним под переменные различных типов, отводиться различное количество памяти, необходимое для хранения переменной. Очень важно понять, что сдвиг при инкрементировании (увеличении значения указателя на единицу), происходит не на следующую ячейку памяти, а на количество байтов, которые занимает тип данных. В нашем случае тип int занимает четыре байта, следовательно, указатель каждый раз сдвигается на четыре байта. Таким образом, гарантируется, что каждый раз указатель будет указывать на следующее значение.

    Можно вывести определенный элемент массива, прибавив к указателю установленному на первый элемент массива целое число. Вот как, например, вывести с помощью указателя, пятую букву в строчке «Привет, мир!»:

    #include <iostream>
    #include <windows.h>
    using namespace std;

    int main()
    {
        char *pointer;
        char str[25] = "Привет, Мир!";

        pointer = str;

        cout << "Выводим на экран строку целиком:\n\n";

        while(*pointer)
        {
            cout << *pointer;
            pointer++;
        }
        cout << "\n\n";

        cout << "Выводим на экран пятую букву нашей строки:\n\n";
     
         pointer = str;
        pointer = pointer + 4;
        cout << *pointer;

        cout << "\n\n";
        system("PAUSE");
        return 0;
    }

    Обратите внимание, что после цикла while нам пришлось переустанавливать значение указателя pointer. Дело в том, что указатель является всегда глобальным, в отличие от переменной.

    Теперь давайте рассмотрим декримент указателя (уменьшение на единицу). В качестве примера я написал программу, которая с помощью указателя отображает введенную строку наоборот (код программы в низу):

    #include <iostream>
    #include <cstdio>
    #include <windows.h>
    using namespace std;

    int main()
    {
        char str[80];
        char *ptr1;
        char *ptr2;
        int x;

        cout << "Введите строку: \n\n";
        gets(str);

        cout << "\n\nВыводим строку, так как мы ее ввели: \n\n";
        cout << "\n" << str << "\n\n";

        ptr1 = str;
        ptr2 = ptr1;

         x = (strlen(str)) - 1;
        ptr1 = ptr1 + x;

        cout << "Выводим строку в обратном порядке: \n\n";

        while(!(ptr1 == ptr2))
        {
            cout << *ptr1;
            --ptr1;
        }
        cout << *ptr1;

        cout << "\n\n";
        system("PAUSE");
        return 0;
    }

    Прежде, чем мы двинемся дальше, я хотел бы объяснить, зачем нам потребовалось присвоить адрес,  записанный в указатель ptr1 указателю ptr2. Во время выполнения цикла, указатель с каждым витком цикла будет указывать на предыдущий символ. И для того, чтобы цикл while вовремя остановился, в условии цикла он должен сравниваться с указателем, в котором находиться адрес первого символа. Как только они совпадают, цикл сразу же завершает свою работу. Указатель в сущности такая же переменная, только в отличие от обычной переменной он хранит не значение, а адрес (номер ячейки) по которому находиться значение, а значит, адрес, записанный в одном указателе можно присвоить другому указателю (записать в другой указатель), так что они будут оба указывать на одно и то же значение, что мы и делаем, написав в программе строчку: ptr2 = ptr1;. Далее мы с помощью функции strlen(), которая вычисляет длину строки, узнаем длину нашего массива. Так как у нас указатель уже установлен на первый символ нашего массива, нужно вычесть единицу из длины массива, чтобы указатель содержал адрес последнего символа. В условии мы сравниваем текущее положение указателя ptr1 с указателем ptr2, который всегда указывает на первый символ нашего массива. Это условие является ложным, пока два указателя не будут равны. И что бы цикл выполнялся, мы заключили сравнение указателей в скобки и перед ними поставили знак логического не (восклицательный знак). Если логическое выражение истинное, он делает его ложным, а ложное истинным. В результате чего как только оба указателя будут равны, цикл завершит свою работу. Ну а затем уже за пределами цикла выводим первую букву нашей строки.

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

    #include <iostream>
    #include <windows.h>
    using namespace std;

    int main()
    {
        int *pointer;
        int number[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

        pointer = number;

        for(int i=0; i<10; i++)
        {
        cout << pointer[i] << " ";
        }

        cout << "\n\n";
        system("PAUSE");
        return 0;
    }

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

    Категория: C++ | Добавил: stranger140569 (03.05.2013)
    Просмотров: 2617 | Рейтинг: 5.0/1
    Всего комментариев: 0
    Имя *:
    Email *:
    Код *:
    Copyright MyCorp © 2018
    Конструктор сайтов - uCoz