Все вроде бы хорошо, но вот вопрос: а если в переменной S строковое представление числа содержит ошибку? Например, вместо точки стоит запятая или вместо одного из цифровых знаков случайно попала буква? Что тогда? А тогда все плохо: метод ToDouble не сработает. Более того, сбой в выполнении этого метода приведет к аварийному завершению программы из-за неправильных данных. Такое аварийное завершение называется прекращением работы программы из-за возникновения исключительной ситуации. Говорят, что системой выработано исключение.
Когда вырабатывается исключение, исполняемая программа завершается. На экран выводится информация о том, в каком именно месте программы произошла авария и в чем суть ошибки. Хорошо, если эта информация будет выведена в тот момент, когда с программой работает автор – он быстро найдет неправильность в исходном тексте и внесет нужные исправления. А если программа уже признана готовой и передана пользователю-непрограммисту? Он начал работать с ней и в ответ на предложение ввести с клавиатуры вещественное число ввел его, но с ошибкой, наподобие описанной выше. Вся его работа пошла насмарку, надо начинать исполнение программы сначала. Это, разумеется, очень плохо. Такие дефекты в программах недопустимы. Следовательно, программист должен что-то предпринять, чтобы подобное не случалось.
Но возникает вопрос: а что же можно предпринять? Можно, например, попытаться написать программный код, анализирующий введенную строку до выполнения метода ToDouble. Надо будет предварительно проверить наличие точки в числе (да еще чтобы она была именно точкой, а не запятой и только одна!), а потом убедиться, что все остальные символы – это цифры. Да и вместо знака минус (если он есть) не вкрался какой-нибудь «левый» символ.
Нетрудно представить, какой длины будет этот программный код. А если учесть, что таких переводов в число может быть в программе много, получается, что программист должен значительную часть кода посвятить «защите от дурака» – а как еще назвать пользователя, набирающего число с нелепой ошибкой? Однако такой подход (программирование специального защитного кода) нельзя назвать удачным.
Есть иной выход. Попробуем понять, что делает система, обнаружив невозможность преобразования данных. Она ведь не сообщает, какую именно неправильность внес в выполнение программы пользователь. Система просто информирует, что данные неправильны – и все. То есть исключение имеет причину: «неправильные данные». Вот на эту информацию и может положиться программист. В языке C# есть возможность запрограммировать реакцию на исключение, т. е. написать такой код, который будет работать только в том случае, если возникнет исключение из-за неправильных данных. Если такой код будет включен в программу, то программа не завершит работу аварийно, а выполнит предусмотренный код реакции на исключение. И спокойно продолжит работу дальше.
Заметим, что исключение может быть выработано системой не только из-за неправильных данных. Причин возникновения исключений очень много. Это и попытка читать данные из несуществующего файла, и деление на 0, и еще много разных случаев. Все причины классифицированы, имеют соответствующие коды. А программирование реакции на исключение обеспечивает набор специальных операторов языка. Имеется также специальный класс Exception, свойства и методы которого позволяют работать с исключениями. В этом классе определены виды исключений, которые задаются определенными идентификаторами.
Сейчас перед нами не стоит задача досконального изучения всех возможных ситуаций, когда может возникнуть исключение. По мере изучения языка вы будете изучать и разные ситуации. Здесь же мы на простом примере посмотрим слегка упрощенный механизм организации обработки исключения.
Если на некотором участке программного кода предполагается возможность появления исключений, т. е. возможна ошибка пользователя, приводящая к аварийному завершению программы, то нужно этот участок оформить как составной оператор (блок) – заключить его в фигурные скобки. Перед блоком поставить зарезервированное в языке слово try (испытание, контроль). Итак, контролируемый блок имеет вид:
try
{ … операторы контролируемого блока
}
Вслед за этим блоком нужно расположить другой блок, в котором следует описать программный код, обрабатывающий исключение. Этот блок имеет вид:
catch (код исключения)
{ … операторы обработки исключения
}
Слово catch понимается в смысле «захват, ловушка». Как это работает? Если в процессе выполнения ни в одном операторе контролируемого блока не будет выработано исключение, то операторы блока обработки исключения просто не будут работать. Если же исключение появится, то система определит, в чем причина исключения, и установит его код. Далее она установит, есть ли в программе catch-блок, предназначенный для обработки этого кода, т. е. найдет блок, в круглых скобках которого указан этот код. Если такой блок найдется, то он и продолжит работу программы. Рассмотрим простой пример.
int k;
double d;
do
{
try // контролируемый блок
{k = Convert. ToInt32(Console. ReadLine();} // 1
catch (FormatException) // блок обработки исключения
{ k = – 1;
Console.WriteLine(”Введенное значение – не число”);
}
if (k > 0)
{
d = 1 / (double) k;
Console.WriteLine(”обратное значение = {0}”, d);
}
} while (k != 0);
Суть программы проста. Пользователь вводит целое число. В программе вычисляется обратное значение числа и выводится на экран. Процесс продолжается до тех пор, пока введенное число не окажется нулем. Если же пользователь по ошибке введет нечисловое значение или нецелочисленное (включит в состав числа десятичную точку), то оператор 1 выработает исключение, после чего сработает блок обработки. В нем на экран будет выведено предупреждение, а переменная k получит отрицательное значение. Цикл не прервется, потому что условие продолжения цикла включает и отрицательные значения, и пользователь сможет ввести следующее число.
Сделаем обобщение. В действительности в программе может быть не один блок обработки исключения, а много. Каждый из них может быть настроен на обработку какого-то конкретного кода ошибки. То есть в контролируемом блоке могут возникать разные исключения, каждое из которых будет обработано специальным блоком.
Мы рассмотрели упрощенный вариант механизма обработки исключений. Полностью механизм обработки исключений можно найти в [1].
10. МАССИВЫ
Определим для начала ряд понятий, общих для всех языков программирования.
Массивом называют упорядоченную совокупность значений одного типа. Значение, входящее в массив, принято называть элементом массива. Каждый элемент массива имеет индексы, определяющие порядок элемента в массиве. Индексы задаются целочисленным типом. Число индексов характеризует размерность массива. Если конечное значение индексов задано константным выражением, то число элементов массива известно в момент его объявления и ему может быть выделена память еще на этапе компиляции программы. Такие массивы называются статическими. Если же конечное значение индексов зависит от переменной, получающей значение в процессе выполнения программы, то такой массив называют динамическим. Память ему может быть отведена только динамически в процессе выполнения программы, когда становятся известными значения соответствующих переменных. Массиву, как правило, выделяется непрерывная область памяти.
В языке C# каждый индекс изменяется в диапазоне от 0 до некоторого конечного значения. Массивы в языке C# являются настоящими динамическими массивами независимо от того, задана размерность явно или через переменную. Как следствие этого, массивы относятся к ссылочным типам – переменная, определяющая массив, содержит ссылку на нулевой элемент массива. Память массиву отводится динамически в куче уже при выполнении программы.
Массивы могут быть одномерными и многомерными. Объявление массива выглядит следующим образом:
тип[, ... ,] объявители;
Число запятых, увеличенное на единицу, и задает размерность массива. Например, когда в квадратных скобках нет ни одной запятой, то объявляется одномерный массив.
Каждый объявитель может быть именем (идентификатором) или именем с инициализацией. В первом случае речь идет об отложенной инициализации. Нужно понимать, что при объявлении с отложенной инициализацией сам массив не формируется, а создается только ссылка на массив, имеющая неопределенное значение null. Поэтому пока массив не будет реально создан и его элементы инициализированы, использовать его в вычислениях нельзя.
10.1. ЧИСЛЕННЫЕ МАССИВЫ
Рассмотрим несколько примеров. Ниже объявлено 3 целочисленных массива с отложенной инициализацией:
int [] a, b, c;
Возможна инициализация массива при его объявлении. Ниже инициализация является явной и задается константным массивом:
double [ ] x = {5.5, 6.6, 7.7};
int [,] matrix = {{1, 2}, {3, 4}};
Следуя синтаксису, элементы константного массива необходимо заключать в фигурные скобки. Если массив инициализируется константным массивом, то в динамической памяти создается константный массив с заданными значениями, с которым и связывается ссылка.
Инициализация возможна также с помощью операции new. Назначение этой операции в том и состоит, чтобы выделять место в динамической памяти. Например:
int [] d = new int [5];
Здесь объявлен динамический вещественный массив, в котором будут храниться 5 целых чисел. Массив создается в динамической памяти, его элементы получают начальные нулевые значения, и ссылка связывается с этим массивом.
Как обычно задаются элементы массива, если они не заданы при инициализации? Они либо вычисляются, либо вводятся пользователем. Рассмотрим пример использования массивов.
// объявляются три одномерных массива A, B, C
int [] A = new int [5], B = new int [5], C = new int [5];
// с клавиатуры заполняется данными массив A
for (int i = 0; i < 5; i++) A [i]= Convert. ToInt32(Console. ReadLine( ));
// вычисляются элементы массива C
for (int i = 0; i < 5; i++) C[i] = A[i] + B [i];
// объявление массива с явной инициализацией
int [] x = {5, 56, 6, 7, 7};
// объявление массивов с отложенной инициализацией
int [] u, v;
u = new int [3]; // инициализация массива u
for (int i = 0; i < 3; i++) u [i] = i + 1;
// v = {1, 2, 3}; // присваивание константного массива недопустимо!
v = new int [4];
v = u; // допустимое присваивание – массивы одного класса
int [,] w = new int [3, 5];
// v = w; // недопустимое присваивание: объекты разных классов
На что следует обратить внимание, анализируя данный текст примера? Показаны разные способы объявления массивов. Вначале объявляются одномерные массивы A, B и C. Значения элементов этих трех массивов имеют один и тот же тип int. То, что они содержат одинаковое число элементов, произошло по воле программиста, а не диктовалось требованиями языка. Значения в массив А вводились, а в массив B нет, но сложение элементов корректно, потому что при объявлении элементы массива B получили нулевые значения.
Массив x объявлен с явной инициализацией. Число и значения его элементов определяются константным массивом. Массивы u и v объявлены с отложенной инициализацией. В последующих операторах массив u инициализируется в объектном стиле (через new), а его элементы получают значения в цикле.
Обратите внимание на закомментированный оператор присваивания. В отличие от инициализации использовать константный массив в правой части оператора присваивания недопустимо. Эта попытка приводит к ошибке, поскольку v – это ссылка и ей нельзя присвоить константный массив. А вот ссылку присвоить можно. Что происходит в операторе присваивания v = u? Это корректное ссылочное присваивание: хотя u и v имеют разное число элементов, но они являются объектами одного класса – оба массива целочисленные с одной и той же размерностью. В результате присваивания память, отведенная массиву v, освободится и будет в дальнейшем освобождена сборщиком мусора. Обе ссылки u и v будут теперь указывать на один и тот же массив, так что изменение элемента одного массива немедленно отразится на другом массиве. Имена u и v становятся синонимами (или псевдонимами друг друга).
Далее определяется двумерный массив w и делается попытка выполнить оператор присваивания v = w. Это ссылочное присваивание некорректно, поскольку объекты w и v разных классов: оба они целочисленные, но имеют разную размерность. Для них не выполняется требуемое для присваивания согласование по типу.
В вышеприведенном примере объявлялись статические массивы, поскольку нижняя граница равна нулю по определению, а верхняя всегда задавалась в этих примерах константой. Вспомним, что в C# все массивы, независимо от того, каким выражением описывается граница, рассматриваются как динамические, и память для них распределяется в куче. Чисто синтаксически нет существенной разницы в объявлении статических и динамических массивов. Выражение, задающее границу изменения индексов, в динамическом случае содержит переменные. Единственное требование – значения переменных должны быть определены в момент объявления. Например:
// объявление динамического массива A1
Console. WriteLine("Введите число элементов массива A1");
int size = Convert. ToInt32(Console. ReadLine( ));
int [] A1 = new int [size];
В особых комментариях приведенный код не нуждается. Здесь верхняя граница массива определяется пользователем, вводящим значение.
Теперь можно рассмотреть особый вид оператора цикла – оператор foreach, о котором было упомянуто в разделе 7. Его синтаксис:
foreach (тип идентификатор in массив) оператор
Цикл работает в полном соответствии со своим названием – тело цикла выполняется для каждого элемента в массиве. Тип идентификатора должен быть согласован с типом элементов, хранящихся в массиве данных. Предполагается также, что элементы массива упорядочены. На каждом шаге цикла идентификатор, задающий текущий элемент массива, получает значение очередного элемента в соответствии с порядком, установленным на элементах массива. С этим текущим элементом и выполняется тело цикла. Шаг цикла выполняется столько раз, сколько элементов находится в массиве. Цикл заканчивается, когда полностью выбраны все элементы массива.
В приведенном ниже примере показана работа с трехмерным массивом. Массив создается с использованием циклов типа for, а при нахождении суммы его элементов, минимального и максимального значения используется цикл foreach:
int [ , , ] arr3d = new int [10, 10, 10];
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
for (int k = 0; k < 10; k++)
arr3d [i, j, k] = Convert. ToInt32(Console. ReadLine( ));
long sum = 0; int min = arr3d [0, 0, 0], max = arr3d [0, 0, 0];
foreach (int item in arr3d) // для каждого элемента в массиве
{
sum += item;
if (item > max) max = item;
else if (item < min) min = item;
}
Console. WriteLine ("sum = {0}, min = {1}, max = {2}", sum, min, max);
Серьезным недостатком циклов foreach в языке C# является то, что цикл работает только на чтение, но не на запись элементов. Так что наполнять массив элементами приходится с помощью других операторов цикла.
10.2. КЛАССЫ CHAR И CHAR[]
Символы в C# представлены в двухбайтовой кодировке (Unicode). По сравнению с числами объявление символьных переменных и использование символьных значений имеет некоторые особенности. Имеется символьный тип сhar, основанный на классе System. Char. Значения этого типа можно задавать явно, используя символьные константы. Константу можно задать:
- символом, заключенным в одинарные кавычки (апострофы); escape-последовательностью, задающей код символа; Unicode-последовательностью, задающей Unicode-код сим-вола.
Ниже приведен пример объявления символьных переменных и некоторые особенности их использования.
// обычный символ
char ch1='A';
// обычная ESC-последовательность – код символа Z
char ch2 ='\x5A';
// Unicode-последовательность – код символа X
ch3='\u0058';
// объявление переменной в объектном стиле в динамической памяти
char ch = new Char(); // ch – переменная ссылочного типа
// присваивание по правилам присваивания значения ссылочному типу
ch = ch1; // значение ‘A’
// неявное преобразование символьного типа в тип int
int code;
code = ch; // в code – значение 65, код символа A (латинское)
// явное преобразование – в ch1 будет значение B (латинское)
ch1=(char) (code + 1);
// преобразование символьного типа в строку
string s;
// s = ch; // это некорректно – разные типы
// преобразование типов – в строке будет значение BZX
s = ch1.ToString() + ch2.ToString() + ch3.ToString();
В рассмотренном примере использовались латинские символы. Символы кириллицы в кодовой таблице Unicode имеют коды со значением более 1039 (значение десятичное). Например, символ кириллицы А имеет код 1040, а символ а – код 1072. Программа, представленная ниже, позволяет отобразить на экране все коды символов кириллицы.
char s = 'А';
for (int i = 0; i < 66; i++)
{
Console. WriteLine("Символ {0} имеет код {1}", s, (int) s);
s = (char) ((int) s + 1);
}
В классе Сhar имеется ряд свойств и статических методов (подробно о статических методах – в разделе 14). Например:
GetNumericValue – получить численное значение цифрового символа;
IsDigital – дает значение true, если символ является десятичной цифрой;
Parse – преобразует символ в строку;
ToUpper – преобразует символ к символу верхнего регистра.
Методы в основном перегружены. Это значит, что они могут применяться к разным случаям представления и использования символа. В частности, метод можно использовать для отдельного символа и символа, включенного в строку.
В следующем несложном примере вычисляется сумма цифр, имеющихся в произвольной введенной строке. Проверяется, является ли текущий символ строки цифрой, и если да, то вычисляется численное значение цифры и прибавляется к сумме.
string s;
double sum;
for (; ; )
{
s = Console. ReadLine();
if (s. Length == 0) break;
sum = 0;
for (int k = 0; k < s. Length;k++)
{
if (Char. IsDigit(s[k])) sum = sum + Char. GetNumericValue(s[k]);
}
Console. WriteLine(sum);
}
Здесь использовано свойство Length класса String, позволяющее получить длину строки. Полностью список методов и свойств класса можно посмотреть в справочной системе среды программирования.
В языке C# определен класс Char[], и его можно использовать для представления строк постоянной длины. Вместе с тем, массив char[] – это обычный массив. Более того, его нельзя инициализировать строкой символов. Константа, задающая строку символов, принадлежит классу String (об этом классе – в следующем разделе), а в C# не определены взаимные преобразования между классами String и Char[], даже явные. В классе String есть, правда, динамический метод ToCharArray, задающий подобное преобразование. Можно также посимвольно передать содержимое строковой переменной в массив символов. Например:
// ошибка: нет преобразования класса string в класс char[]
// char[] strM1 = "Привет волку!";
// а надо так:
string hello = "Привет волку!";
char[] strM1 = hello.ToCharArray();
// вывод на экран посимвольно как массив
for (int i = 0; i < strM1.Length; i++)
Console. Write (strM1 [i]);
Console. WriteLine();
Метод ToCharArray позволяет преобразовать строку в массив символов. К сожалению, обратная операция не определена, поскольку метод ToString, который можно использовать для всех объектов класса Char[], печатает информацию о классе, а не содержимое массива. Но нет проблем выполнить обратное преобразование с помощью цикла:
string result = "";
for(int i = 0; i < strM1.Length; i++) result = result + strM1[i];
11. СТРОКИ
Основным типом при работе со строками в языке C# является тип string, позволяющий задать строки переменной длины. Имеется и класс String. Объекты класса String объявляются как все прочие объекты простых типов, однако значением является не строка, а ссылка на строку, размещенную в динамической памяти.
Примеры объявления строк:
// объявление с инициализацией значением Мир
string world = "Мир";
// строка в куче из пяти повторяющихся символов
string sssss = new string('s',5);
// string s1 = new string("s1"); // так нельзя
// string s2 = new string(); // так тоже нельзя
// а так можно
string s2; // это и есть объявление пустой строки в куче
// преобразование в символьный массив явно заданной строки
char[] yes = "Привет волку".ToCharArray();
// объявление строки с инициализацией символами из массива
string stryes = new string(yes); // в объектном стиле
// объявление с копированием подстроки Привет
string strye = new string(yes, 0, 6); (с нулевого шесть символов)
Над строками определены следующие операции:
· присваивание (=);
· две операции проверки эквивалентности: равно (==) и не равно (!=);
· конкатенация (объединение) или сцепление строк (+);
· взятие индекса ([]).
Операция присваивания имеет важную особенность. Поскольку string – это ссылочный тип, то в результате присваивания создается ссылка на значение, хранимое в куче. С одним и тем же значением в куче может быть связано несколько переменных строкового типа. Но эти переменные не являются псевдонимами, т. е. не являются разными именами одного и того же объекта. Дело в том, что значения строк в куче нельзя изменять, потому что класс String относится к неизменяемым классам. Есть особый класс строк – изменяемый класс StringBuilder, рассмотренный в [1]. Поэтому когда одна из переменных получает новое значение, то для нее создается в куче новый объект (новое значение). Остальные переменные сохраняют свои связи. Для программиста это означает, что семантика присваивания строк аналогична семантике значимого присваивания. Отсюда следует, что в отличие от других ссылочных типов операции, проверяющие эквивалентность (совпадение строк), сравнивают значения строк, а не ссылки. Эти операции выполняются как над значимыми типами.
Бинарная операция "+" сцепляет две строки, приписывая вторую строку к хвосту первой. Возможность взятия индекса при работе со строками отражает тот приятный факт, что строку можно рассматривать как массив и получать без труда каждый ее символ. Каждый символ строки имеет тип char, доступный, правда, только для чтения, но не для изменения. Рассмотрим пример.
string s1 ="ABC", s2 ="CDE";
// новая строка в куче как объединение строк
string s3 = s1 + s2;
// строка s2 получает новое значение в виде ссылки на АВС
s2 = s1; // значение CDE – объект для сборщика мусора
// строка s2 получает новое значение и новое место в куче
s2 = “FGH’; // а s1 сохраняет старое значение и ссылку
// ch1 получает значение А, ch2 – значение F
char ch1 = s1[0], ch2 = s2[0];
// а вот так нельзя – значение строки неизменяемое
// s1[0] = 'L'; s2[1] = ch1;
В C# существуют два вида записи строковых значений:
- обычные значения, которые представляют строку символов, заключенную в кавычки; @-константы, заданные обычным значением c предшествующим знаком @.
В обычных значениях некоторые символы интерпретируются особым образом. Связано это, прежде всего, с необходимостью задавать в строке непечатаемые символы, такие как, например, символ табуляции (отступ). Возникает необходимость задавать символы их кодом – в виде escape-последовательностей. Для всех этих целей используется комбинация символов, начинающаяся символом \ – обратная косая черта (обратный слэш). Так, пары символов: \n, \t, \\, \"" задают соответственно символ перехода на новую строку, символ табуляции, сам символ обратной косой черты, символ кавычки, вставляемый в строку, но не сигнализирующий о ее окончании. Комбинация "\xNNNN" задает символ, определяемый шестнадцатеричным кодом NNNN. Хотя такое решение возникающих проблем совершенно естественно, иногда возникают неудобства: например, при задании констант, определяющих путь к файлу, приходится каждый раз удваивать символ обратной косой черты. Это одна из причин, по которой появились @-константы.
В @-константах все символы трактуются в полном соответствии с их изображением. Поэтому путь к файлу лучше задавать @-константой. Единственная проблема в таких случаях: как задать символ кавычки, чтобы он не воспринимался как конец самой константы. Решением является удвоение символа. Пример:
// Задание имен файла двумя видами констант
s1 = "d:\\study\\primer. doc";
s2 = @"d:\study\primer. doc";
В классе String имеется много различных методов для работы со строками. Ниже перечислены некоторые статические методы класса String:
- Compare – сравнение строк Copy – копирование строки Format – форматирование строки в соответствии с шаблоном Split – разделение строки на части по заданным разделителям Join – объединение строк
Работа с использованием перечисленных методов подробно описана в [1]. В [4] имеется лабораторная работа № 6, позволяющая осуществить практическое знакомство с тремя последними методами.
Кроме статических методов, в классе имеются динамические методы (подробнее о динамических методах – в разделе 14). Один из них – ToCharArray – мы уже видели в разделе 10.2. Перечислим еще пару часто используемых методов:
- Substring – выделение подстроки из строки, Insert – вставка подстроки, начиная с заданной позиции строки.
12. ТИПЫ ДАННЫХ, ОПРЕДЕЛЯЕМЫЕ
ПРОГРАММИСТОМ
В предыдущих разделах мы рассмотрели типы данных, которые изначально поддерживает язык C#. Это различные численные типы, символьные типы, строки. В программе можно создавать и использовать переменные этих типов. Эти переменные являются простыми переменными: в каждый момент времени исполнения каждая такая переменная содержит единственное значение. На основе встроенных типов можно организовывать массивы значений. Переменная-массив позволяет именовать уже не одно, а много значений одного и того же типа. Используя простые переменные и массивы можно писать программы для решения любых задач.
Вместе с тем, язык C# предоставляет программисту возможность создавать новые типы данных. Потребность в создании нового типа может возникнуть тогда, когда программист захочет описать обрабатываемые данные в более целостном, осмысленном виде. Существуют три приема создания новых типов:
- описание перечислений; описание структур; описание классов.
Два первых приема мы рассмотрим в этом разделе. Описание классов будет изложено в разделе 14.
12.1. ПЕРЕЧИСЛЕНИЯ
Перечисление – это список именованных констант. Раз это константы, то в течение всего времени исполнения программы их значения неизменны. Поскольку это список, то констант может быть любое количество (в разумных пределах, конечно). Так как константы именованные, то каждая из них представляет собой некоторое имя, имеющее смысл в использовании и отображении.
Например, таким списком может быть набор из двух значений: мужской и женский. Этими значениями обычно обозначают пол человека (сотрудника, студента, пациента и т. п.). Другой пример – список цветов, применяемый программистом при решении какой-то задачи. Значениями этого списка могут быть цвета радуги (красный, оранжевый, желтый, зеленый, голубой, синий, фиолетовый) или основные цвета, используемые при отображении на экране (Red, Green, Blue). Применять имена констант (значения) намного удобнее, чем пользоваться списком из чисел, где каждое число мы можем мысленно поставить в соответствие некоторому полу или цвету.
Задать перечисление можно с помощью ключевого слова enum:
enum имя_перечисления {список значений}
Здесь имя_перечисления является именем нового типа данных. Например:
enum Pol {мужской, женский}
enum Cvet {красный, оранжевый, желтый, зеленый, голубой, синий, фиолетовый}
enum RGB {Red, Green, Blue }
Мы объявили три новых типа данных. Но если есть тип, то можно объявлять переменные этих типов. Попробуем:
Pol P1, P2, P3;
Cvet C1, C2;
RGB Palitra;
Теперь этим переменным можно присваивать значения:
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 |


