Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих (Стенли Липпман) 3-е хххх.pdf
Скачиваний:
86
Добавлен:
30.05.2015
Размер:
5.92 Mб
Скачать

С++ для начинающих

78

(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)"Who goes with F\144rgus?\014"

(b)3.14e1L

(c)"two" L"some"

(d)1024f

(e)3.14UL

(f)"multiple line

Какие ошибки допущены в приведенных ниже примерах? comment"

3.2. Переменные

#include <iostream>

Представим себе, что мы решаем задачу возведения 2 в степень 10. Пишем:

int main() {

// a first solution

cout << "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, а вместо отточия подставляется нужное число литералов?

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

С++ для начинающих

79

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

Привлечение более сложных механизмов для той же задачи, как правило, значительно увеличивает время подготовительного этапа. Кроме того, чем более сложные механизмы применяются, тем больше вероятность ошибок. Но даже несмотря на неизбежные ошибки и неверные ходы, применение высоких технологийможет принести выигрыш в скорости разработки, не говоря уже о том, что эти технологии значительно расширяют наши возможности. И что интересно! – сам процесс решения может стать привлекательным.

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

#include <iostream>

int main()

{

// objects of type int int value = 2;

int pow = 10;

cout << value << " в степени " << pow << ": \t";

int res = 1;

//оператор цикла:

//повторить вычисление res

//до тех пор пока cnt не станет больше pow for ( 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;

}

С++ для начинающих

80

Теперь получить любую степень нужного числа не составит никакого труда. Вот как

#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. Что такое переменная

Переменная, или объект это именованная область памяти, к которой мы имеем доступ из программы; туда можно помещать значения и затем извлекать их. Каждая переменная С++ имеет определенный тип, который характеризует размер и расположение этой области памяти, диапазон значений, которые она может хранить, и набор операций,

int student_count; double salary;

bool on_loan; strins street_address;

применимых к этой переменной. Вот пример определения пяти объектов разных типов: char delimiter;

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

собственно значение, или r-значение (от read value – значение для чтения), которое хранится в этой области памяти и присуще как переменной, так и литералу;

значение адреса области памяти, ассоциированной с переменной, или l- значение (от location value – значение местоположения) – место, где хранится r- значение; присуще только объекту.

С++ для начинающих

81

В выражении

ch = ch - '0';

переменная ch находится и слева и справа от символа операции присваивания. Справа расположено значение для чтения (ch и символьный литерал '0'): ассоциированные с переменной данные считываются из соответствующей области памяти. Слева значение местоположения: в область памяти, соотнесенную с переменной ch, помещается результат вычитания. В общем случае левый операнд операции присваивания должен быть l-

//ошибки компиляции: значения слева не являются l-значениями

//ошибка: литерал - не l-значение

0 = 1;

// ошибка: арифметическое выражение - не l-значение

значением. Мы не можем написать следующие выражения: salary + salary * 0.10 = new_salary;

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

// файл module0.C

должна быть использована в другом, появляются проблемы. Например:

//определяет объект fileName string fileName;

//... присвоить fileName значение

//файл module1.C

//использует объект fileName

//увы, не компилируется:

//fileName не определен в module1.C ifstream input_file( fileName );

С++ требует, чтобы объект был известен до первого обращения к нему. Это вызвано

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

// файл module1.C

помощью инструкции объявления переменной:

//использует объект fileName

//fileName объявляется, то есть программа получает

//информацию об этом объекте без вторичного его определения extern string fileName;