III Flashcards

1
Q

Синтаксис имен переменных, венгерская нотация

A
  • Имена переменных в C++ могут состоять из букв и цифр, но не могут на­чинаться с цифр, а также содержать пробелы и арифметические операторы (такие, как +, - и т.п.).
  • Именами переменных не могут быть зарезервированные ключевые слова. Например, переменная по имени return приведет к ошибке при компиляции.
  • В именах переменных можно использовать символ подчеркивания, кото­рый позволяет создавать более понятные, самодокументируемые имена переменных.
  • Вы можете встретить код C++, в котором имя переменной предваряется символа­ми, описывающими тип переменной. Это соглашение называется венгерской нота­цией и часто используется в программировании в Windows. Так, переменная firstNumber в венгерской нотации имела бы имя iFirstNumber, где префикс i означает тип int. Глобальная переменная имела бы имя giFirstNumber. Венгерская нотация в последние годы теряет популярность, частично из-за улучшения интегрированных сред разработки, при необходимости отображающих тип переменной, например при наведении на них указателя мыши.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Проблемы с использованием глобальных переменных

A

Безосновательное использование глобальных переменных обычно счита­ется плохой практикой программирования.

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Концепция знаковых и беззнаковых целых чисел, самый старший бит

A
  • Область памяти размером 1 байт может хранить одно из 2 в степени 8, т.е. 256 разных значений. Аналогично область памяти размером 16 битов может хранить одно из 2 в степени 16 разных значений, т.е. одно из 65536 уникальных значений.
  • Но как же представить в этой же области отрицательные числа? Один из спосо­бов — “пожертвовать” одним из разрядов для хранения знака, который указывал бы, положительное или отрицательное значение содержится в других битах. Такой знаковый разряд имеет смысл делать самым старшим битом (Most-Significant-Bit — MSB). Если старший бит содержит информацию о знаке, предполагается, что значе­ние 0 означает положительное число, а значение 1 — отрицательное. Другие биты содержат абсолютное значение числа.
  • Таким образом, занимающее 8 битов знаковое число может содержать значения в пределах от -128 до 127, а занимающее 16 битов — значения в пределах от -32 768 до 32 767.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Беззнаковые целочисленные типы unsigned short, unsigned int, unsigned long и unsigned long long

A
  • В отличие от знаковых аналогов, беззнаковые целочисленные типы не могут со­держать информацию о знаке, зато могут содержать вдвое большие положительные значения.
  • Переменные беззнакового типа используются тогда, когда ожидаются только неотрицательные значения. Так, если вы подсчитываете количество яблок, не используйте тип int; воспользуйтесь типом unsigned int. Последний может содержать вдвое больше положительных значений, чем первый.
  • Размер переменной знакового и беззнакового типов одинаков; единственным различием этих двух типов является знаковый старший бит.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Переполнение типов

A
  • Типы данных, такие как short, int, long, unsigned short, unsigned int, un­signed long и другие, имеют ограниченную емкость и могут содержать числа, не превышающие некоторые пределы. Превысив предел для выбранного типа, вы по­лучаете переполнение.
  • Возьмем в качестве примера unsigned short. Этот тип данных обычно содержит 16 бит, а потому может содержать значения от 0 до 65 535. Если вы прибавите 1 к 65535 в переменной типа unsigned short, циклический переход приведет к значе­нию 0.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Использование разделителя разрядов

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Определение размера переменной с использованием оператора sizeof

A

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

Размер переменной зависит от ее типа, и в языке C++ есть очень удобный оператор sizeof, который возвращает размер переменной или типа в байтах.

Применение оператора sizeof очень простое. Чтобы определить размер целого числа, вызовите оператор sizeof с параметром в виде типа int:

cout << “Размер int: “ << sizeof (int);

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Запрет сужающего преобразования при использовании инициализации списком

A

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

  • int largeNum = 5000000;*
  • short smallNum = largeNum; // Компилируется, но возможна ошибка*

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

  • int largeNum = 5000000;*
  • short anotherNum{ largeNum }; // Ошибка сужения!*
  • int anotherNum{ largeNum }; // OK!*
  • float someFloat{ largeNum }; // Ошибка! Возможно сужение*
  • float someFloat{ 5000000 }; // OK! 5000000 помещается в float*
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Автоматический вывод типа с использованием auto

A
  • В ряде случаев тип переменной очевиден — по присваиваемому при инициализа­ции значению. Например, если переменная инициализируется значением true, сле­дует ожидать, что, скорее всего, типом переменной будет bool. Компиляторы с поддержкой C++11 и выше дают возможность определять тип неявно, с использованием вместо типа переменной его ключевого слова auto:
  • auto coinFlippedHeads = true;*
  • Здесь задача определения конкретного типа переменной coinFlippedHeads оставлена компилятору. Компилятор просто проверяет природу значения, которым инициа­лизируется переменная, а затем выбирает тип, наилучшим образом подходящий для этой переменной.
  • Использование ключевого слова auto требует инициализации перемен­ной, поскольку компилятор нуждается в инициализирующем значении, что­бы принять решение о наилучшем типе для переменной. Если вы не инициализируете переменную, то применение ключевого слова auto приведет к ошибке при компиляции.
  • Хотя, на первый взгляд, ключевое слово auto кажется не особенно полезным, оно существенно упрощает программирование в тех случаях, когда тип переменной сло­жен.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Использование ключевого слова typedef для замены типа

A

Язык C++ позволяет переименовывать типы переменных так, как вам кажется бо­лее удобным. Для этого используется ключевое слово typedef. Например, програм­мист хочет назначить типу unsigned int более описательное имя STRICTLY_POSITIVE_INTEGER.

typedef unsigned int STRICTLY_POSITIVE_INTEGER;
STRICTLY_POSITIVE_INTEGER numEggsInBasket = 4532;

При компиляции первая строка указывает компилятору, что STRICLY_POSITIVE_INTEGER — это не что иное, как тип unsigned int. Впоследствии, когда компилятор встречает уже определенный тип STRICLY_POSITIVE_INTEGER, он заменяет его типом unsigned int и продолжает компиляцию.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Что такое константа

A

После того как значение константы определено, оно не может быть из­менено. Попытки присваивания значения константе в языке C++ приводят к ошибке при компиляции.

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

  • Литеральные константы.
  • Константы, объявленные с использованием ключевого слова const.
  • Константные выражения, использующие ключевое слово constexpr (нововведение С++11).
  • Константы перечислений, использующие ключевое слово enum.
  • Константы, определенные с помощью макроопределений, использование которых не рекомендуется и осуждается.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Литеральные константы

A

Литеральные константы могут быть многих типов — целочисленные, строки и т.д.

В нашей первой программе строка “Hello World” выводится с помощью следующей инструкции:

std : : cout << “Hello World” << std : : endl;

Здесь “Hello World” — это константа строкового литерала (string literal). Когда вы объявляете целое число наподобие

int someNumber = 10;

целочисленной переменной someNumber присваивается начальное значение, равное 10. Здесь 10 — это часть кода, компилируемая в приложение, которая является неизменной и тоже является литеральной константой (literal constant).

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Объявление переменных как констант с использованием ключевого слова const

A

Самый важный тип констант C++ с практической и программной точек зрения объявляется с помощью ключевого слова const, расположенного перед типом пере­менной. В общем виде объявление выглядит следующим образом:

const имя_типа имя_константы = значение;

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

Константы полезны при объявлении массивов постоянной длины, которые не­изменны во время компиляции.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Объявление констант с использованием ключевого слова constexpr

A

Ключевое слово constexpr позволяет объявлять константы подобно функциям:

constexpr double GetPi( ) {return 3.1415926;}

Одно constexpr - выражение может использовать другое:

constexpr double TwicePi( ) {return 2 * GetPi( );}

GetPi( ) и TwicePi( ) могут казаться функциями, но это не совсем функции. Дело в том, что функции вызываются во время выполнения програм­мы. Эти же константные выражения компилятор заменяет числом 3.141593 при каж­дом использовании GetPi( ) и числом 6.283186 при использовании TwicePi( ). Такое разрешение TwicePi( ) в константу увеличивает скорость выполнения программы по сравнению выполнением вычисления, содержащегося в функции.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Перечисления

A

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

Перечисления (enumerations) — это именно то, что необходимо в данной ситуации. Перечисления объявляются с помощью ключевого слова enum.

Вот пример перечисления, которое определяет цвета радуги:

enum RainbowColors
{
Violet = 0,
Indigo,
Blue,
Green,
Yellow,
Orange,
Red
};

А вот другой пример — направления компаса:

enum CardinalDirections
{
North,
South,
East,
West
};

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

RainbowColors MyWorldsColor = Blue; // Начальное значение

При объявлении перечисления компилятор преобразует его константы, та­кие как Violet и другие, в целые числа. Каждое последующее значение перечисления на единицу больше предыдущего. Начальное значение вы можете задать сами, но если вы этого не сделаете, компилятор начнет счет с 0. Так, значению North соответствует числовое значение 0.

По желанию можно также явно определить числовое значение напротив каждой из перечисляемых констант при их инициализации.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Определение констант с использованием директивы #define

A

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

Единственная причина упоминания определения констант с использованием дирек­тивы #define в этой книге — помочь вам понять некоторые устаревшие программы, в которых для определения числа к мог бы использоваться такой синтаксис:

#define pi 3.141593

Это макрокоманда препроцессора, предписывающая компилятору заменять все упоминания pi значением 3.141593 . Обратите внимание: это текстовая (читай: не­интеллектуальная) замена, осуществляемая препроцессором. Компилятор не знает фактический тип рассматриваемой константы и не заботится о нем.

Определение констант с использованием директивы препроцессора #define считается устаревшим и не рекомендуется.

17
Q

Массивы (+ синтаксис), статические и динамические массивы, длина массива

A
  • В языке C++ массивы позволяют сохранить в памяти элементы данных некоторого типа в последовательном упорядоченном виде.
  • Массивы называются статическими (static array) если количество содержащихся в них элементов, а также размер выделенной для них области памяти остаются неизменными во время компиляции.
  • Для объявления массива в языке C++ используется следующий синтаксис:
  • Тип_элемента Имя_массива [Количество_элементов] = {Необязательные исходные значения};*
  • Все элементы массива можно инициализировать нулем (значение по умолчанию, предоставляемое компилятором):
  • int myNumbers[5] = {0}; // Инициализировать все элементы нулем*
  • Вы можете также инициализировать только часть элементов массива:
  • int myNumbers[5] = {34, 56}; // инициализировать первые два*
  • // элемента значениями 34 и 56, прочие элементы равны нулю*
  • Вы можете определить длину массива (т.е. указать количество элементов в нем) как константу и использовать ее при определении массива:
  • const int ARRAY_LENGTH = 5;*
  • int myNumbers[ARRAYJLENGTH] = {34, 56, -21, 5002, 365};*
  • Это особенно полезно, когда необходимо иметь доступ и использовать длину мас­сива в нескольких местах, например при переборе всех элементов массива. В таком случае при изменении длины массива достаточно будет исправить лишь одно значе­ние, объявленное как const int.
  • Если исходное количество элементов в массиве неизвестно, его можно не указывать:

int myNumbers[] = {2017, 2052, -525};

  • Массивы, размер которых опреде­ляется во время выполнения, называются динамическими.
  • Длина массива должна быть константным целочисленным значением.
  • Динамические массивы позволяют программисту не беспокоиться об установке максимальной длины массива во время компиляции, а также обеспечивают лучшее управление памятью в случае, если размер массива меньше ожидаемого максимума.
18
Q

Объем памяти в байтах, резервируемой компилятором для масси­ва?

A

В общем виде объем памяти в байтах, резервируемой компилятором для массива, составляет:

Байты массива = sizeof (Тип элемента) * Количество элементов

19
Q

Доступ к данным, хранимым в массиве (ключевая проблема), переполнение буфера

A
  • Когда запрашивается доступ к элементу с индексом N, компилятор использует адрес первого элемента (позиция элемента с нулевым индексом) в качестве отправной точки, а затем пропускает N элементов, добавляя к этому адресу смещение, вычисляе­мое как N*sizeof(тип_элемента), чтобы получить адрес N+1-го элемента. Компи­лятор C++ не проверяет, находится ли индекс в пределах фактически определенных границ массива.
  • Результат доступа к массиву за его пределами непредсказуем. Как правило, такое обращение ведет к аварийному завершению программы.
  • Важно не выходить за границы массива. Это явление называется переполнением буфера (buffer overflow), и проверка ввода перед его использованием для индексации элементов позволяет гарантировать отсутствие пересечения границ массива.
20
Q

Многомерные массивы

A
  • Что если нам нужно использовать массив для модели­рования солнечных панелей, показанных на рис. 4.3? Солнечные панели, в отличие от книжных полок, распространяются в двух размерностях: по длине и по ширине.
  • Как можно заметить на рис. 4.3, шесть солнечных панелей располагаются в дву­мерном порядке: два ряда (строки) по три столбца. Но можно рассматривать такое расположение и как массив из двух элементов, каждый из которых сам является мас­сивом из трех панелей; другими словами, как массив массивов.
  • В языке C++ вы може­ те создавать двумерные массивы, но вы не ограничены только двумя размерностями. В зависимости от необходимости и характера приложения вы можете создавать в па­мяти многомерные массивы.
  • Язык C++ позволяет объявлять многомерные массивы, указывая количество эле­ментов, которое необходимо выделить в каждой размерности. Таким образом, дву­мерный массив целых чисел, представляющий солнечные панели на рис. 4.3, можно объявить так:
  • int solarPanelIDs [2] [3];*
  • Обратите внимание, что на рис. 4.3 каждой из шести панелей присвоен также иден­тификатор в диапазоне от 0 до 5. Если мы инициализируем целочисленный массив в том же порядке, то эта инициализация будет иметь следующий вид:
  • int solarPanellDs [2] [3] = {{0, 1, 2}, {3, 4, 5}};*
  • Несмотря на то что язык C++ позволяет использовать модель многомерных массивов, в памяти такие массивы все равно хранятся как одномерные. Компилятор отображает многомерный массив на область памяти, которая расширяется только в одном направлении.
21
Q

Доступ к элементам многомерного массива

A

Рассмотрим следующий массив:

int threeRowsThreeColumns [3] [3] = {{-501, 206, 2017}, {989, 101, 206}, {303, 456, 596}};

Он инициализирован так, что его можно рассматривать как три массива, каждый из которых содержит три целых числа. Здесь целочисленный элемент со значением 206 находится в позиции [0] [1], а элемент со значением 456 — в позиции [2] [1].

22
Q

Строки символов в стиле С

A
  • Строки в стиле С (C-style string) — это частный случай массива символов. Вы уже видели несколько примеров таких строк в виде строковых литералов, когда писали код:

std : : cout << “Hello World”;

  • Это эквивалентно такому объявлению массива:

char sayHello[] = {‘Н’,’е’,’l’,’l’,’о’,’ ‘,’W’,’о’,’г’,’l’,’d’,’\0’}
std : : cout << sayHello << std : : endl;

  • Обратите внимание: последний символ в массиве — нулевой символ ‘\0’. Он так­же называется завершающим нулевым символом (string-terminating character), посколь­ку указывает компилятору, что строка на этом заканчивается. Такие строки в стиле С — это частный случай символьных массивов, последним символом которых всегда является нулевой символ ‘\0’. Когда вы используете в коде строковый литерал, ком­пилятор сам добавляет после него символ ‘\0’.
  • Если вставить символ ‘\0’ в середину массива, то это не изменит его размер; од­нако обработка строки, хранящейся в данном массиве, остановится на этой точке. При этом размер массива не изменится, несмотря на изменение отображаемых данных.
  • Если при объявлении и инициализации символьного массива вы забудете добавить символ ‘\0’ то после вывода
    “Hello World” на консоль будет выведен случайный набор символов. Дело в том, что оператор std : : cout не остановится по окончании масси­ва и будет продолжать вывод, пока не встретит нулевой символ, даже если для этого придется перейти границы массива. Эта ошибка может привести вашу программу к аварийному останову, а в некоторых случаях поставить под угрозу стабильность системы.
  • Приложения, написанные на языке С (или на языке C++ программистами с большим опытом в языке С), зачастую используют в своем коде функции копирования строк, такие как strcpy, функции конкатенации, такие как strcat, и определения длины строк, такие как strlen. Эти функции используют строки в стиле С и потому опасны, так как ищут завершающий нулевой символ и могут легко выйти за границы символьного массива, если программист не гарантировал наличие завершающего
    нулевого символа.
23
Q

Строки C++: использование std : : string

A

Язык C++ предоставляет мощное и в то же время безопасное средство работы со
строками — класс s t d : : s tr in g . Класс s t d : : s tr in g не является статическим мас­
сивом элементов типа char неизменного размера, как строки в стиле С, и допускает
увеличение размера, когда в нем необходимо сохранить больше данных.

Для использования строк C++ в код необходимо включить заголовочный
файл s trin g :
tin c lu d e < strin g >

24
Q

Зачем заботиться об инициализации элементов статического массива?

A

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

25
Q

Следует ли инициализировать элементы динамического массива по причинам,
упомянутым в первом вопросе?

A

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