C++ для начинающих - страница 36

стр.


>// pc1 не адресует никакого массива символов

>char *pc1 = 0;

>// pc2 адресует нулевой символ

>const char *pc2 = "";


Для начинающего программиста использование строк встроенного типа чревато ошибками из-за слишком низкого уровня реализации и невозможности обойтись без адресной арифметики. Ниже мы покажем некоторые типичные погрешности, допускаемые новичками. Задача проста: вычислить длину строки. Первая версия неверна. Исправьте ее.


>#include iostream

>const char *st = "Цена бутылки вина\n";

>int main() {

>int len = 0;

>while ( st++ ) ++len;

>cout len ": " st;

>return 0;

>}


В этой версии указатель st не разыменовывается. Следовательно, на равенство 0 проверяется не символ, на который указывает st, а сам указатель. Поскольку изначально этот указатель имел ненулевое значение (адрес строки), то он никогда не станет равным нулю, и цикл будет выполняться бесконечно.

Во второй версии программы эта погрешность устранена. Программа успешно заканчивается, однако полученный результат неправилен. Где мы не правы на этот раз?


>#include iostream

>const char *st = "Цена бутылки вина\n";

>int main()

>{

>int len = 0;

>while ( *st++ ) ++len;

>cout len ": " st endl;

>return 0;

>}


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

Можно попробовать исправить эту ошибку:


>st = st – len;

>cout len ": " st;


Теперь наша программа выдает что-то осмысленное, но не до конца. Ответ выглядит так:


18: ена бутылки вина


Мы забыли учесть, что заключительный нулевой символ не был включен в подсчитанную длину. st должен быть смещен на длину строки плюс 1. Вот, наконец, правильный оператор:


>st = st – len - 1;


а вот и и правильный результат:


18: Цена бутылки вина


Однако нельзя сказать, что наша программа выглядит элегантно. Оператор


>st = st – len - 1;


добавлен для того, чтобы исправить ошибку, допущенную на раннем этапе проектирования программы, – непосредственное увеличение указателя st. Этот оператор не вписывается в логику программы, и код теперь трудно понять. Исправления такого рода часто называют заплатками – нечто, призванное заткнуть дыру в существующей программе. Гораздо лучшим решением было бы пересмотреть логику. Одним из вариантов в нашем случае может быть определение второго указателя, инициализированного значением st:


>const char *p = st;


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


>while ( *p++ )


3.4.2. Класс string


Как мы только что видели, применение встроенного строкового типа чревато ошибками и не очень удобно из-за того, что он реализован на слишком низком уровне. Поэтому достаточно распространена разработка собственного класса или классов для представления строкового типа – чуть ли не каждая компания, отдел или индивидуальный проект имели свою собственную реализацию строки. Да что говорить, в предыдущих двух изданиях этой книги мы делали то же самое! Это порождало проблемы совместимости и переносимости программ. Реализация стандартного класса string стандартной библиотекой С++ призвана была положить конец этому изобретению велосипедов.

Попробуем специфицировать минимальный набор операций, которыми должен обладать класс string:

* инициализация массивом символов (строкой встроенного типа) или другим объектом типа string. Встроенный тип не обладает второй возможностью;

* копирование одной строки в другую. Для встроенного типа приходится использовать функцию strcpy();

* доступ к отдельным символам строки для чтения и записи. Во встроенном массиве для этого применяется операция взятия индекса или косвенная адресация;

* сравнение двух строк на равенство. Для встроенного типа используется функция strcmp();

* конкатенация двух строк, получая результат либо как третью строку, либо вместо одной из исходных. Для встроенного типа применяется функция strcat(), однако чтобы получить результат в новой строке, необходимо последовательно задействовать функции strcpy() и strcat();