Глава 8
Вверх
Один указатель можно вычитать из другого. Если например два указателя
ссылаются на разные элементы массива, то вычитание одного указателя из
другого позволяет получить количество элементов массива, находящихся между
ними. Наиболее эффективно эта методика используется при обработке
массивов.
Пример:
Разделение символьной строки по словам. Вы вводите символьную строку,
а программа выводит каждое слово с отдельной новой строки, таким образом
разделяет введенную вами строку по словам.
Но если ввести строку кириллицей то будут казусы.
Чтобы не было казусов с кирилицей в этой программе, надо включать
соответствующие заголовочные файлы для кирилицы.
Listing8_11 Разделение символьной строки по словам
bool GetWord(char* theString,
char* word, int& wordOffset);
// driver program
char buffer[bufferSize+1]; // hold the entire string
int wordOffset = 0; // start at the beginning
std::cout << "Enter a string: ";
while (GetWord(buffer,word,wordOffset))
{
return 0;
// function to parse words from a string.
bool GetWord(char* theString, char* word, int& wordOffset)
{
if (!theString[wordOffset]) // end of string?
char *p1, *p2;
// eat leading spaces
// see if you have a word
// p1 now points to start of next word
// march p2 to end of word
// p2 is now at end of word
// copy the word into the buffer
// null terminate it
// now find the beginning of the next word
wordOffset = int(p2-theString);
return true;
1. Сначала объявляется и инициализируется константа
Эта константа будет определять длину символьного массива.
2. Затем объявляется два символьных массива ,
их длина определяется вышеуказанной константой.
Массивы расположены в стеке.
Здесь первый массив будет буфером для вводимой пользователем строки.
А второй массив будет буфером для отдельных слов уже во время работы
программы.
3. Затем объявляется еще одна переменная инициализируемая нулем.
4. Далее предлагается ввести строку.
И введенная строка при помощи библиотечной функции cin.getline()
помещается в массив buffer[]
5. Далее массив buffer в котором содержится введенная строка
передается функции GetWord() в качестве аргумента, наряду с буфером
для первого слова(неинициализированный массив word)
и целочисленной переменной WordOffset, инициализированной
ранее нулевым значением.
Помним что первый и второй параметр этой функции были объявлены
как указатели на переменную типа char !.
Суть цикла таков:как только функция GetWord() возвратит 1 или true
сразу выполнится оператор внутри фигурных скобок.
Если функция возвратит false то сразу выход из цикла.
Слова выводятся на экран по мере возвращения их функцией GetWord()
до тех пор пока она не вернет значение false.
6. Рассмотрим работу функции GetWord().
Обратите внимание здесь снова используется способность С++ интерпретировать
значение 0 как false. В противном случае строку пришлось бы переписать так
сравни:
Этим условием программа проверяет, не является ли рассматриваемый
символ символом окончания строки?Если истинно то функция вернет false
в противном случае -продолжается выполнение функции.
6. Посмотрим работу функции GetWord()
Объявляются два указателя на переменную символьного типа . В следующей строке
оба указателя устанавливаются в начало следующего слова., заданного значением
переменной wordOffset.
Вначале значение переменной wordoffset равно нулю, следовательно они
указывают на начало строки.
Вместо theString мы передали как аргумент указатель на массив buffer.
Мы передали имя массива без индекса , а Имя массива без индекса
указывает на первый элемент массива.(в данном случае это первый символ
строки , это может быть даже пробел.)
Итак оба указателя p1 и p2 мы инициализировали адресом начала строки.
С помощью следующего цикла указатель p1 перемещается на первый символ
являющийся буквой или цифрой.(убираются все пробелы в начале строки,
если таковые имеются)Если такой символ не найден, функция возвращает
false.(если вообще не найдено ни одного символа кроме пробела, то- false)
//убрать ведущие пробелы
Суть этого цикла в том чтобы прибавлять на 1 указатель p1 до тех пор
пока не встретится первый элемент (символ) массива.p1[0]-это первый
элемент массива и пока i не превысит длину массива.
Выражение (int)strlen(p1) обозначает количество символов во введенном
нами символьном массиве. Эта величина постоянная. Я имею в виду что
при выполнении цикла, p1 в скобках в результате инкремента изменяется
но количество элементов в символьном массиве, то есть значение
вышеуказанного выражения не изменяется и является первым постоянным условием
этого цикла.
Второе выражение как второе условие цикла !isalnum(p1[0]) означает
"пока пробел" или "пока указатель p1 указывает на пробел".
Но этот цикл не будет выполнен ни разу если указатель сразу оказался
на первом слове введенного нами предложения. И программа сразу
пойдет выполняться дальше.
Следующие две строки обозначают что если вообще не встретится символ,
а длина строки уже исчерпана, то возвратить как результат работы
функции нуль или false.
//является ли это словом?
Таким образом указатель p1 соответствует началу очередного (или первого)
слова , а следующая строка присваивает указателю p2 то же значение.
p2 теперь указывает на начало следующего(или первого) слова так же как и p1
В следующих строках осуществляется осуществляется поиск в строке первого
символа, не являющегося ни цифрой ни буквой(являющегося пробелом).
Указатель p2 перемещается на этот символ.
Теперь p1 и p2 указывают на начало и конец слова
соответственно.
Напомню что выражение isalnum(p2[0]) обозначает "не пробел" или другими
словами "буква или цифра"
//переместить p2 в конец слова
Теперь p1 и p2 указывают на начало и конец слова соответственно.
Вычтем из значения указателя p2 значение p1 и преобразуем результат к
целочисленному типу. Результатом выполнения такой операции будет длина
очередного (или первого) слова.
//p2 теперь в конце слова, p1 теперь в начале слова
//их разница составляет длину слова
int len = int (p2 - p1);
Теперь в конец слова надо поместить нулевой символ
Далее указатель p2 перемещается в начало следующего слова, а
переменной wordoffset присваивается значение смещения начала
очередного слова относительно начала строки. Возвращая значение
true функция сообщает что слово найдено.
//найти начало следующего слова
for (int j = int(p2-theString); j<(int)strlen(theString)
Этот цикл будет выполняться пока не будет найден первый символ
следующего слова. Теперь p2 указывает на первый символ следующего слова.
Выражение int(p2-theString) обозначает число символов от начала массива
до начала следующего взятого для рассмотрения слова.
Вычислим смещение этого слова относительно начала строки.
wordOffset -смещение слова относительно начала строки.
Вверх
Для объявления указателя достаточно установить тип объекта, адрес
которого он будет содержать, а затем ввести оператор косвенного
доступа (*) и имя указателя. После объявления указатель следует
инициализировать. Если адрес объекта неизвестен, указатель инициализируется
значением 0.
Для доступа к значению, записанному по адресу указателя, используется
оператор косвенного доступа(*). Указатель можно объявить постоянным.
В этом случае не допускается присвоение ему нового адреса. Указатель,
хранящий адрес постоянного объекта, не может использоваться для изменения
этого объекта.
Чтобы создавать новый объект в динамической памяти, используется ключевое
слово new. Полученный адрес присваивается указателю. Чтобы освободить
выделенную память, применяется ключевое слово delete. Оператор delete
освобождает память, но не уничтожает указатель. Поэтому после освобождения
памяти указатель необходимо переприсвоить, по крайней мере назначить ему
нулевое значение.
Вверх
1. Почему указатели так важны?
Сегодня рассматривалось использование указателей для доступа к объектам,
размещенным в динамической памяти, а также для передачи аргументов в функции
по ссылке. В уроке 14 будет рассмотрено использование указателей в полиморфизме
классов.
2. Чем удобно размещение объектов в динамической памяти?
Объекты находящиеся в динамической памяти , не уничтожаются после
выхода из той области, в которой они были объявлены. Кроме того
появляется возможность уже в процессе выполнения программы решать,
какое количество объектов требуется объявить. Более подробно эта тема
в следующем уроке.
3. Зачем объявлять объект постоянным, ведь это ограничивает его возможности?
Каждый программист желает привлечь компилятор для поиска ошибок. Одной
из серьезнейших ошибок, крайне трудных для обнаружения является функция
изменяющая объект нетрадиционным способом, которые не очевидны ни для
вызывающей функции, ни для самого программиста. Объявление объекта постоянным,
предотвращает такие действия.
Назад |
Начало урока |
Вверх |
Вперед
Арифметические операции над указателями
#include <iostream>
#include <ctype.h>
#include <string.h>
int main()
{
char word[bufferSize+1]; // hold the word
std::cin.getline(buffer,bufferSize);
return false;
p1 = p2 = theString+wordOffset; // point to the next word
for (int i = 0; i<(int)strlen(p1) && !isalnum(p1[0]); i++)
p1++;
if (!isalnum(p1[0]))
return false;
// point p2 there as well
p2 = p1;
while (isalnum(p2[0]))
p2++;
// p1 is at beginning of word
// length of word is the difference
int len = int (p2 - p1);
strncpy (word,p1,len);
word[len]='\0';
for (int j = int(p2-theString); j<(int)strlen(theString)
&& !isalnum(p2[0]); j++)
{
p2++;
const int bufferSize = 255;
char buffer[bufferSize+1]; // hold the entire string
char word[bufferSize+1]; // hold the word
int wordOffset = 0; // start at the beginning
std::cout << "Enter a string: ";
std::cin.getline(buffer,bufferSize);
while (GetWord(buffer,word,wordOffset))
{
std::cout << "Got this word: " << word << std::endl;
bool GetWord(char* theString, char* word, int& wordOffset)
if (theString[wordOffset]==0) // end of string?
return false;
if (!theString[wordOffset]) // end of string?
return false;
char *p1, *p2;
p1 = p2 = theString+wordOffset; // point to the next word
// eat leading spaces
for (int i = 0; i<(int)strlen(p1) && !isalnum(p1[0]); i++)
p1++;
// see if you have a word
if (!isalnum(p1[0]))
return false;
// p1 now points to start of next word
// point p2 there as well
p2 = p1;
// march p2 to end of word
while (isalnum(p2[0]))
p2++;
// p2 is now at end of word
Затем на основании данных о начале и длине полученное слово копируется
в буфер.
//копировать слово в буфер
// p1 is at beginning of word
// length of word is the difference
// copy the word into the buffer
p1 здесь указывает на первый символ слова, len-количество символов в слове.
strncpy (word,p1,len);
// null terminate it
word[len]='\0';
// now find the beginning of the next word
&& !isalnum(p2[0]); j++)
{
p2++;
wordOffset = int(p2-theString);
Это классический пример кода который лучше всего изучать
поместив в отладчик и запустив пошаговое выполнение.
return true;
Резюме
Указатели являются мощнейшим средством косвенного доступа к данным.
Каждая переменная имеет адрес, получить который можно с помощью
оператора взятия адреса(&). Для хранения адреса используются указатели.
Вопросы и ответы
Содержание