Глава 7
В этой главе...
Компьютерные программы занимаются обработкой различных данных, которые, в свою
очередь, могут принадлежать различным типам. Например, в электронных таблицах
данные могут быть представлены числами с плавающей запятой, в инвентаризационных
системах— порядковыми записями, а в программах для рисования— автофигурами.
Однако нужно понимать, что, хотя режиссеры Голливуда и создают мифы о всемогущих
и всезнающих компьютерах, на самом деле это не так. Компьютер может работать только
с данными строго определенных типов, причем предварительно (нужно указать, данные
какого именно типа будут ему передаваться. В этой главе вы познакомитесь с основными
типами данных, особенно детально обсуждается один из самых важных, который
обозначается словом String .
Вверх
Строгие языки программирования, такие как C++, требуют, чтобы программисты заранее объявляли, данные каких типов они собираются использовать. Например, если вы хотите сохранить число, нужно заранее сообщить об этом компьютеру, чтобы он был готов принять от вас это число.
Строгие языки имеют ряд неоспоримых преимуществ. Например, если вы случайно попытаетесь запись о сотруднике интерпретировать как число, компилятор выдаст сообщение об ошибке. Это хорошо, поскольку без подобного контроля вы можете непреднамеренно уничтожить важную информацию. Предположим, запись о сотруднике занимает 8 байт памяти, а для числа отводится только 2 байта. Когда вы удаляете запись, освобождается 8 байт памяти.
Помимо строгих языков программирования, есть также нестрогие, к числу которых относится, например, JavaScript. Они не требуют предварительного объявления типов данных и сами оценивают данные в тот момент, когда с ними сталкиваются. Например, если вы присвоите переменной текстовое значение root , языковой процессор сделает эту переменную текстовой. Нестрогие языки программирования могут быть проще в использовании, и некоторые программисты отдают им предпочтение, однако они не умеют предостерегать вас от ошибок, как это делают строгие языки.
Вверх
В языке C++ необходимо заранее объявлять переменную и указывать тип используемых
Процесс создания переменной называется ее определением. Чтобы определить переменную, нужно просто указать используемый ею тип данных, а затем ее имя. Ниже приведено несколько примеров определения переменных. В следующей строке создается переменная foe типа int (integer, число):
Далее создается переменная bar типа char (character, символ):
И ниже сообщается о том, что функция min принимает два числа типа int в качестве значений параметров и возвращает другое число типа int в качестве результата. (Более подробно определение и использование функций рассматривается в главе 12.)
Когда вы объявляете тип данных, вы просто указываете, значения какого типа
Когда вы что-то определяете, вы создаете это физически. В случае с переменными это различие
Познакомившись со структурами
(глава 9) и классами (глава 17), вы увидите, что для них определение и объявление
— это два разных действия, каждое из которых выполняется отдельно.
В языке C++ имеется набор заранее определенных типов данных, которые сразу можно
Вот три наиболее часто используемых типа данных:
Строгие и нестрогие языки программирования
Если же вы используете эту запись как число и затем это число удалите, помимо числа будет удалено еще 6 байт памяти, в которых может содержаться очень важная информация. Возможно, потеря этой информации будет иметь для вас очень плохие последствия.
Объявление переменных
ею данных. Другими словами, нужно сразу указать компилятору, значения какого типа могут быть присвоены переменной. (Более подробно об использовании переменных речь идет в главе 8. Пока же рассматривайте переменную как нечто, способное сохранять информацию, например как ячейку электронной таблицы, которая, однако, имеет свое собственное имя.) Объявить тип переменной можно несколькими способами, но обычно это делается в момент ее создания.
int too;
char bar;
int min
(int first, int second);
будет принимать эта переменная. При этом выделения памяти для объявляемой
переменной не происходит. (Физически переменная не создается.)
не столь принципиально. Почти всегда при определении переменной указывается
и ее тип. Хотя делать это можно и раздельно.
Наиболее часто используемые типы данных
использовать. Используя стандартные типы данных, можно создавать собственные, более
сложные, типы данных, что описывается в главе 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++, поскольку таким образом автоматически выявляются наиболее распространенные ошибки. (Это действительно лучше, чем игнорирование компилятором неправильного использования типов данных, за исключением разве что тех случаев, когда вы создаете самоуничтожающуюся программу.)
Приведенный ниже код будет работать корректно.
//Присвоение переменной значения 7
//Создание переменной типа int
int nSize;
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);
Если вас интересуют другие подробности приведенного выше кода, прочитайте этот абзац.
Буква S перед значением "3.14" указывает компилятору, что это должна быть строка, предусмотренная для использования в среде .NET. На самом деле можно создавать текстовые значения самых разных видов, но в данном случае создается именно такое значение, которое для среды .NET подходит наилучшим образом.
Использование двух двоеточий (::) в фрагменте Double :: Parse
является специальным синтаксическим приемом, позволяющим применять методы (такие, как Parse) объектных типов (таких, как double) без предварительного создания самих объектов.
В будущем вы сможете по достоинству оценить эту
возможность языка C++.
Вверх
Иногда при написании программы требуется многократно использовать одно и то же число или одну и ту же строку. Например, если вы знакомы с математикой, то знаете, что число n всегда равно 3,141592.... Если вы составляете программу, вычисляющую ваш гороскоп, то используемая в вычислениях дата вашего рождения также будет оставаться неизменной.
В подобных случаях становится возможным создание констант. Сами константы — это
Использование констант значительно облегчает чтение кодов ваших программ, поскольку обычные числа можно заменить объясняющими их именами. Кроме того, это удобно, если одно и то же значение должно быть изменено во всей программе. Для этого нужно просто определять новое значение для соответствующей константы. Например, если вы вдруг решите приспособить написанную вами программу к некоторой виртуальной реальности, где пространство и время деформированы и число п равно, скажем, числу 7, вам нужно будет всего лишь изменить значение соответствующей константы. При этом все вычисления, выполняемые вашей программой с участием числа п, автоматически будут использовать новое указанное значение, что избавляет вас от необходимости самостоятельно просматривать коды всей
программы и вносить нужные коррективы.
В кодах программы использовать константы можно везде, где может быть использован
Ниже приведен код программы, в которой используются константы и выполняется преобразование типов данных. В процессе ее выполнения пользователю предлагается указать радиус, в соответствии с которым вычисляется площадь круга. Полученный результат отображается на экране. Не поленитесь и создайте такую же программу. Для этого создайте новую программу .NET, наберите приведенный здесь код и запустите программу на выполнение.
#include "stdafx.h"
//С этой строки начинается выполнение программы
//Отображение вопроса о значении радиуса
//Получение ответа и преобразование его к числу с
//Вычисление площади круга
//Отображение результата на экране
//Ожидание, пока пользователь не остановит
return 0;
Код программы в файле CircleArea
Еще одно преимущество использования констант: если вы случайно попытаетесь изменить их значения в процессе выполнения программы, компилятор заметит это и выдаст сообщение об ошибке. Предположим, например, что где-то в кодах программы вы набрали такую строку:
Как только компилятор увидит этот код, он сразу же выдаст сообщение об ошибке, в котором будет сказано, что изменять значение константы нельзя. Своевременное обнаружение и устранение таких, казалось бы, мелких ошибок позволяет избежать возникновения в дальнейшем более серьезных проблем.
Вверх
Таблица 7.2. Способы обработки текстовой информации
Константы – то, что никогда
не меняется
просто названия элементов, которые в процессе выполнения программы никогда не изменяются. Чтобы сделать элемент константой, начните его объявление со слова const. Например, следующим кодом создастся константа PI, значение которой постоянно будет равно числу 3,141592:
const double PI = 3.141592;
элемент того же типа, что и сама константа. Например, если у вас есть константа, представляющая число с плавающей запятой, вы можете применять ее везде, где будет уместно использование числа с плавающей запятой. Так. константа PI может быть умножена на диаметр круга, в результате чего будет вычислена длина окружности.
Использование констант в кодах программы
//CircleArea
//Вычисление площади по значению радиуса
#using <mscorlib.dll>
using namespace System;
#ifdef _UNICODE
int wmain(void)
telse
int main(void)
#endif
{
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 () ;
Константы и борьба с ошибками
PI = 15;
Строки как один из наиболее важных типов данных
Строки (тип String ) являются основными данными почти для всех создаваемых программ. По крайней мере почти ни одна программа не может без них обойтись. Чтобы эффективно их использовать, нужно владеть некоторым количеством не очень сложных приемов.
Основные их них описаны в табл. 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
Результат:
Назад |
Начало урока |
Вверх |
Вперед
Содержание