С.Б. Липпман, Ж. Лажойе - Язык программирования С++ Вводный курс (1114944), страница 16
Текст из файла (страница 16)
Программы, использующие особенности реализациитого или иного компилятора или операционной системы, являются непереносимыми. Мыкрайне не рекомендуем пользоваться такими конструкциями.Упражнение 3.1Объясните разницу в определениях следующих литералов:77С++ для начинающих(a) 'a', L'a', "a", L"a"(b) 10, 10u, 10L, 10uL, 012, 0*C(c) 3.14, 3.14f, 3.14LУпражнение 3.2(a)(b)(c)(d)(e)(f)"Who goes with F\144rgus?\014"3.14e1L"two" L"some"1024f3.14UL"multiple lineКакие ошибки допущены в приведенных ниже примерах?comment"3.2.
Переменные#include <iostream>Представим себе, что мы решаем задачу возведения 2 в степень 10. Пишем:int main() {// a first solutioncout << "2 raised to the power of 10: ";cout << 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2;cout << endl;return 0;}Задача решена, хотя нам и пришлось неоднократно проверять, действительно ли 10 разповторяется литерал 2. Мы не ошиблись в написании этой длинной последовательностидвоек, и программа выдала правильный результат – 1024.Но теперь нас попросили возвести 2 в 17 степень, а потом в 23. Чрезвычайно неудобнокаждый раз модифицировать текст программы! И, что еще хуже, очень простоошибиться, написав лишнюю двойку или пропустив ее... А что делать, если нужнонапечатать таблицу степеней двойки от 0 до 15? 16 раз повторить две строки, имеющиеcout << "2 в степени X\t";общий вид:cout << 2 * ...
* 2;где Х последовательно увеличивается на 1, а вместо отточия подставляется нужное числолитералов?Да, мы справились с задачей. Заказчик вряд ли будет вникать в детали,удовлетворившись полученным результатом. В реальной жизни такой подход достаточночасто срабатывает, более того, бывает оправдан: задача решена далеко не самымизящным способом, зато в желаемый срок. Искать более красивый и грамотный вариантможет оказаться непрактичной тратой времени.78С++ для начинающихВ данном случае “метод грубой силы” дает правильный ответ, но как же неприятно искучно решать задачу подобным образом! Мы точно знаем, какие шаги нужно сделать, носами эти шаги просты и однообразны.Привлечение более сложных механизмов для той же задачи, как правило, значительноувеличивает время подготовительного этапа.
Кроме того, чем более сложные механизмыприменяются, тем больше вероятность ошибок. Но даже несмотря на неизбежные ошибкии неверные ходы, применение “высоких технологий” может принести выигрыш вскорости разработки, не говоря уже о том, что эти технологии значительно расширяютнаши возможности. И – что интересно! – сам процесс решения может статьпривлекательным.Вернемся к нашему примеру и попробуем “технологически усовершенствовать” егореализацию.
Мы можем воспользоваться именованным объектом для хранения значениястепени, в которую нужно возвести наше число. Кроме того, вместо повторяющейся#include <iostream>int main(){// objects of type intint value = 2;int pow = 10;cout << value << " в степени "<< pow << ": \t";int res = 1;// оператор цикла:// повторить вычисление res// до тех пор пока cnt не станет больше powfor ( int cnt=1; cnt <= pow; ++cnt )res = res * value;cout << res << endl;последовательности литералов применим оператор цикла. Вот как это будет выглядеть:}value, pow, res и cnt – это переменные, которые позволяют хранить, модифицировать иизвлекать значения. Оператор цикла for повторяет строку вычисления результата powраз.Несомненно, мы создали гораздо более гибкую программу. Однако это все еще нефункция.
Чтобы получить настоящую функцию, которую можно использовать в любойпрограмме для вычисления степени числа, нужно выделить общую часть вычислений, аint pow( int val, int exp )конкретные значения задать параметрами.{for ( int res = 1; exp > 0; --exp )res = res * val;return res;}79С++ для начинающихТеперь получить любую степень нужного числа не составит никакого труда. Вот как#include <iostream>extern int pow(int,int);реализуется последняя наша задача – напечатать таблицу степеней двойки от 0 до 15:int main(){int val = 2;int exp = 15;cout << "Степени 2\n";for ( int cnt=0; cnt <= exp; ++cnt )cout << cnt << ": "<< pow( val, cnt ) << endl;return 0;}Конечно, наша функция pow() все еще недостаточно обобщена и недостаточно надежна.Она не может оперировать вещественными числами, неправильно возводит числа вотрицательную степень – всегда возвращает 1. Результат возведения большого числа вбольшую степень может не поместиться в переменную типа int, и тогда будетвозвращено некоторое случайное неправильное значение.
Видите, как непросто,оказывается, писать функции, рассчитанные на широкое применение? Гораздо сложнее,чем реализовать конкретный алгоритм, направленный на решение конкретной задачи.3.2.1. Что такое переменнаяПеременная, или объект – это именованная область памяти, к которой мы имеем доступиз программы; туда можно помещать значения и затем извлекать их.
Каждая переменнаяС++ имеет определенный тип, который характеризует размер и расположение этойобласти памяти, диапазон значений, которые она может хранить, и набор операций,intdoubleboolstrinsstudent_count;salary;on_loan;street_address;применимых к этой переменной. Вот пример определения пяти объектов разных типов:chardelimiter;Переменная, как и литерал, имеет определенный тип и хранит свое значение в некоторойобласти памяти.
Адресуемость – вот чего не хватает литералу. С переменнойассоциируются две величины:•собственно значение, или r-значение (от read value – значение для чтения),которое хранится в этой области памяти и присуще как переменной, так илитералу;•значение адреса области памяти, ассоциированной с переменной, или lзначение (от location value – значение местоположения) – место, где хранится rзначение; присуще только объекту.80С++ для начинающихВ выраженииch = ch - '0';переменная ch находится и слева и справа от символа операции присваивания. Справарасположено значение для чтения (ch и символьный литерал '0'): ассоциированные спеременной данные считываются из соответствующей области памяти.
Слева – значениеместоположения: в область памяти, соотнесенную с переменной ch, помещается результатвычитания. В общем случае левый операнд операции присваивания должен быть l-// ошибки компиляции: значения слева не являются l-значениями// ошибка: литерал - не l-значение0 = 1;// ошибка: арифметическое выражение - не l-значениезначением. Мы не можем написать следующие выражения:salary + salary * 0.10 = new_salary;Оператор определения переменной выделяет для нее память. Поскольку объект имееттолько одну ассоциированную с ним область памяти, такой оператор может встретиться впрограмме только один раз.
Если же переменная, определенная в одном исходном файле,// файл module0.Cдолжна быть использована в другом, появляются проблемы. Например:// определяет объект fileNamestring fileName;// ... присвоить fileName значение// файл module1.C// использует объект fileName// увы, не компилируется:// fileName не определен в module1.Cifstream input_file( fileName );С++ требует, чтобы объект был известен до первого обращения к нему. Это вызванонеобходимостью гарантировать правильность использования объекта в соответствии сего типом. В нашем примере модуль module1.C вызовет ошибку компиляции, посколькупеременная fileName не определена в нем. Чтобы избежать этой ошибки, мы должнысообщить компилятору об уже определенной переменной fileName.
Это делается с// файл module1.Cпомощью инструкции объявления переменной:// использует объект fileName// fileName объявляется, то есть программа получает// информацию об этом объекте без вторичного его определенияextern string fileName;81С++ для начинающих82ifstream input_file( fileName )Объявление переменной сообщает компилятору, что объект с данным именем, имеющийданный тип, определен где-то в программе. Память под переменную при ее объявлениине отводится. (Ключевое слово extern рассматривается в разделе 8.2.)Программа может содержать сколько угодно объявлений одной и той же переменной, ноопределить ее можно только один раз.
Такие объявления удобно помещать взаголовочные файлы, включая их в те модули, которые этого требуют. Так мы можемхранить информацию об объектах в одном месте и обеспечить удобство ее модификациив случае надобности. (Более подробно о заголовочных файлах мы поговорим в разделе8.2.)3.2.2. Имя переменнойИмя переменной, или идентификатор, может состоять из латинских букв, цифр исимвола подчеркивания. Прописные и строчные буквы в именах различаются. Язык С++не ограничивает длину идентификатора, однако пользоваться слишком длиннымиименами типа gosh_this_is_an_impossibly_name_to_type неудобно.Некоторые слова являются ключевыми в С++ и не могут быть использованы в качествеидентификаторов; в таблице 3.1 приведен их полный список.Таблица 3.1. Ключевые слова C++asmautoboolbreakcasecatchcharclassconstconst_castcontinuedefaultdeletedodoubledynamic_castelseenumexplicitexportexternfalsefloatforfriendgotoifinlineintlongmutablenamespacenewoperatorprivateprotectedpublicregisterreinterpret_castreturnshortsignedsizeofstaticstatic_caststructswitchtemplatethisthrowtruetrytypedeftypeidtypenameunionunsignedusingvirtualvoidvolatilewchar_twhileЧтобы текст вашей программы был более понятным, мы рекомендуем придерживатьсяобщепринятых соглашений об именах объектов:•имя переменной обычно пишется строчными буквами, например index (длясравнения: Index – это имя типа, а INDEX – константа, определенная с помощьюдирективы препроцессора #define);С++ для начинающих•идентификатор должен нести какой-либо смысл, поясняя назначение объекта впрограмме, например: birth_date или salary;если такое имя состоит из нескольких слов, как, например, birth_date, то принято либоразделять слова символом подчеркивания (birth_date), либо писать каждое следующееслово с большой буквы (birthDate).