Шестнадцатерична система счисления

Поскольку двоичная система громоздка и трудна для понимания, для упрощения манипулирования с данными было бы полезно иметь возможность быстро и динамично приводить двоичные значения к числам с большим основанием. Оказалось, что преобразовывать двоичные значения к числам шестнадцатеричной системы счисления намного проще и быстрее, чем к десятичным числам. Почему? Давайте сначала рассмотрим, что представляют собой шестнадцатеричные числа.

Для представления шестнадцатеричных чисел используется 16 символов: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, А, В, С, D, E и F. Как видите, последние шесть символов — не цифры, а буквы. Буквы A—F выбраны произвольно, просто как первые буквы латинского алфавита. Вот чему равны граничные значения в шестнадцатеричной системе счисления:

4 3 2 1

16^3 16^2 16^1 16^0

4096 256 16 1

При переводе шестнадцатеричного числа в десятичное можно использовать описанную выше схему (вычислить сумму произведений цифр числа на граничные значения соответствующих порядков). Возьмем, например, число F8C:

F * 256 = 15 * 256 = 3840

8 * 16 = 128

С * 1 = 12 * 1 = 12

3840 + 128 + 1 = 3980

Перевод числа FC в двоичное число лучше всего делать путем первоначального перевода в десятичное, а затем уже в двоичное:

F * 16 = 15 * 16 = 240

С * 1 = 12 * 1 = 12

240 + 12 = 252

Преобразование числа 252(10) в двоичное представление показано в следующей таблице:

Разряд 9 8 7 6 5 4 3 2 1

Степень 2^8 2^7 2^6 2^5 2^4 2 2 2 2

Значение 256 128 64 32 16 8 4 2 1

256 не укладывается ни разу.

1 раз 128 остаток 124

1 раз 64 остаток 60

1 раз 32 остаток 28

1 раз 16 остаток 12

1 раз 8 остаток 4

1 раз 4 остаток 0

0 раз 2

0 раз 1

1 1 1 1 1 1 0 0

Таким образом, мы получили двоичное число 1111 1100.

Теперь оказывается, что, представив это число как два набора, состоящих из четырех цифр, мы можем сделать одно магическое превращение.

Правый набор представляет собой число 1100. В десятичном выражении это число 12, а в шестнадцатеричном — число С.

Левый набор имеет вид 1111, который по основанию 10 представляется как число 15, а по основанию 16 — как число F. Итак, мы получили следующее:

1111 0000

F С

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

1011 0001 1101 0111

Напомню, что в двоичной системе используются следующие граничные значения порядков: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384 и 32768.

1 X 1 = 1

1 x 2 = 2

1 X 4 = 4

0 x 8 = 0

1 x 16 = 16

1 x 32 = 0

1 x 64 = 64

1 x 128 = 128

1 x 256 = 256

1 x 512 = 0

1 Х 1024 = 0

1 x 2048 = 0

1 x 4096 = 4096

1 x 8192 = 8192

1 x 16384 = 0

1 x 32768 = 32768

Итого: 45527

Для преобразования этого числа в шестнадцатеричное вспомним граничные значения порядков в этой системе счислений:

65536 4096 256 16 1

Число 65 536 (значение пятого порядка) не укладывается в числе 45 527, в пятой позиции ставим 0. Число 4096 (значение четвертого порядка) укладывается в числе 45 527 одиннадцать раз с остатком 471. В остатке 471 число 256 (значение третьего порядка) укладывается один раз с остатком 215. В новом остатке 215 число 16 (значение второго порядка) укладывается 13 раз с остатком 7. Таким образом, получаем шестнадцатеричное число BlD7.

Проверим наши математические выкладки:

В (11) * 4096 = 45 056

1 * 256 = 256

D (13) * 16 = 208

7 * 1 = 7

Всего 45 527

Для проверки ускоренного метода перевода возьмем двоичное число 1011000111010111, разделим его на группы по четыре знака: 1011 0001 1101 0111. Каждая из четырех групп затем преобразуется в шестнадцатеричное число:

1011 =

1 x 1 = 1

1 x 2 = 2

0 x 4 = 0

1 x 8 = 8

Всего: 11

Шестнадцатеричное: В

0001 =

1 x 1 = 1

0 x 2 = 0

0 X 4 = 0

0 x 8 = 0

Всего: 1

Шестнадцатеричное: 1

1101 =

1 x 1 = 1

0 x 2 = 0

1 x 4 = 4

1 x 8 = 8

Всего: 13

Шестнадцатеричное: D

0111 =

1 X 1 = 1

1 X 2 = 2

1 x 4 = 4

0 x 8 = 0

Всего: 7

Шестнадцатеричное: 7

Итак, получаем шестнадцатеричное число B1D7

Приложение Г - Ответы

День 1

Контрольные вопросы

1. В чем разница между интерпретаторами и компиляторами?

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

2. Как происходит компиляция исходного кода программы?

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

3. В чем состоит назначение компоновщика?

Задача компоновщика — связать скомпилированный код программы с элементами, взятыми из стандартных библиотек, предоставленных фирмой — изготовителем компилятора, и другими источниками. Компоновщик позволяет формировать отдельные модули программы, а затем соединять эти части в одну большую программу.

4. Какова обычная последовательность действий в цикле разработки?

Редактирование исходного кода, компиляция, компоновка, тестирование, повторение перечисленных выше действий.

Упражнения

1. Инициализирует две целочисленные переменные, а затем выводит их сумму и произведение.

2. Ознакомьтесь с документацией, которая прилагается к вашему компилятору.

3. В первой строке перед словом include нужно поместить символ #.

4. Эта программа выводит на экран слова Hello World, которые завершаются символом разрыва строки.

День 2

Контрольные вопросы

1. В чем разница между компилятором и препроцессором?

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

2. В чем состоит особенность функции main()?

Функция main() вызывается автоматически при каждом выполнении программы.

3. Какие два типа комментариев вы знаете и чем они отличаются друг от друга?

Строки комментариев в стиле C++ задаются двумя символами слеша (//), которые блокируют любой текст до конца текущей строки. Комментарии в стиле языка С заключаются в пары символов (/* */), и все, что находится между этими символами, блокируется от выполнения компилятором. Следует внимательно отнестись к использованию пар символов комментариев, чтобы не заблокировать целый блок программы.

4. Могут ли комментарии быть вложенными?

Да, комментарии в стиле C++ могут быть вложены внутрь комментариев в стиле С. Можно также комментарии в стиле С вкладывать внутрь комментариев в стиле C++, но при этом следует помнить, что комментарии в стиле C++ заканчиваются в конце текущей строки.

5. Могут ли комментарии занимать несколько строк?

Это позволено лишь комментариям в стиле С. Если же вы хотите продолжить на следующей строке комментарии в стиле C++, необходимо поставить в начале второй строки еще одну пару символов (//).

Упражнения

1. Напишите программу, которая выводит на экран сообщение I love C++.

1: #include <iostream.h>

2:

3: int main()

4: {

5: cout << "I love C++\n";

6: return 0;

7: }

2. Напишите самую маленькую программу, которую можно скомпилировать, скомпоновать и выполнить.

int main() { returna 0; }

3. Жучки: введите эту программу и скомпилируйте ее. Почему она дает сбой? Как ее можно исправить?

1: #include <iostream.h>

2: int main()

3: {

4: cout << Is there a bug here?";

5: return 0;

6: }

В строке 4 пропущена открывающая кавычка для строкового выражения.

4. Исправьте ошибку в упражнении 3, после чего перекомпилируйте ее, скомпонуйте и запустите на выполнение.

1: #include <iostream.h>

2: int main()

3: {

4: cout << " Is there а bug fiere?";

5: return 0;

6: }

День 3

Контрольные вопросы

1. В чем разница между целочисленной и вещественной (с плавающей точкой) переменными?

Целочисленные переменные предназначены для работы с целыми числами, а вещественные — с вещественными числами, содержащими плавающую десятичную точку. Числа с плавающей точкой могут быть представлены с использованием мантиссы и экспоненты.

2. Каково различие между типами unsigned short int и long int?

Ключевое слово unsigned означает, что данная целочисленная переменная будет содержать только положительные числа. В большинстве компьютеров для коротких (short) целых чисел отводится 2 байта, а для длинных (long) — 4 байта.

3. Каковы преимущества использования символьной константы вместо литерала? Символьная константа "говорит сама за себя", т.е. само имя константы указывает на ее назначение. Кроме того, при внесении изменений символьную константу достаточно переопределить в одной строке исходного кода, в то время как при использовании литералов программисту придется редактировать код всюду, где встречается этот литерал.

4. Каковы преимущества использования ключевого слова const вместо директивы #define?

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

5. Как влияет на работу программы "хорошее" или "плохое" имя переменной? Хорошее имя переменной говорит о назначении этой переменной; плохое не несет никакой информации. Например, MyAge (МойВозраст) и PeopleOnTheBus (Пассажиров в автобусе) — это хорошие имена переменных, а в таких именах, как xjk и prndl, вероятно, мало пользы.

6. Если перечисление (enum) заданно таким образом, то каково значение его члена Blue?

enum COLOR { WHITE, BLACK = 100, RE0, BLUE, GREEN = 300 }; BLUE = 102

7. Какие из следующих имен переменных являются хорошими, плохими и вообще недопустимыми?

а) Age — хорошее имя;

б) !ex — недопустимое имя;

в) R79J — допустимое, но неудачное имя;

г) TotalIncome — хорошее имя;

д) _Invalid — допустимое, но неудачное имя.

Упражнения

1. Какой тип переменной был бы правильным для хранения следующей информации?

• Ваш возраст.

Unsigned short integer

• Площадь вашего заднего двора. Unsigned long integer или unsigned float

• Количество звезд в галактике. Unsigned double

• Средний уровень выпадения осадков за январь месяц. Unsigned short integer

2. Создайте подходящие имена переменных для хранения этой информации.

а) myAge (МойВозраст)

б) backYardArea (ПлощадьЗаднегоДвора)

в) StarsInGalaxy (ЗвездВГалактике)

г) averageRainFall (СреднемесячныйУровеньОсадков)

3. Объявите константу для числа pi, равного 3.14159.

const float pi = 3.14159;

4. Объявите переменную типа float и инициализируйте ее, используя константу pi.

float myPi = PI;

День 4

Контрольные вопросы

1. Что такое выражение?

Это любой набор операторов, возвращаюший значение.

2. Является ли запись x = 5 + 7 выражением? Каково его значение?

Да, является и возвращает значение 12.

3. Каково значение выражения 201 / 4?

4. Каково значение выражения 201 % 4?

5. Если переменные myAge, а и b имеют тип int, то каковы будут их значения после выполнения выражения:

myAge = 39;

а = myAge++;

b = ++myAge;

myAge: 41, а: 39, b: 41

6. Каково значение выражения 8+2*3?

7. Какая разница между выражениями if(x = 3) и if(x == 3)?

Первое выражение присваивает переменной x значение 3 и возвращает TRUE. Второе выражение проверяет, равно ли значение переменной x числу 3, и возвращает TRUE, если значение переменной x равно 3, и FALSE в противном случае.

8. Будут ли следующие выражения возвращать true или false?

а) 0

FALSE

б) 1

TRUE

в) -1

TRUE

г) x = 0

FALSE

д) x == 0 // предположим, что x имеет значение 0

TRUE

Упражнения

1. Напишите один оператор if, который проверяет две целочисленные переменные и присваивает переменной с большим значением меньшее значение, используя только один дополнительный оператор else.

if (x > у) x = у;

else // у > x || у == x

У = x;

2. Проанализируйте следующую программу. Представьте, что вы ввели три значения. Какой результат вы ожидаете получить?

1: #include <iostream.h>

2: int main()

3: {

4: int а, Ь, с;

5: cout << "Please enter three numbers\n";

6: cout << "а: ";

7: cin >> а;

8: cout << "\nb: ";

9: cin >> b;

10: cout << "\nc: ";

11: cin >> с;

12:

13: if (с = (a-b))

14: { cout << "а: ";

15: cout << а;

16: cout << "minus b: ";

17: cout << b;

18: cout << "equals с: ";

19: cout << с << endl; >

20: else

21: cout << "a-b does not equal с: " << endl;

22: return 0;

23: }

3. Введите программу из упражнения 2; скомпилируйте, скомпонуйте и выполните ее. Введите числа 20, 10 и 50. Вы получали результат, который и ожидали? Почему нет?

Введите числа 20, 10, 50. А вот результат:

а: 20 minus

b: 10 equals

с: 10

Неожиданный результат? Дело в том, что в строке 13 выполняется присваивание, а не проверка равенства.

4. Проанализируйте эту программу и спрогнозируйте результат:

1: #include<iostream.h>

2: int main()

3: {

4: int а = 1, b = 1, с;

5: if (с = (a-b))

6: cout << "The value of c is: " << c;

7: return 0;

8: }

5. Введите, скомпилируйте, скомпонуйте и выполните программу из упражнения 4. Каков был результат? Почему?

6. Поскольку в строке 5 переменной с присваивается значение а - b, то значение присваивания выражения а (1) минус b (1) равно 0. Поскольку 0 означает false (ложь), то выходит, что условие проверки не выполняется и поэтому ничего не выводится.

День 5

Контрольные вопросы

1. В чем разница между объявлением прототипа функции и определением функции?

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

2. Должны ли имена параметров, указанные в прототипе, определении и вызове функции соответствовать друг другу?

Нет. Все параметры идентифицируются позицией, а не по имени.

3. Если функция не возвращает значение, как следует объявить такую функцию? Для возврата функции следует установить тип void.

4. Если не объявить тип возврата, то какой тип будет принят по умолчанию для возвращаемого значения?

Любая функция, в которой явно не объявляется тип возвращаемого значения, возвращает значение типа int.

5. Что такое локальная переменная?

Это переменная, передаваемая или объявленная внутри некоторого блока (обычно функции). Она видима только в пределах этого блока.

6. Что такое область видимости?

Область видимости определяет "продолжительность жизни" локальных и глобальных переменных и обычно устанавливается набором фигурных скобок.

7. Что такое рекурсия?

В общем случае это способность функции вызывать самое себя.

8. Когда следует использовать глобальные переменные?

Глобальные переменные обычно используются, когда многим функциям нужен доступ к одним и тем же данным. В C++ глобальные переменные используются очень редко. Как только вы научитесь создавать статические переменные класса, вы практически не будете обращаться к глобальным переменным.

9. Что такое перегрузка функции?

Это способность записать несколько функций с одним и тем же именем, но с различным числом или типом параметров.

10. Что такое полиморфизм?

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

Упражнения

1. Запишите прототип для функции с именем Perimeter, которая возвращает значение типа unsigned long int и принимает два параметра, имеющих тип unsigned short int.

unsigned long int Perimeter(unsigned short int, unsigned short int);

2. Запишите определение функции Perimeter согласно объявлению в упражнении 1. Два принимаемых ею параметра представляют длину и ширину прямоугольника, а функция возвращает его периметр (удвоенная длина плюс удвоенная ширина).

unsigned long int Perimeter(unsigned short int length, unsigned short int width)

{

return 2*length + 2*width;

}

3. Жучки: что неправильно в этой функции?

#include <iostream.h>

void myFunc(unsigned short int x);

int main()

{

unsigned short int x, у;

у = myFunc(int);

cout << "x: " << x << " у: " << у << "\n";

}

void myFunc(unsigned short int x)

{

return (4*x);

}

Функция объявлена с использованием ключевого слова void, и поэтому она не может возвращать какое-либо значение. Кроме того, при вызове функции myFunc ей следует передать параметр x, а не int.

4. Жучки: что неправильно в этой функции?

#include <iostream.h>

int myFunc(unsigned short int x);

int main()

{

unsigned short int x, у;

у = myFunc(x);

cout << "x: " << X << " у: " << у << "\n";

}

int myFunc(unsigned short int x);

{

return (4*x);

}

Эта функция была бы идеальной, если бы не точка с запятой, поставленная в конце заголовка ее определения.

5. Напишите функцию, которая принимает два параметра типа unsigned short int и возвращает результат деления первого параметра на второй. Функция не должна выполнять операцию деления, если второе число равно нулю, но в этом случае она должна возвратить значение -1.

short int Divider(unsigned short int valOne, unsigned short int valTwo)

{

if (valTwo == 0)

return -1;

else

return valOne / valTwo;

}

6. Напишите программу, которая запрашивает у пользователя два числа и вызывает функцию, записанную при выполнении упражнения 5. Выведите результат или сообщение об ошибке, если функция возвратит значение, равное -1.

#include <iostream.h>

typedef unsigned short int USHORT;

typedef unsigned long int ULONG;

short int Divider(

unsigned short int valone,

unsigned short int valtwo);

int main()

{

USHORT one, two;

short int answer;

cout << "Enter two numbers.\n Number one: ";

cin >> one;

cout << "Number two: ";

cin >> two;

answer = Divider(one, two);

if (answer > -1)

cout << "Answer: " << answer;

else

cout << "Error, can't divide by zero!";

return 0;

}

7. Напишите программу, которая запрашивает число и показатель степени. Напишите рекурсивную функцию, которая возводит число в степень путем многократного умножения числа на самое себя, т.е. если число равно 2, а показатель степени равен 4, то эта функция должна возвратить число 16.

#include <iostream.h>

typedef unsigned short USHORT;

typedef unsigned long ULONG;

ULONG GetPower(USHORT n, USHORT power);

int main()

{

USHORT number, power;

ULONG answer;

cout << "Enter a number: ";

cin >> number;

cout << "To what power? ";

cin >> power;

answer = GetPower(number,power);

cout << number << " to the " << power << "th power is " << answer << endl;

return 0;

}

ULONG GetPower(USHORT n, USHORT power)

{

if(power == 1)

return n;

else

return (n * GetPower(n,power-1));

}

День 6

Контрольные вопросы

1. Что представляет собой оператор прямого доступа и для чего он используется?

Оператор точки прямого доступа представляет собой символ точки (.). Он используется для обращения к членам класса.

2. Что резервирует память — объявление или определение?

Память резервируется определениями переменных. Объявления классов не резервируют память.

3. Объявление класса является его интерфейсом или выполнением?

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

4. Какова разница между открытыми (public) и закрытыми (private) данными- членами?

К открытым переменным-членам могут обращаться клиенты класса, а к закрытым могут получить доступ только функции-члены класса.

5. Могут ли функции-члены быть закрытыми?

Да. Как функции-члены, так и переменные-члены могут быть закрытыми.

6. Могут ли переменные-члены быть открытыми?

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

7. Если объявить два объекта класса Cat, могут ли они иметь различные значения своих переменных-членов itsAge?

Да. Каждый объект класса имеет свои собственные переменные-члены.

8. Ну5кно ли объявления класса завершать точкой с запятой? А определения методов класса?

Объявления класса заканчиваются точкой с запятой после закрывающей фигурной скобки, а определения функций-членов — нет.

9. Как бы выглядел заголовок функции-члена Meow класса Cat, которая не принимает никаких параметров и возвращает значение типа void?

Заголовок функции-члена Meow() класса Cat, которая не принимает параметров и возвращает значение типа void, должен иметь следующий вид:

void Cat::Meow()

10. Какая функция вызывается для выполнения инициализации класса? Для инициализации класса вызывается конструктор.

Упражнения

1. Напишите программу, которая объявляет класс с именем Employee (Служащие) с такими переменными-членами: age (возраст), yearsOfService (стаж работы) и Salary (зарплата).

class Employee

{

int Age;

int YearsOfService;

int Salary;

};

2. Перепишите класс Employee, чтобы сделать данные-члены закрытыми и обеспечить открытые методы доступа для чтения и установки всех данных-членов.

class Employee

{

public:

int GetAge() const;

void SetAge(int age);

int GetYearsOfService() const;

void SetYearsOfService(int years);

int GetSalary() const;

void SetSalary(int salary);

private:

int Age;

int YearsOfService;

int Salary;

};

3. Напишите программу с использованием класса Employee, которая создает два объекта класса Employee; устанавливает данные-члены age, YearsOfService и Salary, а затем выводит их значения.

int main()

{

Employee John;

Employee Sally;

John.SetAge(30);

John.SetYearsOfService(5);

John.SetSalary(50000);

Sally.SetAge(32);

Sally.SetYearsOfService(8);

Sally.SetSalary(40000);

cout << "At AcmeSexist company, John and Sally have the same job.\n";

cout << "John is " << John.GetAge() << " years old and he has been with";

cout << "the firm for " << John.GetYearsOfService << " years.\n";

cout << "John earns $" << John.GetSalary << " dollars per year.\n\n";

cout << "Sally, on the other hand is " << Sally.GetAge() << " years old and has";

cout << "been with the company " << Sally.GetYearsOfService;

cout << " years. Yet Sally only makes $" << Sally.GetSalary();

cout << " dollars per year! Something here is unfair.";

return 0;

}

4. На основе программы из упражнения 3 создайте метод класса Employee, который сообщает, сколько тысяч долларов зарабатывает служащий, округляя ответ до 1 000 долларов.

float Employee:GetRoundedThousands() const

{

return (Salary+500) / 1000;

}

5. Измените класс Employee так, чтобы можно было инициализировать данные-члены

age, YearsOfService и Salary в процессе "создания" служащего.

class Employee

{

public:

Employee(int Age, int yearsOfService, int salary);

int GetAge() const;

void SetAge(int Age);

int GetYearsOfService() const;

void SetYearsOfService(int years);

int GetSalary() const;

void SetSalary(int salary);

private:

int Age;

int YearsOfService;

int Salary;

};

6. Жучки: что неправильно в следующем объявлении?

class Square

{

public:

int Side;

}

Объявления классов должны завершаться точкой с запятой.

7. Жучки: что весьма полезное отсутствует в следующем объявлении класса?

class Cat

{

int GetAge() const;

private:

int itsAge;

};

Метод доступа к данным GetAge() является закрытым по умолчанию. Помните: все члены класса считаются закрытыми, если не оговорено иначе.

8. Жучки: какие три ошибки обнаружит компилятор в этом коде?

class TV

{

public:

void SetStation(int Station);

int GetStation() const;

private:

int itsStation;

};

main()

{

TV myTV;

myTV.itsStation = 9;

TV.SetStation(10);

TV myOtherTv(2);

}

Нельзя обращаться к переменной itsStation непосредственно. Это закрытая пере- менная-член.

Нельзя вызывать функцию-член SetStation()npHMO в классе. Метод SetStation() можно вызывать только для объекта.

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

День 7

Контрольные вопросы

1. Можно ли в цикле for инициализировать сразу несколько переменных-счетчиков?

Можно, для этого в строке инициализации их следует разделить запятыми, например:

for <х = 0. у = 10; х < 100; х++, у++)

2. Почему следует избегать использование оператора goto?

Оператор Goto выполняет переход в любом направлении к любой произвольной

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

3. Можно ли с помошью оператора for организовать цикл, тело которого не будет выполняться?

Да. Если условие после инициализации оказывается ложным (FALSE), то тело цикла

for никогда не будет выполняться. Вот пример:

for (lnt х = 100; х < 100; х++)

4. Можно ли организовать цикл while внутри цикла for?

Да. Любой цикл может быть вложен внутрь любого другого цикла.

5. Можно ли организовать цикл, который никогда не завершится? Приведите пример.

Да. Ниже приведены примеры как для цикла for, так и для цикла wtiile:

for(;;)

{

// Этот цикл никогда не заканчивается!

}

whlle(1)

{

// Этот цикл никогда не заканчивается!

}

6. Что происходит при запуске бесконечного цикла?

Программа зависнет и придется перезагрузить компьютер.

Упражнения

1. Каким будет значение переменной х после завершения никла

for (int X = 0; X < 100; х++)?

2. Создайте вложенный цикл for, заполняющий нулями массив размером 10x10.

for (int = 0; i< 10; i++)

{

for ( int j = 0; j< 10; j++)

cout << О ;

cout << "\n";

}

3. Организуйте цикл for, счетчик которого изменяется от 100 до 200 с шагом 2.

for (int х = 100; х<=200; х*=2)

4. Организуйте цикл while, счетчик которого изменяется от 100 до 200 с шагом 2.

int х = 100;

while (х <= 200)

х+= 2:

5. Организуйте цикл do...while, счетчик которого изменяется от 100 до 200 с шагом 2.

int х = 100;

do

{

х+=2:

} while (х <= 200);

6. Жучки: найдите ошибку в приведенном фрагменте программы:

int counter = 0;

while (counter < 10)

{

cout << "counter: " << counter;

}

Нет выражения, в котором выполнялось бы приращение счетчика counter, поэтому цикл while никогда не закончится.

7. Жучки: найдите ошибку в приведенном фрагменте программы:

for (int counter = 0; counter < 10; counter++);

cout << counter << " ";

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

8. Жучки: найдите ошибку в приведенном фрагменте программы:

int counter = 100;

while (counter < 10)

{

cout << "counter now: " << counter;

counter—-;

}

Счетчик counter инициализирован числом 100, но проверяемое условие таково, что, если значение переменной counter больше 10, выражение условия возвратит FALSE и тело цикла никогда не будет выполнено. Если первую строку заменить вариантом int counter = 5;, то этот цикл не закончится до тех пор, пока не выполнится обратный отсчет до минимально возможного значения счетчика. Поскольку тип счетчика int по умолчанию определяется как signed, то мы получим бесконечный цикл.

9. Жучки: найдите ошибку в приведенном фрагменте программы:

cout << "Enter a number between 0 and 5: ";

cin >> theNumber;

switch (theNumber)

{

case 0:

doZero();

case 1: // идем дальше

case 2: П идем дальше

case 3: // идем дальше

case 4: Ц идем дальше

case 5:

doOneToFive();

break;

default:

doDefault();

break;

}

После оператора case 0, видимо, должен быть оператор break. Если это не так, то ситуацию следовало бы разъяснить с помошью комментария.

День 8

Контрольные вопросы

1. Какой оператор используется для получения адреса переменной?

Для возвращения адреса любой переменной используется оператор получения адреса (&).

2. Какой оператор позволяет получить значение, записанное по адресу, содержащемуся в указателе?

Для доступа к значению, сохраненному по адресу, содержащемуся в указателе, используется оператор разыменования (*).

3. Что такое указатель?

Это переменная, которая содержит адрес другой переменной.

4. В чем различие между адресом, который хранится в указателе, и значением, записанным по этому адресу?

Адрес, сохраненный в указателе, — это адрес другой переменной. Значение, сохраненное по этому адресу, — это любое значение, сохраняемое в переменной, на которую ссылается указатель. Оператор разыменования (*) возвращает значение, сохраненное по адресу, который хранится в указателе.

5. В чем различие между оператором разыменования и оператором получения адреса?

Оператор разыменования (*) возвращает значение, хранящееся по адресу, на который ссылается указатель. А оператор получения адреса (&) возвращает адрес переменной в памяти.

6. В чем различие между следующими объявлениями: const int * ptrOne и int * const ptrTwo?

Выражение const int * ptrOne объявляет, что переменная ptrOne представляет собой указатель на постоянное целое число. Само это целое число не может быть изменено с помощью данного указателя.

Выражение int * const pi rTwo объявляет, что переменная ptrTwo является постоянным указателем на некоторое целое число. После такой инициализации этот указатель не может быть переназначен.

Упражнения

1. Объясните смысл следующих объявлений переменных.

• int * рОпе;

• int vTwo;

• int * pThree = &vTwo;

Ответы:

а) int * pOne: — объявляет указатель на целое значение;

б) int vTwo — объявляет целочисленную переменную;

в) int * pThroe = &vTwo; — объявляет указатель на целое значение и инициализирует его адресом переменной.

2. Допустим, в программе объявлена переменная yourAge типа unsigned short. Как объявить указатель, позволяющий манипулировать этой переменной?

unsigned short *рАgе = AyourAgo;

3. С помошью указателя присвойте переменной yourAge значение 50.

*рАge =50

4. Напишите небольшую программу и объявите в ней переменную типа int и указатель на этот тип. Сохраните адрес переменной в указателе. Используя указатель, присвойте переменной какое-либо значение.

int theInteger;

int *plnteger = &theInteger:

*plnteger = 5:

5. Жучки: найдите ошибку в следующем фрагменте программы:

#include <iostream.h>

int main()

{

int *pInt;

*pInt = 9;

cout << " The value at pInt: " << *pInt;

return 0;

}

Указатель pInt должен быть инициализирован. Поскольку он не был инициализирован и ему не присвоен адрес какой-либо ячейки памяти, то он указывает на. случайное место в памяти. Присвоение этому случайному месту числа 9 является опасной ошибкой.

6. Жучки: найдите ошибку в следующем фрагменте программы:

int main()

{

int SomeVariable = 5.

cout << "SomeVariable: " << SomeVariable << "\n";

int *pVar = & SomeVariable;

pVar = 9:

cout << "SomeVariable: " << *pVar << "\n":

return 0;

}

Возможно, программист хотел присвоить число 9 переменной, на которую указывает указатель pVar. К сожалению, число 9 было присвоено самому указателю pVar, поскольку был опушен оператор косвенного доступа (*)• Если указатель pVar используется для присвоения ему значения, такое программирование неминуемо приведет к тяжелым последствиям.

День 9

Контрольные вопросы

1. В чем разница между ссылкой и указателем?

Ссылка — это условное название (псевдоним), а указатель — это переменная, которая содержит адрес. Ссылки не могут быть нулевыми и не могут переназначаться.

2. Когда нужно использовать именно указатель, а не ссылку?

Если в программе нужно назначить указателю новую переменную или если указатель нужно сделать нулевым.

3. Что возвращает оператор new, если для создания нового объекта недостаточно памяти?

Нулевой указатель.

4. Что представляет собой константная ссылка?

Это сокращенный вариант определения ссылки на константный объект.

5. В чем разница между передачей объекта как ссылки и передачей ссылки в функцию?

Передача объекта как ссылки означает, что локальная копия для этого объекта создаваться не будет. Этого можно достичь путем передачи в качестве параметра ссылки или указателя.

Упражнения

1. Напишите программу, которая объявляет переменную типа int, ссылку на значение типа int и указатель на значение типа int. Используйте указатель и ссылку для управления значением переменной типа int.

int main()

{

int varOne;

int& rVar = varOne;

int* pVar = &varOne;

rVar = 5:

*pVar = 7:

return 0;

}

2. Напишите программу, которая объявляет константный указатель на постоянное целое значение. Инициализируйте его, чтобы он указывал на целочисленную переменную varOne. Присвойте переменной varOne значение 6. Используйте указатель, чтобы присвоить переменной varOne значение 7. Создайте вторую целочисленную переменную varTwo. Переназначьте указатель, чтобы он указывал на переменную varTwo. Пока не компилируйте это упражнение.

int main()

{

int varOne;

const int * const pVar = dvarOne;

*pVar = 7;

int varTwo;

pVar = &varTwo.

return 0;

}

3. Скомпилируйте программу, написанную в упражнении 2. Какие действия компилятор считает ошибочными? Какие строки генерируют предупреждения?

Нельзя присваивать значение константному объекту и нельзя переназначать константный указатель.

4. Напишите программу, которая создает блуждающий указатель.

int main()

{

int >> pVar;

*pVar = 9;

return 0;

}

5. Исправьте программу из упражнения 4, чтобы блуждающий указатель стал нулевым.

int main()

{

int varOne;

int * pVar = ivarOne;

*pVar = 9;

return 0.

}

6. Напишите программу, которая приводит к утечке памяти.

#include <iostream.h>

int * FuncOne();

int main()

{

int * pInt = FuncOneO;

cout << "the value of pint in main is: " << *pInt << endl:

return 0;

}

int * FuncOne()

{

int * pInt = new int (5):

cout << "the value of pint in FuncOne is: " << *pInt << endl:

return pInt;

}

7. Исправьте программу из упражнения 6.

#include <iostream.h>

int FuncOne();

int main()

{

int theInt = FuncOne();

cout << "the value of pint in main is: " << theInt << endl;

return 0:

}

int FuncOne()

{

int * pInt = new int (5);

cout << "the value of pint in FuncOne is: " << <<pInt << endl;

delete pint;

return temp;

}

8. Жучки: что неправильно в этой программе?

1: #include <iostream.h>

3: class CAT

4: {

5: public:

6: CAT(int age) { itsAge = age; }

7: ~CAT(){ }

8: int GetAge() const { return itsAge; }

9: private:

10: int itsAge:

11: };

12:

13: CAT & MakeCat(int age):

14: int main()

15: {

16: int age = 7;

17: CAT Boots = MakeCat(age);

18: cout << "Boots is " << Boots.GetAge() << " years old\n";

19: return 0:

20: }

22: CAT & MakeCat(int age)

23: {

24: CAT * pCat = new CAT(age);

25: return *pCat;

26: }

Функция MakeCat возвращает ссылку на объект класса CAT, созданный в свободной памяти. Но поскольку здесь не предусмотрена операция по освобождению этой памяти, создание нового объекта приводит к ее утечке.

9. Исправьте программу из упражнения 8.

1: #include <iostream.h>

2:

3: class CAT

4: {

5: public:

6: CAT(int age) { itsAge = age; }

7: ~CAT(){ }

8: int GetAgeO const { return itsAge;}

9: private:

10: int itsAge;

11: };

13: CAT * MakeCat(int age);

14: int main()

15: {

16: int age = 7;

17: CAT * Boots = MakeCat(age);

18: cout << "Boots is " << Boots

Наши рекомендации