Листинг 3.1. Программа, основанная на материалах недели 3



1: // ************************************

2: //

3: // Название: Обзор недели 3

4: //

5: // Файл: Week3

6: //

7: // Описание: Программа с использованием связанного списка

8: // на основе шаблона с обработкой исключительных ситуаций

9: //

10: // Классы: PART - хранит номера запчастей и потенциально другую

11: // информацию о запчастях. Зто будет

12: // пример класса для хранения списка.

13: // Обратите внимание на использование

14: // оператора << для печати информации о запчасти

15: // на основе его типа и времени выполнения,

16: //

17: // Node - действует как узел в классе List

18: //

19: // List - список, основанный на шаблоне, который

20: // обеспечивает работу связанного списка

21: //

22: //

23: // Автор: Jesse Liberty (jl)

24: //

25: // Разработан: Pentium 200 Pro. 128MB RAM MVC 5.0

26: //

27: // Требования: Не зависит от платформы

28: //

29: // История создания: 9/94 - Первый выпуск (jl)

30: // 4/97 - Обновлено (jl)

31: // ************************************

32:

33: #include <iostream.h>

34:

35: // классы исключений

36: class Exception { };

37: class OutOfMemory : public Exception{ };

38: class NullNode : public Exception{ };

39: class EmptyList : public Exception { };

40: class BoundsError : public Exception { };

41:

42:

43: // **************** Part **************

44: // Абстрактный базовый класс запчастей

45: class Part

46: {

47: public:

48: Part():its0bjectNumber(1) { }

49: Part(int 0bjectNumber):its0bjectNumber(ObjectNumber){ }

50: virtual ~Part(){ };

51: int GetObjectNumber() const { return itsObjectNumber; }

52: virtual void Display() const =0; // функция будет замещена в производном классе

53:

54: private:

55: int itsObjectNumber;

56: };

57:

58: // выполнение чистой виртуальной функции, необходимой

59: // для связывания объектов производного класса

60: void Part::Display() const

61: {

62: cout << "\nPart Number: " << itsObjectNumber << endl;

63: }

64:

65: // Этот оператор << будет вызываться для всех объектов запчастей.

66: // Его не нужно объявлять другом, поскольку он не обращается к закрытым данным.

67: // Он вызывает метод Display(), в результате чего реализуется полиморфизм классов.

68: // Было бы не плохо замещать функцию оператора для разных

69: // типов thePart, но C++ не поддерживает контравариантность

70: ostream& operator<<( ostream& theStream,Part& thePart)

71: {

72: thePart.Display(); // косвенная реализация полиморфизма оператора вывода!

73: return theStream;

74: }

75:

76: // **************** Car Part ************

77: class CarPart : public Part

78: {

79: public:

80: CarPart():itsModelYear(94){ }

81: CarPart(int year, int partNumber);

82: int GetModelYear() const { return itsModelYear; }

83: virtual void Display() const;

84: private:

85: int itsModelYear;

86: };

87:

88: CarPart::CarPart(int year, int partNumber):

89: itsModelYear(year),

90: Part(partNumber)

91: { }

92:

93: void CarPart::Display() const

94: {

95: Part::Display();

96: cout << "Model Year: " << itsModelYear << endl;

97: }

98:

99: // **************** AirPlane Part ************

100: class AirPlanePart : public Part

101: {

102: public:

103: AirPlanePart():itsEngineNumber(1){ } ;

104: AirPlanePart(int EngineNumber, int PartNumber);

105: virtual void Display() const;

106: int GetEngineNumber()const { return itsEngineNumber; }

107: private:

108: int itsEngineNumber;

109: };

110:

111: AirPlanePart::AirPlanePart(int EngineNumber, int PartNumber):

112: itsEngineNumber(EngineNumber),

113: Part(PartNumber)

114: { }

115:

116: void AirPlanePart::Display() const

117: {

118: Part::Display();

119: cout << "Engine No,: " << itsEngineNumber << endl;

120: }

121:

122: // Обьявление класса List

123: template <class T>

124: class List;

125:

126: // **************** Node ************

127: // Общий узел, который можно добавить к списку

128: // **********************************

129:

130: template <class T>

131: class Node

132: {

133: public:

134: friend class List<T>;

135: Node (T*);

136: ~Node();

137: void SetNext(Node * node) { itsNext = node; }

138: Node * GetNext() const;

139: T * GetObject() const;

140: private:

141: T* its0bject;

142: Node * itsNext;

143: };

144:

145: // Выполнение узла...

146:

147: template <class T>

148: Node<T>::Node(T* p0jbect):

149: itsObject(pOjbect),

150: itsNext(0)

151: { }

152:

153: template <class T>

154: Node<T>::~Node()

155: {

156: delete its0bject;

157: itsObject = 0;

158: delete itsNext;

159: itsNext = 0;

160: }

161:

162: // Возвращает значение NULL, если нет следующего узла

163: template <class T>

164: Node<T> * Node<T>::GetNext() const

165: {

166: return itsNext;

167: }

168:

169: template <class T>

170: T * Node<T>::GetObject() const

171: {

172: if (itsObject)

173: return itsObject;

174: else

175: throw NullNode();

176: }

177:

178: // **************** List ************

179: // Общий шаблон списка

180: // Работает с любым нумерованным объектом

181: // **********************************

182: template <olass T>

183: class List

184: {

185: public:

186: List();

187: ~List();

188:

189: T* Find(int & position, int 0bjectNumber) const;

190: T* GetFirst() const;

191: void Insert(T *);

192: T* operator[](int) const;

193: int GetCount() const { return itsCount; }

194: private:

195: Node<T> * pHead;

196: int itsCount;

197: };

198:

199: // Выполнение списка...

200: template <class T>

201: List<T>::List();

202: pHead(0),

203: itsCount(0)

204: { }

205:

206: template <class T>

207: List<T>::~List()

208: {

209: delete pHead;

210: }

211:

212: template <class T>

213: T* List<T>::GetFirst() const

214: {

215: if (pHead)

216: return pHead->itsObject;

217: else

218: throw EmptyList();

219: }

220:

221: template <class T>

222: T * List<T>::operator[](int offSet) const

223: {

224: Node<T>* pNode = pHead;

225:

226: if (!pHead)

227: throw EmptyList();

228:

229: if (offSet > itsCount)

230: throw BoundsError();

231:

232: for (int i=0;i<offSet; i++)

233: pNode = pNode->itsNext;

234:

235: return pNode->itsObject;

236: }

237:

238: // Находим данный обьект в списке на основе его идентификационного номера (id)

239: template <class T>

240: T* List<T>::Find(int & position, int 0bjectNumber) const

241: {

242: Node<T> * pNode = 0;

243: for (pNode = pHead, position = 0;

244: pNode!=NULL;

245: pNode = pNode->itsNext, position++)

246: {

247: if (pNode->itsObject->GetObjectNumber() == 0bjectNumber)

248: break;

249: }

250: if (pNode == NULL)

251: return NULL;

252: else

253: return pNode->itsObject;

254: }

255:

256: // добавляем в список, если номер объекта уникален

257: template <class T>

258: void List<T>::Insert(T* pObject)

259: {

260: Node<T> * pNode = new Node<T>(p0bject);

261: Node<T> * pCurrent = pHead;

262: Node<T> * pNext = 0;

263:

264: int New = p0bject->Get0bjectNumber();

265: int Next = 0;

266: itsCount++;

267:

268: if (!pHead)

269: {

270: pHead = pNode;

271: return;

272: }

273:

274: // если номер текущего объекта меньше номера головного,

275: // то этот объект становится новым головным узлом

276: if (pHead->itsObject->GetObjectNumber() > New)

277: {

278: pNode->itsNext = pHead;

279: pHead = pNode;

280: return;

281: }

282:

283: for (;;)

284: {

285: // если нет следующего обьекта, добавляем в конец текущий объект

286: if (!pCurrent->itsNext)

287: {

288: pCurrent->itsNext = pNode;

289: return;

290: }

291:

292: // если данный объект больше текущего, но меньше следующего,

293: // то вставляем его между ними, в противном случае переходим к следующему объекту

294: pNext = pCurrent->itsNext;

295: Next = pNext->itsObject->GetObjectNumber();

296: if (Next > New)

297: {

298: pCurrent->itsNext = pNode;

299: pNode->itsNext = pNext;

300: return;

301: }

302: pCurrent = pNext;

303: }

304: }

305:

306:

307: int main()

308: {

309: List<Part> theList;

310: int choice;

311: int ObjectNumber;

312: int value;

313: Part * pPart;

314: while (1)

315: {

316: cout << "(0)Quit (1)Car (2)Plane: ";

317: cin >> choice;

318:

319: if (!choice)

320: break;

321:

322: cout << " New PartNumber?: ";

323: cin >> ObjectNumber;

324:

325: if (choice == 1)

326: {

327: cout << "Model Year?: ";

328: cin >> value;

329: try

330: {

331: pPart = new CarPart(value,ObjectNumber);

332: }

333: catch (OutOfMemory)

334: {

335: cout << "Not enough memory; Exiting..." << endl;

336: return 1;

337: }

338: }

339: else

340: {

341: cout << "Engine Number?: ";

342: cin >> value;

343: try

344: {

345: pPart = new AirPlanePart(value,ObjectNumber);

346: }

347: catch (OutOfMemory)

348: {

349: cout << "Not enough memory: Exiting..." << endl;

350: return 1;

351: }

352: }

353: try

354: {

355: theList.Insert(pPart);

356: }

357: catch (NullNode)

358: {

359: cout << "The list is broken, and the node is null!" << endl;

360: return 1;

361: }

362: catch (EmptyList)

363: {

364: cout << "The list is empty!" << endl;

365: return 1;

366: }

367: }

368: try

369: {

370: for (int i = 0; i < theList.GetCount(); i++ )

371: cout << *(theList[i]);

372: }

373: catch (NullNode)

374: {

375: cout << "The list is broken, and the node is null!" << endl;

376: return 1;

377: }

378: catch (EmptyList)

379: {

380: cout << "The list is empty!" << endl;

381: return 1;

382: }

383: catch (BoundsError)

384: {

385: cout << "Tried to read beyond the end of the list!" << endl;

386: return 1;

387: }

388: return 0;

389: }

 

Результат:

(0)Quit (1)Car (2)Plane: 1

New PartNumber?: 2837

Model Year? 90

(0)Quit (1)Car (2)Plane: 2

New PartNumber?: 378

Engine Number?: 4938

(0)Quit (1)Car (2)Plane: 1

New PartNumber?: 4499

Model Year? 94

(0)Quit (1)Car (2)Plane: 1

New PartNumber?: 3000

Model Year? 93

(0)Quit (1)Car (2)Plane: 0

Part Number: 378

Engine No. 4938

Part Number: 2837

Model Year: 90

Part Number: 3000

Model Year: 93

Part Number 4499

Model Year: 94

 

Анализ: Итоговая программа, основанная на материале за неделю 3, — это модификация программы, приведенной в обзорной главе по материалам за неделю 2. Изменения заключались в добавлении шаблона, обработке объекта ostream и исключительных ситуаций. Результаты работы обеих программ идентичны.

В строках 36—40 объявляется ряд классов исключений. В этой программе используется несколько примитивная обработка исключительных ситуаций. Классы исключений не содержат никаких данных или методов, они служат флагами для перехвата блоками catch, которые выводят простые предупреждения, а затем выполняют выход.

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

В строке 45 объявляется абстрактный класс Part, причем точно так же, как это было сделано в листинге, обобщающем материал за неделю 2. Единственное интересное изменение здесь — это использование оператора operator<<(), который не является членом класса (он объявляется в строках 70—74). Обратите внимание, что он не является ни членом класса запчастей Part, ни другом класса Part. Он просто принимает в качестве одного из своих параметров ссылку на класс Part.

Возможно, вы бы хотели иметь замещенный оператор operator<<() для объектов классов CarPart и AirPlanePart с учетом различий в типах объектов. Но поскоДьку программа передает указатель на объект базового класса Part, а не указатель на указатель производных классов CarPart и AirPlanePart, то выбор правильной версии функции пришлось бы основывать не на типе объекта, а на типе одного из параметров функции. Это явление называется контравариантностью и не поддерживается в C++.

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

Виртуальные функции также не будут здесь работать, поскольку оператор operator<< не является функцией-членом класса запчастей Part. Вы не можете сделать оператор operator<< функцией-членом класса Part, потому что в программе потребуется выполнить следующий вызов:

cout << thePart

Это означает, что фактически вызов относится к объекту cout.operator<<(Part&), а объект cout не имеет версии оператора operator<<, который принимает ссылку на класс запчастей Part!

Чтобы обойти это ограничение, в приведенной выше программе используется только один оператор operator<<, принимающий ссылку на класс Part. Затем вызывается метод Display(), который является виртуальной функцией-членом, в результате чего вызывается правильная версия этого метода.

В строках 130—143 класс Node определяется как шаблон. Он играет ту же роль, что и класс Node в программе из обзора за неделю 2, но эта версия класса Node не связана с объектом класса Part. Это значит, что данный класс может создавать узел фактически для любого типа объекта.

Обратите внимание: если вы попытаетесь получить объект из класса Node и окажется, что не существует никакого объекта, то такая ситуация рассматривается как исключительная и исключение генерируется в строке 175.

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

В строках 307—308 управляющая программа создает список двух типов объектов класса Part, а затем печатает значения объектов в списке, используя стандартные потоки вывода.

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

 

Вопросы и ответы

В комментарии, содержащемся в строках 65-69 говорится, что C++ не поддерживает контравариантность. Что такое контравариантность?

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

 

Предупреждение: ВНИМАНИЕ: Этот листинг не будет скомпилирован!

 

Листинг 3.2. Пример контравариантности

#include <iostream.h>

class Animal

{

public:

virtual void Speak() { cout << "Animal Speaks\n";}

};

class Dog : public Animal

{

public:

void Speak() { cout << "Dog Speaks\n"; }

};

class Cat : public Animal

{

public:

void Speak() { cout << "Cat Speaks\n"; }

};

void DoIt(Cat*);

void DoIt(Dog*);

int main()

{

Animal * pA = new Dog;

DoIt(pA);

return 0;

}

void DoIt(Cat * с)

{

cout << "They passed а cat!\n" << endl;

c->Speak();

}

void DoIt(Dog * d)

{

cout << "They passed a dog!\n" << endl;

d->Speak();

}

 

Но в C++ эту проблему можно решить с помощью виртуальной функции.

 

#include<iostream.h>

class Animal

{

public:

virtual void Speak() { cout << "Animal Speaks\n"; }

};

class Dog : public Animal

{

public:

void Speak() { cout << "Dog Speaks\n"; }

};

class Cat : public Animal

{

public:

void Speak() { cout << "Cat Speaks\n"; }

};

void DoIt(Animal*);

int main()

{

Animal * pA = new Dog;

DoIt(pA);

return 0;

}

void DoIt(Animal * с)

{

cout << "They passed some kind of animal\n" << endl;

c->Speak();

}

 

 

Приложение А

 

Приоритеты операторов

 

Важно понять, что операторы имеют приоритеты, но запоминать их совсем не обязательно.

Приоритет оператора определяет последовательность, в которой программа выполняет операторы в выражении или формуле. Если один оператор имеет приоритет над другим оператором, то он выполняется первым.

Приоритет оператора убывает с увеличением номера категории. Все операторы одной категории имеют равный приоритет. Унарные операторы (категория 3), условный оператор (категория 14) и операторы присваивания (категория 15) ассоциируются справа налево, все остальные — слева направо. В приведенной ниже таблице операторы перечислены по категориям в порядке убывания их приоритетности.

 

Категория: 1 (Наивысшего приоритета)

Название или действие: Разрешение обасти видимости, индексирования

Оператор: :: []

 

Категория: 2

Название или действие: Прямое и косвенное обращение к члену класса

Оператор: . ->

Название или действие: Вызов функции

Оператор: ()

Название или действие: Постфиксные инкремент и декремент

Оператор: ++ --

 

Ктегория: 3 (унарные)

Название или действие: Префиксные инкремент и декремент

Оператор: ++ --

Название или действие: Размер

Оператор: sizeof, sizeof()

Название или действие: Дополнение до единицы и логическое отрицание

Оператор: ^ !

Название или действие: Унарные минус и плюс

Оператор: - +

Название или действие: Получение адреса и разыменование

Оператор: ? *

Название или действие: оздание и удаление динамического объекта

Оператор: new, new[], delete, delete[]

Название или действие: Приведение типа

Оператор: casting

 

Категория: 4 (мультипликтивные)

Название или действие: Умножение, деление, деление по модулю

Оператор: * / %

 

Категория: 5 (аддитивные)

Название или действие: Бинарный плюс, бинарный минус

Оператор: + -

 

Категория: 6 (сдвига)

Название или действие: Вывода и ввода

Оператор: <<, >>

 

Категория: 7 (отношения)

Название или действие: Меньше, меньше или равно, больше, больше или равно

Оператор: <, <=, >, =>

 

Категория: 8 (равенства)

Название или действие: Равно, не равно

Оператор: ==, !=

 

Категория: 9

Название или действие: Побитовое И

Оператор: &

 

Категория: 10

Название или действие: Побитовое исключающее ИЛИ

Оператор: ^

 

Категория: 11

Название или действие: Побитовое ИЛИ

Оператор: |

 

Категория: 12

Название или действие: Логическое И

Оператор: &&

 

Категория: 13

Название или действие: Логическое ИЛИ

Оператор: ||

 

Категория: 14

Название или действие: Условный

Оператор: ?:

 

Категория: 15 (присваивания)

Название или действие: Простое присваивание

Оператор: =

Название или действие: Присваивание с умножением и делением

Оператор: *= /=

Название или действие: Присваивание с делением по модулю

Оператор: %=

Название или действие: Присваивание с суммой и разностью

Оператор: += -=

Название или действие: Присваивание со сдвигом

Оператор: <<= >>=

Название или действие: Присваивание с побитовым И, включающим ИЛИ и исключающим ИЛИ

Оператор: &= |= ^=

Название или действие: Генерация исключения

Оператор: throw

Название или действие: Запятая

Оператор: ,

 

 

Приложение Б

 

Ключевые слова C++

 

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

auto, break, case, catch, char, class, const, continue, default, delete, do, double, else, enum, extern, float, for, friend, goto, if, int, long, mutable, new, operator, private, protected, public, register, return, short, signed, sizeof, static, struct, switch, template, this, throw, typedef, union, unsigned, virtual, void, volatile, while

 

 

Приложение В

 

Двоичные о числа

 

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

Понимание двоичных и шестнадцатеричных чисел потребует по-новому взглянуть на число 145 и увидеть в нем не число, а некоторый код для его выражения,

Начнем с малого. Рассмотрим взаимоотношения между числом три и символом "3". Символ числа (цифра) 3 — это некая "закорючка" на листе бумаги, число три — это некоторая идея или понятие. Определенная цифра используется для представления определенного числа.

Отличие между идеей и символом становится яснее, если осознавать, что для представления одного и того же понятия могут использоваться совершенно разные символы: три, 3, | | |, III или ***.

В десятичной системе счисления для представления чисел используются цифры 0, 1, 2, 3, 4, 5, 6, 7, 8 и 9. Как же представляется число десять?

Здесь возможны разные варианты. Можно было бы для представления этого понятия использовать букву А или "сороконожку" IIIIIIIIII, Римляне использовали символ X. В арабской системе (которой придерживаемся и мы) для представления числовых значений играет роль комбинация базовых десяти цифр. Первая (самая крайняя) позиция, или порядок, используется для единиц, а расположенная слева от нее — для десятков. Следовательно, число пятнадцать представляется как 15 (читается как "один, пять" ), т.е. 1 десяток и 5 единиц.

Итак, вырисовываются некоторые правила, позволяющие сделать ряд обобщений.

1. Для представления чисел по основанию 10 используются цифры 0-9.

2. Порядок представляет собой степень числа десять: единицы (1), десятки (10), сотни (100) и т.д.

3. Поскольку третья позиция в числе представляет сотни, то самым большим двузначным числом может быть 99. В общем случае, используя n позиций, можно представить числа от 0 до (Юп-1). Следовательно, с помощью трех позиций можно представить числа от 0 до (103-1), или 0-999.

 

Другие системы счисления

 

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

010201025301020100

1. Для представления чисел по основанию 8 используются цифры 0-7.

2. Позиции разных порядков представляют собой степени числа восемь: единицы (1), восьмерки (8), 64-ки и т.д.

3. Используя n позиций, можно представить числа от 0 до (8n-1).

Чтобы различать числа, написанные с использованием разных оснований, это основание записывают рядом с числом как нижний индекс. Тогда число пятнадцать по основанию 10 следует записать как 15(10) и читать как "один, пять по основанию десять".

Таким образом, для представления числа 15(10) по основанию 8 следует записать 17(8). Это читается как "один, семь по основанию восемь". Обратите внимание, что это число также можно прочитать как "пятнадцать", поскольку именно его мы и имеем в виду, просто используем другое обозначение.

Откуда взялось число 17? Цифра 1 означает одну восьмерку, а цифра 7 означает 7 единиц. Одна восьмерка плюс семь единиц равно пятнадцати. Рассмотрим пятнадцать

звездочек: ***** *****

*****

Наше естественное желание — создать две группы: одна содержит десять звездочек, а другая — пять. В десятичной системе эта "композиция" представляется числом

15 (1 десяток и 5 единиц). Но те же звездочки можно сгруппировать и по-другому:

**** *******

****

т.е. имеем две группы: с восемью и семью звездочками. Такое распределение звездочек может служить иллюстрацией представления числа 17(8) с использованием основания восемь (одна восьмерка и семь единиц),

 

 

Еще об основаниях

 

Число пятнадцать по основанию десять представляется как 15, по основанию девять — как 16(9), no основанию восемь — как 17(8), а по основанию семь — как 21(7). В системе счисления по основанию 7 нет цифры 8, поэтому для представления числа пятнадцать нужно использовать две семерки и одну единицу.

Как же прийти к какому-нибудь общему принципу? Чтобы преобразовать десятичное число в число с основанием 7, вспомните о значении каждой порядковой позиции. В семеричной системе счисления переход к следующему порядку будет происходить на значениях, соответствующих десятичным числам: единица, семь, сорок девять, триста сорок три и т.д. Откуда взялись эти числа? Так ведь это же степени числа семь: 7^0, 7^0, 7^2, 7^3 и т.д. Построим следующую таблицу:

4 3 2 1

7^3 7^2 7^1 7^0

343 49 7 1

В первой строке представлен порядок числа. Во второй — степень числа семь, а в третьей — десятичное представление соответствующей степени числа семь.

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

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

Чтобы узнать, сколько раз число 49 (граничное значение третьего порядка) "поместится" в нашем числе, разделите его на 49. В ответе получается число 4, поэтому поставьте 4 в третью позицию и рассмотрите остаток, который в данном случае тоже равен 4. Поскольку в этом остатке не укладывается ни одной целой семерки, то во второй разряд (второй порядок) помещаем цифру 0. Нетрудно догадаться, что в остатке 4 содержится 4 единицы, поэтому и ставим цифру 4 в первую позицию (порядок единиц). В итоге получаем число 404(7).

Для преобразования числа 968 в систему счисления по основанию 6 используем следующую таблицу:

5 4 3 2 1

6^4 6^3 6^2 6^1 6^0

1296 216 36 6 1

В числе 968 число 1296 (граничное значение пятого порядка) не умещается ни разу, поэтому мы имеем дело с числом четвертого порядка. При делении числа 968 на число 216 (граничное значение четвертого порядка) получается число 4 с остатком, равным 104. В четвертую позицию ставим цифру 4. Делим остаток 104 на число 36 (граничное значение третьего порядка). Получаем в результате деления число 2 и остаток 32. Поэтому третья позиция будет содержать цифру 2. При делении остатка 32 на число 6 (граничное значение второго порядка) получаем 5 и остаток 2. Итак, в ответе имеем число 4252(6), что наглядно показано в следующей таблице:

5 4 3 2 1

6^4 6^3 6^2 6^1 6^0

1296 216 36 6 1

0 4 2 5 2

Для обратного преобразования, т.е. из системы счисления с недесятичным основанием (например, с основанием 6) в десятичную систему, достаточно умножить каждую цифру числа на граничное значение соответствующего порядка, а затем сложить полученные произведения:

4 * 216 864

2 * 36 = 72

5 * 6 = 30

2 * 1 = 2

sum = 968

Двоичная система счисления

 

Минимальным допустимым основанием является 2. В этом случае используются только две цифры: 0 и 1. Вот как выглядят порядки двоичного числа:

Порядок 8 7 6 5 4 3 2 1

Степень 2^7 2^6 2^5 2^4 2^3 2^2 2^1 2^0

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

Для преобразования числа 88 в двоичное число (с основанием 2) выполните описанную выше процедуру. В числе 88 число 128 не укладывается ни разу, поэтому в восьмой позиции ставим 0.

В числе 88 число 64 укладывается только один раз, поэтому в седьмую позицию ставим 1, а остаток равен 24. В числе 24 число 32 не укладывается ни разу, поэтому шестая позиция содержит 0.

В числе 24 число 16 укладывается один раз, поэтому пятой цифрой двоичного числа будет 1. Остаток при этом равен 8. В остатке 8 число 8 (граничное значение четвертого порядка) укладывается один раз, следовательно, в четвертой позиции ставим 1. Новый остаток равен нулю, поэтому в оставшихся позициях будут стоять нули.

0 1 0 1 1 0 0 0

Чтобы протестировать полученный ответ, выполним обратное преобразование:

1 * 64 = 64

0 * 32 = 0

1 * 16 = 16

1 * 8 = 8

0 * 4 = 0

0 * 2 = 0

0 * 1 = 0

sum = 88

 

Почему именно основание 2

 

Система счисления с основанием 2 более всего соответствует способу представления информации в компьютере. На самом деле компьютеры "понятия не имеют" ни о каких буквах, цифрах, командах или программах, поскольку представляют собой обычные электронные схемы, для которых важны только такие понятия, как сила тока и напряжение в сети.

Чтобы не усложнять себе жизнь измерениями относительной силы тока в сети (малая, меньше средней, средняя, больше средней и т.д.), разработчики первых компьютерных систем сошлись на том, что проще и надежнее отслеживать только два состояния: есть ток — нет тока. Эти состояния можно выразить словами "да" и "нет", или "истинно" и "ложно", или цифрами 1 и 0. По соглашению 1 означает истинно или "да", но это всего лишь соглашение. С таким же успехом единица могла бы означать ложно или "нет".

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

 

Биты, байты и полубайты

 

Если мы приняли решение кодировать данные последовательностями единиц и нулей, то минимальной единицей информации будет двоичный разряд (или бит). На заре компьютерной эры информация передавалась порциями по 8 битов, поэтому минимальной смысловой единицей (словом) в программировании было 8-разрядное число, называемое байтом.

 

Примечание:  Половина байта (4 бита) называется полубайтом (nybble).

 

С помощью восьми двоичных разрядов можно представить до 256 различных значений. Почему? Рассмотрим разрядные позиции. Если все восемь разрядов установлены (равны 1), то полученное двоичное число будет соответствовать десятичному 255. Если не установлен ни один из разрядов, значение равно 0, т.е. в диапазоне 0- 255 укладываются 256 возможных значений.

 

Что такое килобайт

 

Оказывается, что 2^10 (1 024) приблизительно равно 10^3 (1 000). Это совпадение грешно было бы не использовать, поэтому ученые компьютерщики 2^10 байтов начали называть 1 килобайтом (1 Кбайт), используя префикс "кило", который в переводе с латинского означает тысяча.

Аналогично и число 1024 * 1024 (1 048 576) не намного отличается от миллиона, в результате в компьютерной среде широко используется обозначение 1 Мбайт (или 1 мегабайт), а 1 024 мегабайта называют 1 гигабайтом ("гига" означает тысячу миллионов, или миллиард).

 

Двоичные числа

 

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

Например, микросхема Intel 80.6 интерпретирует битовый набор 1001 0101 как команду. В десятичном представлении это значение соответствует числу 149, что для человека, не сведущего в механизмах работы процессора, также ни о чем не говорит.

Иногда числа представляют собой команды, иногда — значения, а иногда — структурные элементы кода. Одним из стандартизованных наборов кодов является ASCII. В нем каждая буква или знак препинания имеет 7-разрядное двоичное представление. Например, строчная буква "а" представляется двоичным числом 0110 0001. Хотя это значение можно преобразовать в десятичное число 97 (64 + 32 + 1), следует понимать, что это не цифра, а буква. Поэтому иногда говорят, что буква "а" в ASClI представлена числом 97, хотя на самом деле двоичное представление десятичного числа 97 (0110 0001) является кодом буквы "а".

 


Дата добавления: 2019-02-12; просмотров: 232; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!