Назад | Начало урока | Вперед
Содержание

Глава 7

Типы данных — это серьезно


В этой главе...


Компьютерные программы занимаются обработкой различных данных, которые, в свою
очередь, могут принадлежать различным типам. Например, в электронных таблицах
данные могут быть представлены числами с плавающей запятой, в инвентаризационных
системах— порядковыми записями, а в программах для рисования— автофигурами.
Однако нужно понимать, что, хотя режиссеры Голливуда и создают мифы о всемогущих
и всезнающих компьютерах, на самом деле это не так. Компьютер может работать только
с данными строго определенных типов, причем предварительно (нужно указать, данные
какого именно типа будут ему передаваться. В этой главе вы познакомитесь с основными
типами данных, особенно детально обсуждается один из самых важных, который
обозначается словом String .

Вверх

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

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

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

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

Вверх

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

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

Процесс создания переменной называется ее определением. Чтобы определить переменную, нужно просто указать используемый ею тип данных, а затем ее имя. Ниже приведено несколько примеров определения переменных. В следующей строке создается переменная foe типа int (integer, число):

int too;

Далее создается переменная bar типа char (character, символ):

char bar;

И ниже сообщается о том, что функция min принимает два числа типа int в качестве значений параметров и возвращает другое число типа int в качестве результата. (Более подробно определение и использование функций рассматривается в главе 12.)

int min (int first, int second);

Обратите внимание, что объявление переменной отличается от ее определения.

Когда вы объявляете тип данных, вы просто указываете, значения какого типа
будет принимать эта переменная. При этом выделения памяти для объявляемой
переменной не происходит. (Физически переменная не создается.)

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

Познакомившись со структурами (глава 9) и классами (глава 17), вы увидите, что для них определение и объявление — это два разных действия, каждое из которых выполняется отдельно.

Наиболее часто используемые типы данных

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

Вот три наиболее часто используемых типа данных:

double Число с плавающей запятой. (Не думайте, что это число с водой, в которой плавает запятая. Это всего лишь вещественное число с десятичной запятой, которая может "плавать" в зависимости от количества десятичных знаков в числе и не имеет точно зафиксированной позиции. Сюда относятся, например, такие числа, как 4,3444; 42,1; 3,и тд .) Переменные такого типа могут содержать значения в пределах от ± 1,7*10-308 до ± 1,7*10*+308.
int Целое число. Значение может быть положительным, отрицательным либо нулевым. Например, к этому типу относятся значения 0, 1, 39, -42, но не 1,5.
String Любой текст, например Hello World.

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

double dblRadius;

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

Перед созданием программы определите для себя, переменные каких типов вам потребуются для хранения всех тех данных, которые вы собираетесь использовать. Например, для сохранения числа, обозначающего количество людей, посетивших концерт, можно создать переменную типа int (целое число), поскольку, разумеется, количество людей не может быть дробным. (Правда, кто-то может прийти со своей "второй половиной", но это для него она "половина", а для всех остальных— отдельный человек.) Если же вам нужна переменная для сохранения значения радиуса круга, лучше использовать тип double, поскольку радиус вполне может обозначаться дробным числом. И если вам нужно сохранять текстовую информацию, создайте переменную типа String .

Реже используемые типы данных

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

float Вещественное число с плавающей запятой; однако переменные этого типа данных могут принимать значения из диапазона гораздо меньшего, чем переменные типа double. Для этого типа допустимые значения лежат в интервале от ±3,4*10~308 до±3,4*10*~308.
long Это слово может быть добавлено перед словом int , чтобы обозначить, что для хранения одного значения должно быть выделено 32 бит памяти. В Visual C++.NET такой объем памяти выделяется по умолчанию, поэтому в использовании этого слова нет необходимости.
short Это слово может быть добавлено перед словом int . чтобы обозначить, что для хранения одного значения должно быть выделено 16 бит памяти.
signed Это слово может быть добавлено перед словом int , чтобы обозначить, что принимаемые значения могут быть как позитивными, так и негативными. (В Visual C++ эта возможность предусмотрена по умолчанию.)
unsigned Это слово может быть добавлено перед словом int , чтобы обозначить, что все принимаемые значения будут только позитивными. Благодаря этому освобождается один дополнительный бит памяти для обозначения величины принимаемого
значения. Так, если переменная типа int может принимать значения в диапазоне от -2 147 483 648 до +2 147 483 647, то слово unsigned определяет диапазон от 0 до 4 294 967 295.
void Тип не определяется. Используется для обозначения того, что функция не возвращает никакого результата. (Более подробно об этом речь идет в главе 12.)
char Символ. А, В или & — это все символы. Этот тип данных чаще всего используется в неуправляемых программах, в то время как в управляемых в основном применяется более гибкий тип String .

Обеспечение типовой безопасности

Языки машинного уровня не заботятся о корректном использовании типов данных. Для
них данные — это всего лишь нечто, занимающее часть памяти. Вот почему программы, написанные на языках машинного уровня, могут так просто сохранять числа поверх записей о сотрудниках, символы поверх чисел, и вообще делать все, что вы им прикажете. (Многие компьютерные вирусы уничтожают данные именно таким способом.)

Тип, который отличается от всех остальных

Чтобы убедиться, что все объявленные типы используются корректно, Visual C++ проделывает большую работу. Во время компиляции исходного файла формируется так называемая таблица имен. Она содержит подробную информацию обо всех переменных и функциях, используемых в исходном файле. Каждый раз, когда переменная (или функция) как-то используется, компилятор находит ее описание в таблице имен, проверяет объявленный для нее тип данных и определяет, возможно ли в отношении переменной (или функции) предпринимать такие действия.

Компилятор имеет также набор строгих правил, определяющих, как правильно преобразовать значения одного типа к значениям другого. Например, если функции требуется для работы значение параметра типа double (более детально передача функции значений параметров описана в главе 12), а вы передаете ей значение типа int, компилятор сам сможет преобразовать целое число к числу с плавающей запятой. Если же он обнаружит несоответствие типов, для которых не определено правило взаимного преобразования, будет возвращено сообщение об ошибке.

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

Проверка правильности использования типов данных (так называемый внешний анализ) осуществляется с использованием техники, которая называется корректировкой имен. Когда вы объявляете переменную, функцию или любой другой элемент, компилятор несколько изменяет присваиваемое вами имя (это имя также называют внешним). Внешнее имя содержит информацию о типе элемента. Можно сказать, например, что компилятор добавляет к исходному имени букву i, чтобы обозначить, что элемент имеет тип int, и буквы сp, чтобы обозначить, что элемент имеет тип char . Далее предположим, что а одном из исходных файлов вы создали такую функцию:

int NumberOfSongs();

В другом исходном файле вы набрали такой код:

Char MyText = NumberOfSongs ();

Компилятор преобразует имя NumberOf Songs к имени iNumberOf Songs, а имя MyText – к имени cpMyText. Сами вы никогда не увидите внутренних имен (разве что посмотрите на листинг кодов языка ассемблера), но компоновщик видит их очень хорошо.Для него, например, последняя строка кодов выглядит так;

CpMyText = iNumberOfSongs ();

Сравнивая скорректированные имена, компоновщик фиксирует несовпадение типов (поскольку символ и число - это разные данные) и выдает сообщение об ошибке.

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

Приведенный ниже код будет работать корректно.
//Создание переменной типа int
int nSize;

//Присвоение переменной значения 7
nSize = 7;

Но, если вы наберете следующий код, компилятор выдаст сообщение об ошибке, по-
скольку переменная типа int не может принимать текстовые значения:

//Попытка присвоить переменной текстовое значение
nSize = "Hello World";

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

int nSize;
nSize = 6.3;

После выполнения этого кода переменной nSize присваивается значение 6. (Компилятор автоматически округляет число. Все цифры, расположенные справа от десятичной запятой, просто отбрасываются.) Однако при этом компилятор отображает на экране предупреждение о том, что будет выполнено преобразование чисел. Хорошие и по-настоящему качественные программы должны компилироваться без предупреждений и ошибок. Чтобы избежать подобных предупреждений, можно явно указать компилятору на необходимость преобразования одного типа данных к другому. Для этого перед переменной, значение которой должно быть преобразовано, в круглых скобках укажите требующийся тип данных. Например:

int nSize;
double BigSize;
nSize = (int) BigZise;

В этом случае компилятор без лишних предупреждений преобразует значение типа
double к значению типа int . Это означает, что дробная часть значения переменной
BigZise будет просто усечена. Например, если эта переменная имеет значение, равное числу 3,141592653, переменной nSize будет присвоено значение, равное числу 3.

Вверх

Функции преобразования типов

В стандартном C++ типы данных являются просто структурами, которые требуют для себя некоторого количества памяти. С другой стороны, в среде .NET типы данных обладают еще и встроенной функциональностью. Технически они являются объектами. (Более подробно объекты описаны в главе 17.) Как и другие объекты, типы данных имеют встроенные функции, наиболее полезными из которых являются функции преобразования одних типов в другие.
В табл. 7,1 приведен список функций преобразования типов для основных типов данных.

Таблица 7.1. Функции преобразования типов данных

Тип Функция Выполняемое действие
String ToDouble () Преобразует тип String к типу double
String ToInt32() Преобразует тип String к типу int
double ToString() Преобразует тип double к типу String
double Parse() Преобразует тип String к типу double
int ToString() Преобразует тип int к типу String
int Parse Преобразует тип String к типу int

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

int nNumber = 3;
Console::WriteLine(nNumber.ToString ());

Теперь предположим, что у вас есть текстовое значение (String) и из него нужно сделать число (double). Делается это так:

String *szNumber = S"3.14"
double dNumber;
dNumber = Double::Parse(szNumber);

Вы, наверное, обратили внимание на использование звездочки (*) перед именем текстовой переменной. Она там нужна, поскольку при работе со строками приходится использовать указатели. Более подробно указатели описаны в главе 13. Пока же просто примите к сведению, что при объявлении текстовых переменных перед их именем нужно добавлять звездочку (*). И каждый раз при вызове встроенных для типа String функций вместо точки (.) используйте символы ->.

Если вас интересуют другие подробности приведенного выше кода, прочитайте этот абзац.

Буква S перед значением "3.14" указывает компилятору, что это должна быть строка, предусмотренная для использования в среде .NET. На самом деле можно создавать текстовые значения самых разных видов, но в данном случае создается именно такое значение, которое для среды .NET подходит наилучшим образом.

Использование двух двоеточий (::) в фрагменте Double :: Parse является специальным синтаксическим приемом, позволяющим применять методы (такие, как Parse) объектных типов (таких, как double) без предварительного создания самих объектов.
В будущем вы сможете по достоинству оценить эту возможность языка C++.

Вверх

Константы – то, что никогда
не меняется

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

В подобных случаях становится возможным создание констант. Сами константы — это
просто названия элементов, которые в процессе выполнения программы никогда не изменяются. Чтобы сделать элемент константой, начните его объявление со слова const. Например, следующим кодом создастся константа PI, значение которой постоянно будет равно числу 3,141592:

const double PI = 3.141592;

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

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

Использование констант в кодах программы

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


//CircleArea
//Вычисление площади по значению радиуса

#include "stdafx.h"
#using <mscorlib.dll>
using namespace System;

//С этой строки начинается выполнение программы
#ifdef _UNICODE
int wmain(void)
telse
int main(void)
#endif
{

double floatRadius;
double floatArea;
const double PI = 3.141592;

//Отображение вопроса о значении радиуса
Console::WriteLine(S"Укажите радиус круга");

//Получение ответа и преобразование его к числу с
//плавающей запятой
fltRadius = Double::Parse(Console::ReadLine());

//Вычисление площади круга
fltArea = PI*fltRadius*iltRadius;

//Отображение результата на экране
//Обратите внимание, что для этого число должно быть
//предварительно преобразовано в строку
Console::WriteLine(S"Площадь круга составляет {0} единиц.", Area.ToString () ) ;

//Ожидание, пока пользователь не остановит
//выполнение программы
Console::WriteLine(S"Нажмите клавишу Enter, чтобы остановить выполнение программы");
Console :: ReadLine () ;

return 0;

}

Код программы в файле CircleArea

Константы и борьба с ошибками

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

PI = 15;

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

Вверх

Строки как один из наиболее важных типов данных
Строки (тип String ) являются основными данными почти для всех создаваемых программ. По крайней мере почти ни одна программа не может без них обойтись. Чтобы эффективно их использовать, нужно владеть некоторым количеством не очень сложных приемов. Основные их них описаны в табл. 7.2.

Таблица 7.2. Способы обработки текстовой информации

Задача Решение
Комбинирование (объединение) двух строк sResult = String::Concat (str1,str2) ;
Отображение на экране текста со значениями Console::WriteLine(S"3начение 1:{0}Значение: 2{0}", str1, str2 );
Сравнивание двух строк cResult = str1->Equals(str2) ;
Определение позиции, начиная с которой одна строка входит во вторую nOffset = str1->IndexOf(str2) ;
Получение n символов начиная с m-й позиции mResult =str1->Substring(m,n) ;
Определение длины строки nLength = str1->Length ;
Удаление лишних пробелов в начале и в конце строки Result = str1->Trim();

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


// Strings101
// Shows some basic string manipulations

#include "stdafx.h"
#using <mscorlib.dll>
using namespace System;

// This is the entry point for this application
#ifdef _UNICODE
int wmain(void)
#else
int main(void)
#endif
{


String *pszString;
int nNumber;
double fltNumber;

//Combine two strings
pszString = String::Concat(S"Peanut butter", S" and jelly");
Console::WriteLine(pszString);

//Combine two strings while converting
nNumber = 3;
pszString = String::Concat(S"Converted ", nNumber.ToString());
Console::WriteLine(pszString);

//Convert to a floating point number
Console::WriteLine(S"Please enter a number");
fltNumber = Double::Parse(Console::ReadLine());

//Add a number to it
fltNumber = fltNumber + 30.5;
//Now write it out
Console::WriteLine(String::Concat("The number is ", fltNumber.ToString()));

//Write some strings out concatenating in place
Console::WriteLine(String::Concat(S"First ", S"Second ", S"Third"));

//Write using format syntax
Console::WriteLine(S"A string: {0}\nAn integer {1}", S"my string", nNumber.ToString());

//Get rid of leading and trailing whitespace
pszString = S" Hello World ";
Console::WriteLine(S"With: {0}\n and without leading/trailing whitespace: {1}", pszString, pszString->Trim());

//Find the character in the middle of the string
Console::WriteLine(S"The middle character of {0} is \"{1}\"", pszString,

pszString->Substring(pszString->Length/2, 1));

//Hang out until the user is finished
Console::WriteLine(S"Hit the enter key to stop the program");
Console::ReadLine();
return 0;

}

Код программы в файле Strings101

Результат:

Подсказка


Назад | Начало урока | Вверх | Вперед
Содержание