C++ для начинающих - страница 35
Вот как выглядит типичный пример использования адресной арифметики при переборе элементов массива с помощью итератора:
>int ia[10];
>int *iter = ia[0];
>int *iter_end = ia[10];
>while (iter != iter_end) {
>do_something_with_value (*iter);
>++iter;
>}
Даны определения переменных:
>int ival = 1024, ival2 = 2048;
>int *pi1 = ival, *pi2 = ival2, **pi3 = 0;
Что происходит при выполнении нижеследующих операций присваивания? Допущены ли в данных примерах ошибки?
>(a) ival = *pi3; (e) pi1 = *pi3;
>(b) *pi2 = *pi3; (f) ival = *pi1;
>(c) ival = pi2; (g) pi1 = ival;
>(d) pi2 = *pi1; (h) pi3 = pi2;
Работа с указателями – один из важнейших аспектов С и С++, однако в ней легко допустить ошибку. Например, код
>pi = ival;
>pi = pi + 1024;
почти наверняка приведет к тому, что pi будет указывать на случайную область памяти. Что делает этот оператор присваивания и в каком случае он не приведет к ошибке?
Данная программа содержит ошибку, связанную с неправильным использованием указателей:
>int foobar(int *pi) {
>*pi = 1024;
>return *pi;
>}
>int main() {
>int *pi2 = 0;
>int ival = foobar(pi2);
>return 0;
>}
В чем состоит ошибка? Как можно ее исправить?
Ошибки из предыдущих двух упражнений проявляются и приводят к фатальным последствиям из-за отсутствия в С++ проверки правильности значений указателей во время работы программы. Как вы думаете, почему такая проверка не была реализована? Можете ли вы предложить некоторые общие рекомендации для того, чтобы работа с указателями была более безопасной?
3.4. Строковые типы
В С++ поддерживаются два типа строк – встроенный тип, доставшийся от С, и класс string из стандартной библиотеки С++. Класс string предоставляет гораздо больше возможностей и поэтому удобней в применении, однако на практике нередки ситуации, когда необходимо пользоваться встроенным типом либо хорошо понимать, как он устроен. (Одним из примеров может являться разбор параметров командной строки, передаваемых в функцию main(). Мы рассмотрим это в главе 7.)
3.4.1. Встроенный строковый тип
Как уже было сказано, встроенный строковый тип перешел к С++ по наследству от С. Строка символов хранится в памяти как массив, и доступ к ней осуществляется при помощи указателя типа char*. Стандартная библиотека С предоставляет набор функций для манипулирования строками. Например:
>// возвращает длину строки
>int strlen( const char* );
>// сравнивает две строки
>int strcmp( const char*, const char* );
>// копирует одну строку в другую
>char* strcpy( char*, const char* );
Стандартная библиотека С является частью библиотеки С++. Для ее использования мы должны включить заголовочный файл:
>#include cstring
Указатель на char, с помощью которого мы обращаемся к строке, указывает на соответствующий строке массив символов. Даже когда мы пишем строковый литерал, например
const char *st = "Цена бутылки вина\n";
компилятор помещает все символы строки в массив и затем присваивает st адрес первого элемента массива. Как можно работать со строкой, используя такой указатель?
Обычно для перебора символов строки применяется адресная арифметика. Поскольку строка всегда заканчивается нулевым символом, можно увеличивать указатель на 1, пока очередным символом не станет нуль. Например:
>while (*st++ ) { ... }
st разыменовывается, и получившееся значение проверяется на истинность. Любое отличное от нуля значение считается истинным, и, следовательно, цикл заканчивается, когда будет достигнут символ с кодом 0. Операция инкремента ++ прибавляет 1 к указателю st и таким образом сдвигает его к следующему символу.
Вот как может выглядеть реализация функции, возвращающей длину строки. Отметим, что, поскольку указатель может содержать нулевое значение (ни на что не указывать), перед операцией разыменования его следует проверять:
>int string_length( const char *st )
>{
>int cnt = 0;
>if ( st )
>while ( *st++ )
>++cnt;
>return cnt;
>}
Строка встроенного типа может считаться пустой в двух случаях: если указатель на строку имеет нулевое значение (тогда у нас вообще нет никакой строки) или указывает на массив, состоящий из одного нулевого символа (то есть на строку, не содержащую ни одного значимого символа).