» » » » Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

На нашем литературном портале можно бесплатно читать книгу Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп, Бьёрн Страуструп . Жанр: Программирование. Онлайн библиотека дает возможность прочитать весь текст и даже без регистрации и СМС подтверждения на нашем литературном портале litmir.org.
Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп
Название: Программирование. Принципы и практика использования C++ Исправленное издание
Дата добавления: 22 август 2024
Количество просмотров: 100
Читать онлайн

Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних просмотр данного контента СТРОГО ЗАПРЕЩЕН! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту readbookfedya@gmail.com для удаления материала

Программирование. Принципы и практика использования C++ Исправленное издание читать книгу онлайн

Программирование. Принципы и практика использования C++ Исправленное издание - читать бесплатно онлайн , автор Бьёрн Страуструп

Специальное издание самой читаемой и содержащей наиболее достоверные сведения книги по C++. Книга написана Бьярне Страуструпом — автором языка программирования C++ — и является каноническим изложением возможностей этого языка.
Помимо подробного описания собственно языка, на страницах книги вы найдете доказавшие свою эффективность подходы к решению разнообразных задач проектирования и программирования. Многочисленные примеры демонстрируют как хороший стиль программирования на С-совместимом ядре C++, так и современный -ориентированный подход к созданию программных продуктов. Третье издание бестселлера было существенно переработано автором. Результатом этой переработки стала большая доступность книги для новичков. В то же время, текст обогатился сведениями и методиками программирования, которые могут оказаться полезными даже для многоопытных специалистов по C++. Не обойдены вниманием и нововведения языка: стандартная библиотека шаблонов (STL), пространства имен (namespaces), механизм идентификации типов во время выполнения (RTTI), явные приведения типов (cast-операторы) и другие.
Настоящее специальное издание отличается от третьего добавлением двух новых приложений (посвященных локализации и безопасной обработке исключений средствами стандартной библиотеки), довольно многочисленными уточнениями в остальном тексте, а также исправлением множества опечаток.
Книга адресована программистам, использующим в своей повседневной работе C++. Она также будет полезна преподавателям, студентам и всем, кто хочет ознакомиться с описанием языка «из первых рук».

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

 

 А не мог ли компилятор как-то увидеть, что массив lac содержит только двадцать элементов, так что выражение lac[200] — это ошибка? В принципе мог бы, но, как нам известно, в настоящее время не существует ни одного такого компилятора. Дело в том, что отследить границы массива на этапе компиляции невозможно в принципе, а перехват простейших ошибок (таких как приведены выше) не решает всех проблем. 

18.5.1. Указатели на элементы массива

Указатель может ссылаться на элемент массива. Рассмотрим пример.

double ad[10];

double* p = &ad[5]; // ссылается на элемент ad[5]

Указатель p ссылается на переменную типа double, известную как ad[5].

Этот указатель можно индексировать и разыменовывать.

*p =7;

p[2] = 6;

p[–3] = 9;

Теперь ситуация выглядит следующим образом.

Иначе говоря, мы можем индексировать указатель с помощью как положительных, так и отрицательных чисел. Поскольку результаты не выходят за пределы допустимого диапазона, эти выражения являются правильными. Однако выход на пределы допустимого диапазона является незаконным (аналогично массивам, размещенным в свободной памяти; см. раздел 17.4.3). Как правило, выход за пределы массива компилятором не распознается и (рано или поздно) приводит к катастрофе.

Если указатель ссылается на элемент внутри массива, то для его переноса на другой элемент можно использовать операции сложения и вычитания. Рассмотрим пример.

p += 2; // переносим указатель p на два элемента вправо

Итак, приходим к следующей ситуации.

Аналогично,

p –= 5; // переносим указатель p на пять элементов вправо

В итоге получим следующее.

 

 Использование операций +, –, += и –= для переноса указателей называется арифметикой указателей (pointer arithmetic). Очевидно, поступая так, мы должны проявлять большую осторожность, чтобы не выйти за пределы массива.

p += 1000;     // абсурд: p ссылается на массив, содержащий

               // только 10 чисел

double d = *p; // незаконно: возможно неправильное значение

               // (совершенно непредсказуемое)

*p = 12.34;    // незаконно: можно задеть неизвестные данные

К сожалению, не все серьезные ошибки, связанные с арифметикой указателей, легко обнаружить. Лучше всего просто избегать использования арифметики указателей.

Наиболее распространенным использованием арифметик указателей является инкрементация указателя (с помощью оператора ++) для ссылки на следующий элемент и декрементация указателя (с помощью оператора ––) для ссылки на предыдущий элемент. Например, мы могли вы вывести элементы массива ad следующим образом:

for (double* p = &ad[0]; p<&ad[10]; ++p) cout << *p << 'n';

И в обратном порядке:

for (double* p = &ad[9]; p>=&ad[0]; ––p) cout << *p << 'n';

Это использование арифметики указателей не слишком широко распространено. Однако, по нашему мнению, последний (“обратный”) пример небезопасен. Почему &ad[9], а не &ad[10]? Почему >=, а не >? Эти примеры были бы одинаково хороши (и одинаково эффективны), если бы мы использовали индексацию. Кроме того, они были бы совершенно эквивалентны в классе vector, в котором проверка выхода за пределы допустимого диапазона осуществляется проще.

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

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

double* p1 = &ad[0];

double* p2 = p1+7;

double* p3 = &p1[7];

if (p2 != p3) cout << "impossible!n";

 

 В основном это произошло по историческим причинам. Эти правила были разработаны для языка C несколько десяткой лет назад, и отменить их невозможно, не выбросив в мусорную корзину огромное количество программ. Частично это объясняется тем, что арифметика указателей обеспечивает определенное удобство в некоторых низкоуровневых приложениях, например в механизме управления памятью.

18.5.2. Указатели и массивы

 

 Имя массива относится ко всем элементам массива. Рассмотрим пример.

char ch[100];

Размер массива ch, т.е. sizeof(ch), равен 100. Однако имя массива без видимых причин превращается в указатель.

char* p = ch;

Здесь указатель p инициализируется адресом &ch[0], а размер sizeof(p) равен 4 (а не 100). Это свойство может быть полезным. Например, рассмотрим функцию strlen(), подсчитывающую количество символов в массиве символов, завершающимся нулем.

int strlen(const char* p) // аналогична стандартной

                          // функции strlen()

{

  int count = 0;

  while (*p) { ++count; ++p; }

  return count;

}

Теперь можем вызвать ее как с аргументом strlen(ch), так и с аргументом strlen(&ch[0]). Возможно, вы заметили, что такое обозначение дает очень небольшое преимущество, и мы с вами согласны. Одна из причин, по которым имена массивов могут превращаться в указатели, состоит в желании избежать передачи большого объема данных по значению. Рассмотрим пример.

int strlen(const char a[]) // аналогична стандартной

                           // функции strlen()

{

  int count = 0;

  while (a[count]) { ++count; }

  return count;

}

char lots [100000];

void f()

{

 int nchar = strlen(lots);

 // ... 

Наивно (но частично обоснованно) мы могли бы ожидать, что при выполнении этого вызова будут скопированы 100 тыс. символов, заданных как аргумент функции strlen(), но этого не происходит. Вместо этого объявление аргумента char p[] рассматривается как эквивалент объявления char* p, а вызов strlen(lots) — как эквивалент вызова strlen(&lots[0]). Это предотвращает затратное копирование, но должно вас удивить. Почему вы должны

Перейти на страницу:
Комментариев (0)