Справочник по C# Герберт Шилдт ббк 32. 973. 26018 75 Ш57 удк 681 07 Издательский дом "Вильямс" Зав редакцией
Скачать 5.05 Mb.
|
Глава 3 Типы данных, литералы и переменные 54 Часть I. Язык C# этой главе рассматриваются три основных элемента языка C#: типы данных, литералы и переменные. В общем случае типы данных определяют класс задач, к которым они могут быть применены. В C# предусмотрен богатый набор встроенных типов данных, что позволяет использовать этот язык для широкого диапазона приложений. Программист может создавать переменные нужного ему типа и определять константы, которые в языке C# называются литералами. О важности типов данных Типы данных имеют в C# особое значение, поскольку C# — строго типизированный язык. Это значит, что все операции проверяются компилятором на соответствие типов. Некорректные операции не компилируются. Таким образом, контроль типов способствует предотвращению ошибок и повышает надежность программ. Для обеспечения контроля типов необходимо, чтобы все переменные, выражения и значения имели определенный тип. Например, в языке не допускается, чтобы переменная не имела типа. Более того, тип значения определяет, какие операции разрешено выполнять с таким значением. Операция, разрешенная для одного типа, может быть недопустимой для другого. Типы значений в C# C# содержит две категории встроенных типов данных: типы значений и ссылочные типы. Ссылочные типы определяются в классах, но о классах речь еще впереди. Ядро языка C# составляют 13 типов, перечисленных в табл. 3.1. Это — встроенные типы, которые определяются ключевыми словами C# и доступны для использования в любой C#- программе. Термин “тип значения” применяется к переменным, которые непосредственно содержат значения. (Для сравнения: переменные ссылочных типов содержат ссылки на реальные значения.) Таким образом, типы значений в C# во многом подобны типам данных, определенным в других языках программирования (например, C++). Типы значений также называют простыми типами. В C# строго определяется диапазон и поведение каждого типа значения. Исходя из требований переносимости, C# на этот счет не допускает никаких компромиссов. Например, тип int должен быть одинаковым во всех средах выполнения. Поэтому при переходе на другую платформу не должна возникать необходимость переделки кода. Несмотря на то, что строгое задание размерных характеристик типов значений может вызвать в некоторых средах небольшие потери производительности, ради достижения переносимости с ними необходимо смириться. Таблица 3.1. Типы значений в C# Ключевое слово Тип bool Логический, или булев, представляет значения ИСТИНА/ЛОЖЬ byte 8-разрядный целочисленный без знака char Символьный decimal Числовой тип для финансовых вычислений double C плавающей точкой двойной точности В Глава 3. Типы данных, литералы и переменные 55 Окончание таблицы 3.1. Ключевое слово Тип float С плавающей точкой int Целочисленный long Тип для представления длинного целого числа sbyte 8-разрядный целочисленный со знаком short Тип для представления короткого целого числа uint Целочисленный без знака ulong Тип для представления длинного целого числа без знака ushort Тип для представления короткого целого числа без знака Целочисленные типы В C# определено девять целочисленных типов: char , byte , sbyte , short , ushort , int , uint , long и ulong . Однако тип char в основном используется для представления символов (подробнее рассматривается ниже в этой главе). Остальные восемь целочисленных типов предназначены для числовой обработки данных. Размер значений в битах и диапазоны представления для каждого из этих восьми типов приведены в табл. 3.2. Таблица 3.2. Характеристики целочисленных типов Тип Размер в битах Диапазон byte 8 0 – 255 sbyte 8 -128 – 127 short 16 -32 768 – 32 767 ushort 16 0 – 65 535 int 32 -2 147 483 648 – 2 147 483 647 uint 32 0 – 4 294 967 295 long 64 -9 223 372 036 854 775 808 – 9 223 372 036 854 775 807 ulong 64 0 – 18 446 744 073 709 551 615 Согласно этой таблице, в C# определены обе версии всех целочисленных типов: как со знаком, так и без него. Различие между этими версиями заключается в способе интерпретации старшего разряда. Если в программе задано целочисленное значение со знаком, то компилятор сгенерирует код, в котором предусматривается, что старший разряд такого значения используется в качестве флага знака (sign flag). Если флаг знака равен нулю, значит, число положительно, если он равен 1, то число отрицательно. Отрицательные числа почти всегда представляются в виде дополнения до двух. Для получения дополнительного кода сначала все разряды числа, за исключением знакового, инвертируются, а затем результат инвертирования суммируется с единицей. Наконец, флаг знака устанавливается равным единице. Целочисленные значения со знаком широко используются во многих алгоритмах, но следует понимать, что по абсолютному значению (по модулю) они составляют только половину от своих “родственников” со знаком. Например, вот как выглядит в двоичном коде число 32 767 в качестве значения типа short : 01111111 11111111 56 Часть I. Язык C# Если старший разряд этого значения со знаком установить равным 1, оно будет интерпретироваться как -1 (с использованием формата дополнения до двух). То же значение, но объявленное с типом ushort (и с “единичным” старшим разрядом), будет равно числу 65 535. Вероятно, самым популярным целочисленным типом является int . Переменные типа int часто используются для управления циклами, индексации массивов и в математических вычислениях общего назначения. Если нужно обрабатывать величины, диапазон которых превышает диапазон, допустимый для типа int , у вас есть несколько возможных вариантов для выбора. Если вы не предполагаете обрабатывать отрицательные числа, можно воспользоваться типом uint (в этом случае диапазон представления чисел увеличится вдвое). Для работы с большими числами со знаком, возможно, подойдет тип long , без знака — тип ulong . Рассмотрим, например, программу, которая вычисляет расстояние от Земли до Солнца в дюймах. Поскольку результат будет представлять собой довольно большое число, для его хранения в этой программе используется переменная типа long // Вычисляем расстояние от Земли до Солнца в дюймах. using System; class Inches { public static void Main() { long inches; long miles; miles = 93000000; // 93 000 000 миль до Солнца //5280 футов в миле, 12 дюймов в футе inches = miles*5280* 12; Console.WriteLine("Расстояние от Земли до Солнца: " + inches + " дюймов."); } } Вот как выглядит результат выполнения этой программы: Расстояние от Земли до Солнца: 5892480000000 дюймов. Ясно, что полученный результат невозможно было бы сохранить в переменной типа int или uint Самый маленький диапазон представления имеют целочисленные типы byte и sbyte . Тип byte предназначен для хранения чисел без знака от 0 до 255. Переменные типа byte особенно полезны для обработки двоичных данных, например, полученных в результате опроса некоторого датчика. Для небольших целых чисел со знаком используется тип sbyte . Приведем пример применения типа byte для управления циклом for , который вычисляет сумму чисел от 1 до 100. // Использование типа byte. using System; class Use_byte { public static void Main() { byte x; int sum; Глава 3. Типы данных, литералы и переменные 57 sum = 0; for(x = 1; x <= 100; x++) sum = sum + x; Console.WriteLine("Сумма чисел от 1 до 100 равна " + sum); } } Результат выполнения этой программы таков: Сумма чисел от 1 до 100 равна 5050 Поскольку цикл for повторяется от 0 до 100 раз, что не выходит за пределы диапазона типа byte , нет необходимости использовать для управления циклом переменную с типом, допускающим более широкий диапазон представления чисел. Если вам понадобится целочисленный тип с диапазоном представления, большим, чем у byte и sbyte , но меньшим, чем у int или uint , используйте тип short или ushort Типы для представления чисел с плавающей точкой Типы с плавающей точкой могут представлять числа с дробными компонентами. Таких типов только два: float и double . Для значений типа float используется 32 бита, которые позволяют представить числа в диапазоне от 1,5Е—45 до 3,4Е+38. Для значений же типа double (т.е. удвоенной точности) используется 64 бита, позволяющие представить числа в диапазоне от 5Е-324 до 1,7Е+308. Из этих двух типов с плавающей точкой гораздо большей популярностью у программистов пользуется тип double . Одной из основных причин этого является использование double -значений множеством математических функций библиотеки классов C# (библиотека .NET Framework). Например, метод Sqrt() , который определен в стандартном классе System.Math , возвращает double -значение, являющееся квадратным корнем из double -аргумента. В следующей программе метод Sqrt() используется для вычисления радиуса круга, исходя из его площади. // Находим радиус круга по его площади. using System; class FindRadius { public static void Main() { double r; double area; area = 10.0; r = Math.Sqrt(area / 3.1416); Console.WriteLine("Радиус равен " + r); } } Результат выполнения этой программы таков: Радиус равен 1,78412203012729 58 Часть I. Язык C# В этой программе стоит обратить ваше внимание на то, как вызывается метод Sqrt() : его имени предшествует имя Math . И здесь нет ничего удивительного, ведь, как уже упоминалось выше, метод Sqrt() — член класса Math . (Точно также при вызове метода WriteLine() его имени предшествовало имя класса Console .) Однако необходимо отметить, что не все стандартные методы вызываются посредством первоначального указания имени их “родного” класса. В следующей программе демонстрируется несколько тригонометрических функций, которые являются частью математической библиотеки C#. Они также оперируют данными типа double . Программа отображает значения синуса, косинуса и тангенса для углов (измеряемых в радианах) от 0,1 до 1,0. // Демонстрируем использование методов Math.Sin(), // Math.Cos() и Math.Tan(). using System; class Trigonometry { public static void Main() { double theta; // Угол задан в радианах. for(theta = 0.1; theta <= 0.3; theta = theta + 0.1) { Console.WriteLine("Синус угла " + theta + " равен " + Math.Sin(theta)); Console.WriteLine("Косинус угла " + theta + " равен " + Math.Cos(theta)); Console.WriteLine("Тангенс угла " + theta + " равен " + Math.Tan(theta)); Console.WriteLine(); } } } Вот как выглядит часть результатов выполнения этой программы: Синус угла 0,1 равен 0,0998334166468282 Косинус угла 0,1 равен 0,995004165278026 Тангенс угла 0,1 равен 0,100334672085451 Синус угла 0,2 равен 0,198669330795061 Косинус угла 0,2 равен 0,980066577841242 Тангенс угла 0,2 равен 0,202710035508673 Синус угла 0,3 равен 0,29552020666134 Косинус угла 0,3 равен 0,955336489125606 Тангенс угла 0,3 равен 0,309336249609623 Для вычисления синуса, косинуса и тангенса используются стандартные библиотечные методы Math.Sin() , Math.Cos() и Math.Tan() . Подобно методу Math.Sqrt() эти тригонометрические методы вызываются с аргументом типа double и возвращают результат типа double . При этом углы должны быть заданы в радианах. Тип decimal Возможно, самым интересным в C# числовым типом является тип decimal , который предназначен для выполнения вычислений, связанных с денежными единицами. Тип decimal для представления значений в диапазоне от 1E-28 до 7,9Е+28 использует Глава 3. Типы данных, литералы и переменные 59 128 бит. Применение обычной арифметики с плавающей точкой к десятичным значениям чревато ошибками округления. Во избежание этих ошибок и предусмотрен тип decimal , который способен точно представить до 28 десятичных разрядов (в некоторых случаях до 29). Способность представлять десятичные значения без ошибок округления делает этот тип особенно полезным для вычислений в денежной сфере. Рассмотрим программу, которая использует тип decimal для вычисления цены со скидкой на основе заданных значений исходной цены и процента скидки. // Использование типа decimal для вычисления скидки. using System; class UseDecimal { public static void Main() { decimal price; decimal discount; decimal discounted_price; // Вычисляем цену со скидкой. price = 19.95m; discount = 0.15m; // Ставка дисконта равна 15%. discounted_price = price - (price * discount); Console.WriteLine("Цена со скидкой: $" + discounted_price); } } Результат работы этой программы выглядит так: Цена со скидкой: $16,9575 Обратите внимание на то, что задание decimal -констант сопровождается наличием суффикса m . Без него эти константы интерпретировались бы как стандартные константы с плавающей точкой, несовместимые с типом данных decimal . При этом переменной типа decimal можно присвоить любое целочисленное значение (например, 10) без использования суффикса m . (О задании числовых констант мы еще поговорим более подробно позже в этой главе.) А вот еще один пример использования типа decimal . В следующей программе вычисляется будущая стоимость капиталовложений, которые имеют фиксированную годовую процентную ставку для прибыли на инвестированный капитал. /* Использование типа decimal для вычисления будущей стоимости капиталовложений. */ using System; class FutVal { public static void Main() { decimal amount; decimal rate_of_return; int years, i; amount = 1000.0M; rate_of_return = 0.07M; years = 10; 60 Часть I. Язык C# Console.WriteLine("Исходный вклад: $" + amount); Console.WriteLine("Норма прибыли: " + rate_of_return); Console.WriteLine("Через " + years + " лет"); for(i = 0; i < years; i++) amount = amount + (amount * rate_of_return); Console.WriteLine("Будущая стоимость равна $" + amount); } } Результаты работы этой программы имеют такой вид: Исходный вклад: $1000 Норма прибыли: 0,07 Через 10 лет Будущая стоимость равна $1967,15135728956532249 Обратите внимание на (даже излишнюю) точность результата! Ниже в этой главе вы узнаете, как форматировать результат, чтобы он выглядел более привлекательно. Символы В C# символы представляются не 8-разрядными величинами, как в других языках программирования (например, C++), а 16-разрядными. Для представления символов в C# используется Unicode (уникод), 16-разрядный стандарт кодирования символов, позволяющий представлять алфавиты всех существующих в мире языков. Хотя во многих языках (например, в английском, немецком, французском) алфавиты относительно невелики, существуют языки (например, китайский), построенные на очень больших наборах символов, которые нельзя представить восьмью битами. Чтобы можно было “охватить” символьные наборы всех языков, требуются 16-разрядные значения. Таким образом, в C# char — это 16-разрядный тип без знака, который позволяет представлять значения в диапазоне 0—65 535. Стандартный 8-разрядный набор символов ASCII составляет лишь подмножество Unicode с диапазоном 0—127. Таким образом, ASCII- символы — это действительные C#-символы. Символьной переменной можно присвоить значение, заключив соответствующий символ в одинарные кавычки. Например, чтобы присвоить значение буквы X переменной ch , нужно выполнить следующие инструкции: char ch; ch = 'X'; Чтобы вывести char -значение, хранимое в переменной ch, можно использовать метод WriteLine() Console.WriteLine("Это ch: " + ch); Хотя тип char определяется в C# как целочисленный, его нельзя свободно смешивать с целыми числами во всех случаях без разбору. Все дело в том, что автоматического преобразования целочисленных значений в значения типа char не существует. Например, следующий фрагмент программы содержит ошибку. char ch; ch = 10; // Ошибка, это работать не будет. Поскольку 10 — целое число, оно не может быть автоматически преобразовано в значение типа char . При попытке скомпилировать этот код вы получите сообщение об ошибке. Ниже в этой главе мы рассмотрим “обходной путь”, позволяющий обойти это ограничение. Глава 3. Типы данных, литералы и переменные 61 Тип bool Тип bool представляет значения ИСТИНА/ЛОЖЬ, которые в C# определяются зарезервированными словами true и false . Таким образом, переменная или выражение типа bool будет иметь одно из этих двух значений. В C# не определено ни одно преобразование значения типа bool в целочисленное значение. Например, число 1 не преобразуется в значение true , а число 0 — в значение false Рассмотрим использование типа bool на примере следующей программы: // Демонстрация использования значений типа bool. using System; class BoolDemo { public static void Main() { bool b; b = false; Console.WriteLine("b содержит " + b); b = true; Console.WriteLine("b содержит " + b); // Значение типа bool может управлять if-инструкцией. if(b) Console.WriteLine("Эта инструкция выполняется."); b = false; if(b) Console.WriteLine( "Эта инструкция не выполняется."); // Оператор отношения возвращает результат типа bool. Console.WriteLine("10 > 9 равно " + (10 > 9)); } } Эта программа генерирует следующий результат: b содержит False b содержит True Эта инструкция выполняется. 10 > 9 равно True Итак, что интересного в этой программе? Во-первых, при выводе bool -значения методом WriteLine() отображаются значения True или False . Во-вторых, одного значения bool -переменной вполне достаточно для управления if -инструкцией, т.е. нет необходимости в написании if -инструкции такого вида. if(b == true)... В-третьих, результатом выполнения оператора отношения (например, оператора “ < ”) является bool -значение. Поэтому результат выражения 10 > 9 приводит к отображению значения True . При использовании выражения 10 > 9 , как видно в этой программе, потребовался дополнительный набор круглых скобок, поскольку оператор “ + ” имеет более высокий приоритет, чем оператор “ > ”. |