Лекции.ИНФО


Глава 3: Основные типы данных



Как вы узнали из главы 2, все переменные в C++ должны быть объявлены до их использования. Это необходимо для компилятора, которому нужно иметь информацию о типе данных, содержащихся в переменных. Только в этом случае компилятор сможет надлежащим образом скомпилировать инструкции, в которых используются переменные. В C++ определено семь основных типов данных: символьный, символьный двубайтовый, целочисленный, с плавающей точкой, с плавающей точкой двойной точности, логический (или булев) и "не имеющий значения". Для объявления переменных этих типов используются ключевые слова char, wchar_t, int, float, double, bool и void соответственно. Типичные размеры значений в битах и диапазоны представления для каждого из этих семи типов приведены в табл. 3.1. Помните, что размеры и диапазоны, используемые вашим компилятором, могут отличаться от приведенных здесь. Самое большое различие существует между 16- и 32-разрядными средами: для представления целочисленного значения в 16-разрядной среде используется, как правило, 16 бит, а в 32-разрядной — 32.

Переменные типа char используются для хранения 8-разрядных ASCII-символов (например букв А, Б или В) либо любых других 8-разрядных значений. Чтобы задать символ, необходимо заключить его в одинарные кавычки. Тип wchar_t предназначен для хранения символов, входящих в состав больших символьных наборов. Вероятно, вам известно, что в некоторых естественных языках (например китайском) определено очень большое количество символов, для которых 8-разрядное представление (обеспечиваемое типом char) весьма недостаточно. Для решения проблем такого рода в язык C++ и был добавлен тип wchar_t, который вам пригодится, если вы планируете выходить со своими программами на международный рынок.

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

Тип bool предназначен для хранения булевых (т.е. ИСТИНА/ЛОЖЬ) значений. В C++ определены две булевы константы: true и false, являющиеся единственными значениями, которые могут иметь переменные типа bool.

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

Объявление переменных

Общий формат инструкции объявления переменных выглядит так:

тип список_переменных;

Здесь элемент тип означает допустимый в C++ тип данных, а элемент список_переменных может состоять из одного или нескольких имен (идентификаторов), разделенных запятыми. Вот несколько примеров объявлений переменных.

int i, j, k;

char ch, chr;

float f, balance;

double d;

В C++ имя переменной никак не связано с ее типом.

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

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

Локальные переменные

Переменные, которые объявляются внутри функции, называются локальными. Их могут использовать только инструкции, относящиеся к телу функции. Локальные переменные неизвестны внешним функциям. Рассмотрим пример.

#include <iostream>

using namespace std;

void func();

Int main()

{

 int x; // Локальная переменная для функции main().

 х = 10;

 func();

 cout << "";

 cout << x; // Выводится число 10.

 return 0;

}

Void func()

{

 int x; // Локальная переменная для функции func().

 x = -199;

 cout << x; // Выводится число -199.

}

Локальная переменная известна только функции, в которой она определена.

В этой программе целочисленная переменная с именем х объявлена дважды: сначала в функции main(), а затем в функции func(). Но переменная х из функции main() не имеет никакого отношения к переменной х из функции func(). Другими словами, изменения, которым подвергается переменная х из функции func(), никак не отражаются на переменной х из функции main(). Поэтому приведенная выше программа выведет на экран числа -199 и 10.

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

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

Формальные параметры

Формальный параметр — это локальная переменная, которая получает значение аргумента, переданного функции.

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

Int funс1 (int first, int last, char ch)

{

 .

 .

 .

}

Здесь функция funс1() имеет три параметра с именами first, last и ch. С помощью такого объявления мы сообщаем компилятору тип каждой из переменных, которые будут принимать значения, передаваемые функции. Несмотря на то что формальные параметры выполняют специальную задачу получения значений аргументов, передаваемых функции, их можно также использовать в теле функции как обычные локальные переменные. Например, мы можем присвоить им любые значения или использовать в ка-ких-нибудь (допустимых для C++) выражениях. Но, подобно любым другим локальным переменным, их значения теряются по завершении функции.

Глобальные переменные

Глобальные переменные известны всей программе.

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

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

#include <iostream>

using namespace std;

void funс1();

void func2();

Int count; // Это глобальная переменная.

Int main()

{

 int i; // Это локальная переменная.

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

  count = i * 2;

  funс1();

 }

 return 0;

}

Void func1()

{

 cout << "count: " << count; // Обращение к глобальной переменной.

 cout << ''; // Вывод символа новой строки.

 func2();

}

Void func2()

{

 int count; // Это локальная переменная.

 for(count=0; count<3; count++) cout <<'.';

}

Несмотря на то что переменная count не объявляется ни в функции main(), ни в функции func1(), обе они могут ее использовать. Но в функции func2() объявляется локальная переменная count. Здесь при обращении к переменной count выполняется доступ к локальной, а не к глобальной переменной. Важно помнить, что, если глобальная и локальная переменные имеют одинаковые имена, все ссылки на "спорное" имя переменной внутри функции, в которой определена локальная переменная, относятся к локальной, а не к глобальной переменной.

Модификаторы типов

В C++ перед такими типами данных, как char, int и double, разрешается использовать модификаторы. Модификатор служит для изменения значения базового типа, чтобы он более точно соответствовал конкретной ситуации. Перечислим возможные модификаторы типов.

Signed

Unsigned

Long

Short

Модификаторы signed, unsigned, long и short можно применять к целочисленным базовым типам. Кроме того, модификаторы signed и unsigned можно использовать с типом char, а модификатор long— с типом double. Все допустимые комбинации базовых типов и модификаторов для 16- и 32-разрядных сред приведены в табл. 3.2 и 3.3. В этих таблицах также указаны типичные размеры значений в битах и диапазоны представления для каждого типа. Безусловно, реальные диапазоны, поддерживаемые вашим компилятором, следует уточнить в соответствующей документации.

Изучая эти таблицы, обратите внимание на количество битов, выделяемых для хранения коротких, длинных и обычных целочисленных значений. Заметьте: в большинстве 16-разрядных сред размер (в битах) обычного целочисленного значения совпадает с размером короткого целого. Также отметьте, что в большинстве 32-разрядных сред размер (в битах) обычного целочисленного значения совпадает с размером длинного целого. "Собака зарыта" в С++-определении базовых типов. Согласно стандарту C++ размер длинного целого должен быть не меньше размера обычного целочисленного значения, а размер обычного целочисленного значения должен быть не меньше размера короткого целого. Размер обычного целочисленного значения должен зависеть от среды выполнения. Это значит, что в 16-разрядных средах для хранения значений типа int используется 16 бит, а в 32-разрядных — 32. При этом наименьший допустимый размер для целочисленных значений в любой среде должен составлять 16 бит. Поскольку стандарт C++ определяет только относительные требования к размеру целочисленных типов, нет гарантии, что один тип будет больше (по количеству битов), чем другой. Тем не менее размеры, указанные в обеих таблицах, справедливы для многих компиляторов.

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

Различие между целочисленными значениями со знаком и без него заключается в интерпретации старшего разряда. Если задано целочисленное значение со знаком, С++-компилятор сгенерирует код с учетом того, что старший разряд значения используется в качестве флага знака. Если флаг знака равен 0, число считается положительным, а если он равен 1, — отрицательным. Отрицательные числа почти всегда представляются в дополнительном коде. Для получения дополнительного кода все разряды числа берутся в обратном коде, а затем полученный результат увеличивается на единицу.

Целочисленные значения со знаком используются во многих алгоритмах, но максимальное число, которое можно представить со знаком, составляет только половину от максимального числа, которое можно представить без знака. Рассмотрим, например, максимально возможное 16-разрядное целое число (32 767):

0 1111111 11111111

Если бы старший разряд этого значения со знаком был установлен равным 1, то оно бы интерпретировалось как -1 (в дополнительном коде). Но если объявить его как unsigned int-значение, то после установки его старшего разряда в 1 мы получили бы число 65 535.

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

#include <iostream>

using namespace std;

/* Эта программа демонстрирует различие между signed- и unsigned-значениями целочисленного типа.

*/

Int main()

{

 short int i; // короткое int-значение со знаком

 short unsigned int j; // короткое int-значение без знака

 j = 60000;

 i = j;

 cout << i << " " << j;

 return 0;

}

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

-5536 60000

Дело в том, что битовая комбинация, которая представляет число 60000 как короткое целочисленное значение без знака, интерпретируется в качестве короткого int-значения со знаком как число -5536.

В C++ предусмотрен сокращенный способ объявления unsigned-, short- и long-значений целочисленного типа. Это значит, что при объявлении int-значений достаточно использовать слова unsigned, short и long, не указывая тип int, т.е. тип int подразумевается. Например, следующие две инструкции объявляют целочисленные переменные без знака.

unsigned х;

unsigned int у;

Переменные типа char можно использовать не только для хранения ASCII-символов, но и для хранения числовых значений. Переменные типа char могут содержать "небольшие" целые числа в диапазоне -128--127 и поэтому их можно использовать вместо int-переменных, если вас устраивает такой диапазон представления чисел. Например, в следующей программе char-переменная используется для управления циклом, который выводит на экран алфавит английского языка.

// Эта программа выводит алфавит в обратном порядке.

#include <iostream>

using namespace std;

Int main()

{

 char letter;

 for(letter='Z'; letter >= 'A'; letter--) cout << letter;

 return 0;

}

Если цикл for вам покажется несколько странным, то учтите, что символ 'А' представляется в компьютере как число, а значения от 'Z' до 'А' являются последовательными и расположены в убывающем порядке.

Литералы

Литералы, называемые также константами, — это фиксированные значения, которые не могут быть изменены программой. Мы уже использовали литералы во всех предыдущих примерах программ. А теперь настало время изучить их более подробно.

Константы могут иметь любой базовый тип данных. Способ представления каждой константы зависит от ее типа. Символьные константы заключаются в одинарные кавычки. Например, 'а' и '%' являются символьными литералами. Если необходимо присвоить символ переменной типа char, используйте инструкцию, подобную следующей:

ch = 'Z';

Чтобы использовать двубайтовый символьный литерал (т.е. константу типа wchar_t), предварите нужный символ буквой L. Например, так.

wchar_t wc;

wc = L'A';

Здесь переменной wc присваивается двубайтовая символьная константа, эквивалентная букве А.

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

Существует два основных вещественных типа: float и double. Кроме того, существует несколько модификаций базовых типов, которые образуются с помощью модификаторов типов. Интересно, а как же компилятор определяет тип литерала? Например, число 123.23 имеет тип float или double? Ответ на этот вопрос состоит из двух частей. Во-первых, С++-компилятор автоматически делает определенные предположения насчет литералов. Во-вторых, при желании программист может явно указать тип литерала.

По умолчанию компилятор связывает целочисленный литерал с совместимым и одновременно наименьшим по занимаемой памяти тип данных, начиная с типа int. Следовательно, для 16-разрядных сред число 10 будет связано с типом int, а 103 000— с типом long int.

Единственным исключением из правила "наименьшего типа" являются вещественные (с плавающей точкой) константы, которым по умолчанию присваивается тип double. Во многих случаях такие стандарты работы компилятора вполне приемлемы. Однако у программиста есть возможность точно определить нужный тип.

Чтобы задать точный тип числовой константы, используйте соответствующий суффикс. Для вещественных типов действуют следующие суффиксы: если вещественное число завершить буквой F, оно будет обрабатываться с использованием типа float, а если буквой L, подразумевается тип long double. Для целочисленных типов суффикс U означает использование модификатора типа unsigned, а суффикс Llong. (Для задания модификатора unsigned long необходимо указать оба суффикса U и L.) Ниже приведены некоторые примеры.

Шестнадцатеричные и восьмеричные литералы

Иногда удобно вместо десятичной системы счисления использовать восьмеричную или шестнадцатеричную. В восьмеричной системе основанием служит число 8, а для выражения всех чисел используются цифры от 0 до 7. В восьмеричной системе число 10 имеет то же значение, что число 8 в десятичной. Система счисления по основанию 16 называется шестнадцатеричной и использует цифры от 0 до 9 плюс буквы от А до F, означающие шестнадцатеричные "цифры" 10, 11, 12, 13, 14 и 15. Например, шестнадцатеричное число 10 равно числу 16 в десятичной системе. Поскольку эти две системы счисления (шестнадцатеричная и восьмеричная) используются в программах довольно часто, в языке C++ разрешено при желании задавать целочисленные литералы не в десятичной, а в шестнадцатеричной или восьмеричной системе. Шестнадцатеричный литерал должен начинаться с префикса 0x (нуль и буква х) или , а восьмеричный — с нуля. Приведем два примера.

int hex = OxFF; // 255 в десятичной системе

int oct = 011; // 9 в десятичной системе

Строковые литералы

Язык C++ поддерживает еще один встроенный тип литерала, именуемый строковым. Строка— это набор символов, заключенных в двойные кавычки, например "это тест". Вы уже видели примеры строк в некоторых cout-инструкциях, с помощью которых мы выводили текст на экран. При этом обратите внимание вот на что. Хотя C++ позволяет определять строковые литералы, он не имеет встроенного строкового типа данных. Строки в C++, как будет показано ниже в этой книге, поддерживаются в виде символьных массивов. (Кроме того, стандарт C++ поддерживает строковый тип с помощью библиотечного класса string, который также описан ниже в этой книге.)

Осторожно! Не следует путать строки с символами. Символьный литерал заключается в одинарные кавычки, например 'а'. Однако "а" — это уже строка, содержащая только одну букву.

Управляющие символьные последовательности

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

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

#include <iostream>

using namespace std;

Int main()

{

 cout<<"\";

 return 0;

}

Инициализация переменных

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

тип имя_переменной = значение;

Вот несколько примеров.

char ch = 'а';

int first = 0;

float balance = 123.23F;

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

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

Рассмотрим простой пример инициализации переменных. В следующей программе используется функция total(), которая предназначена для вычисления суммы всех последовательных чисел, начиная с единицы и заканчивая числом, переданным ей в качестве аргумента. Например, сумма ряда чисел, ограниченного числом 3, равна 1 + 2 + 3 = 6. В процессе вычисления итоговой суммы функция total() отображает промежуточные результаты. Обратите внимание на использование переменной sum в функции total().

// Пример использования инициализации переменных.

#include <iostream>

using namespace std;

void total(int x);

Int main()

{

 cout << "Вычисление суммы чисел от 1 до 5.";

 total(5);

 cout << "Вычисление суммы чисел от 1 до 6.";

 total(6);

 return 0;

}

Void total(int x)

{

 int sum=0; // Инициализируем переменную sum.

 int i, count;

 for(i=1; i<=x; i++) {

  sum = sum + i;

  for(count=0; count<10; count++) cout << '.';

  cout << "Промежуточная сумма равна " << sum << '';

 }

}









Читайте также:

Последнее изменение этой страницы: 2016-03-17; Просмотров: 104;


lektsia.info 2017 год. Все права принадлежат их авторам! Главная