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();