4280

Уровни языков программирования. Язык C#

Конспект

Информатика, кибернетика и программирование

Уровни языков программирования Языки программирования могут быть подразделены на три общих типа: Машинные языки – понимаются компьютером Ассемблерные языки (языки низкого уровня) Языки высокого уровня – удобны для програм...

Русский

2012-11-15

344 KB

48 чел.

Уровни языков программирования

Языки программирования могут быть подразделены на три общих типа:

  •  Машинные языки – понимаются компьютером
  •  Ассемблерные языки (языки низкого уровня)
  •  Языки высокого уровня – удобны для программиста

Для преобразования программ, написанных ассемблерных языках, в машинный язык были разработаны программы-трансляторы, называемые ассемблерами.

Программы для преобразования последовательности операторов на языке высокого уровня в машинный язык называются компиляторами.


Язык C#

Язык С# принадлежит к семейству С-подобных языков.

Язык С является родоначальником этого семейства.

На базе языка С был разработан язык С++, поддерживающий объектно-ориентированную технологию.

С# результат дальнейшей эволюции языка C, созданный специально для использования на платформе .NET.

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

Ввод с клавиатуры -> Консольное приложение -> Вывод на экран

Ни ООП, ни .NET для этого не нужны и можно было бы обойтись средствами обычного С. Тем не менее мы будем использовать язык С# только для того, чтобы сразу привыкать к его синтаксису.

Целью данного курса является изучение общих основ программирования. Поэтому многие особенности и возможности C# не будут рассматриваться в данном курсе

Возможно, наиболее фундаментальное определение компьютерной программы — "последовательность операций над данными".

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

Переменные и константы

Любая программа обрабатывают данные. За обработку данных отвечает центральный процессор компьютера. Внутри процессора имеется управляющее устройство, арифметико-логическое устройство, набор регистров для временного хранения данных, схемы адресации внешнего устройства оперативной памяти и устройств ввода-вывода, а также другие схемы и устройства.

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

Данные элементарных типов (бит и байт) позволяют хранить числа в весьма ограниченном диапазоне значений. С помощью 1 бита можно представить всего два числа — 0 и 1, а с помощью 1 байта — 256 целых чисел в диапазоне от 0 до 255. Комбинируя несколько байтов, можно создавать типы данных, предназначенные для хранения намного большего количества значений.

Чтобы использовать переменные в программе С#, их необходимо объявить, указав имя и тип переменной, например:

byte   x1;

x1 = 4;

Числа без знака

Таблица. Числовые типы данных без знака

Тип

Возможные значения

Описание

byte

От 0 до 255

8-разрядное значение без знака,

занимает 1 байт памяти

ushort

От 0 до 65 535

16-разрядное значение без знака,

занимает 2 байта памяти

uint

От 0 до 4 294 967 295

32-разрядное значение без знака,

занимает 4 байта памяти

ulong

От 0 до 18 446 744 073 709 551 615

64-разрядное значение без знака,

занимает 8 байт памяти

Три последних типа данных начинаются на одну и ту же букву. Это первая буква английского слова unsigned, что означает «беззнаковый».

Числа со знаком

Для того чтобы представлять в памяти не только положительные, но и отрицательные числа, старший разряд байта (или комбинации нескольких расположенных рядом байтов) используют для хранения знака числа. Когда этот разряд сброшен (равен 0), то число имеет положительный знак, а когда установлен (равен 1) — отрицательный. При этом в остальных разрядах хранится абсолютное значение числа.

Таблица. Числовые типы данных со знаком

Тип

Возможные значения

Описание

sbyte

От-128 до 127

8-разрядное значение со знаком, занимает 1 байт памяти

short

От -32 768 до 32 767

16-разрядное значение со знаком, занимает 2 байта памяти

int

От -2 147 483 648 до 2 147 483 647

32-разрядное значение со знаком, занимает 4 байта памяти

long

От -9 223 372 036 854 775 808

до 9 223 372 036 854 775 807

64-разрядное значение со знаком, занимает 8 байт памяти

Числа с плавающей точкой

Экспоненциальная часть литерала отделяется символом е или Е.

float z1;

z1=-3.7E-10;

Таблица 1.4. Числовые типы данных с плавающей точкой

Тип

Возможные значения

Описание

float

От 1.5×10-45 до 3.4×1038

32-разрядное число с плавающей точкой, максимальная точность представления чисел — 7 десятичных цифр

double

От 5.0×10-324 до 1.7×10308

64-разрядное число с плавающей точкой, максимальная точность представления чисел — 16 десятичных цифр

decimal

От 1.0×10-28 до 7.9×1028

128-разрядное число с плавающей точкой, максимальная точность представления чисел — 29 десятичных цифр

Текстовые символы

Для хранения отдельных символов текста в языке С# предусмотрен специальный тип данных char. Для этого типа данных используется современная кодировка UNICODE, поэтому символы char занимают в памяти 2 байта.

char bukva;

bukva=’a’;

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

Логический тип данных

Логические переменные в С# объявляются с помощью ключевого слова bool и могут принимать одно из двух значений — true (истина) или false (ложь).

bool finish;

finish=true;

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

Выражения и операторы С#

Итак, мы познакомились с типами данных, переменными и литералами. Теперь нам нужно научиться инициализировать переменные при помощи литералов, а также изучить основные операторы языка С#.

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

Объекты программы, над которыми выполняются действия, называются операндами. Например, оператор сложения чисел принимает два операнда.

Операторы и операнды формируют выражения.

Инициализация переменных и оператор присваивания

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

byte myByte = 25;

x = у = z = 1;

Математические операторы

Бинарные операторы

Таблица.  Математические операторы

Символ

Оператор

+

Сложение

Вычитание

*

Умножение

/

Деление

%

Вычисление остатка при целочисленном делении

Так как в операциях принимают участие два операнда, эти операции называется бинарными (binary).

Унарные операторы

Символ

Оператор

+

Унарный плюс

Унарный минус

++

Инкремент

 

Декремент

!

Унарное логическое отрицание

Инкремент и декремент

int х = 5;

x++;

По выполняемым результатам унарный оператор + +, использованный во второй строке, эквивалентен следующему бинарному оператору:

х = х + 1;

Аналогично вместо унарного оператора х-- можно использовать бинарный оператор:

х = х - 1;

Унарное логическое отрицание

Оператор унарного логического отрицания вычисляет значение своего логического операнда, а затем изменяет его на противоположное значение. Вот пример использования этого оператора:

bool isBlackColor = true;

bool check = !isBlackColor;

Составные операторы

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

Символ

Оператор

+=

Сложение и присваивание

–=

Вычитание и присваивание

*=

Умножение и присваивание

/=

Деление и присваивание

%=

Вычисление остатка от деления и присваивание

Поразрядные операторы

В языке С# имеются так называемые поразрядные операторы, с помощью которых можно изменять состояние отдельных или всех разрядов значений, хранящихся в переменных.

Логические операторы

Логические операторы предназначены для выполнения операций над данными, объявленными в программе при помощи ключевого слова boo1.

Напомним, что логические переменные могут принимать одно из двух значений — true (истина) или false (ложь). Результатом выполнения логического оператора всегда является логическое значение true или false .

В языке С# имеется три логических оператора:

Символ

Оператор

&&

Логический оператор И

||

Логический оператор ИЛИ

!

Унарное логическое отрицание

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

Операторы отношения

Эти операторы иначе именуются операторами сравнения и хорошо знакомы из курса элементарной математики. Результат выполнения бинарных операторов отношения представляется в виде логических значений true или false.

Символ

Оператор

==

Равно

!=

Не равно

<

Меньше

<=

Меньше или равно

>

Больше

>=

Больше или равно

Операторы отношения позволяют сравнивать значения переменных и выражений между собой. Например, результат вычисления выражения 2 < 4 будет true , а результат вычисления выражения 1 = = 2 будет равен false.

Приоритеты операторов

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

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

int х = 2 + 3 * 4;

Чтобы избежать неоднозначности, все операторы в языке С# (как, впрочем, и в других языках программирования) имеют свой приоритет. Более приоритетные операторы выполняются в первую очередь. В приведенном выше примере оператор умножения имеет высший приоритет по сравнению с оператором сложения, поэтому в результате вычисления получится значение 14.

При необходимости мы можем задать порядок вычислений в составных выражениях подобного рода при помощи скобок, например:

int х = ( 2 + 3 ) * 4;

Таким образом мы сообщаем компилятору, что вначале нужно сложить числа 2 и 3, а затем умножить полученный результат на 4. Для наглядности мы рекомендуем в сложных выражениях всегда задавать порядок выполнения операций явным образом. Это поможет избежать ошибок, связанных с неправильной оценкой приоритетов операторов.

Работа с консолью

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

В реальной жизни (а также при выполнении лабораторных работ) часто необходимо, чтобы программа обрабатывала данные, которые пользователь вводит с консоли (то есть с клавиатуры) и выводить результаты обратно в консоль.

Данные выводятся на консоль при помощи метода WriteLine:

Console.WriteLine("Hello World!");

Console.WriteLine(x);

Console.WriteLine("x = {0}, у = {1}, z = {2}", x, y, z) ;

Здесь использован новый формат вызова метода WriteLine. Теперь этому методу передаются несколько параметров (в данном случае четыре), разделенных запятыми. Первый из них представляет собой строковый литерал, а остальные — числовые переменные, значения которых нас интересуют.

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

Во время работы программы метод WriteLine вначале выведет на консоль строку "x = ", а затем вместо конструкции "{0}" подставит значение второго параметра метода, т. е. содержимое переменной х. После этого строка форматирования просматривается дальше, значения "y = " и "z = " выводятся на экран без изменения, а вместо "{1}" и "{2}" подставляются значения следующих параметров метода, т. е. содержимое переменных y и z соответственно.

Данные считываются с консоли при помощи метода ReadLine

text = Console.ReadLine();

Этот метод может ввести с консоли любую информацию в виде строки символов, включая числовые данные (т.к. каждая цифра числа при вводе выступает как отдельный символ).

Преобразование типов

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

Строка символов, которая вводится после приглашения с помощью метода ReadLine, преобразуется в нужный нам тип данных с помощью метода  ToInt32 класса Convert:

 int a = Convert.ToInt32(Console.ReadLine());

Такая запись (помещение вызова метода ReadLine внутри круглых скобок метода ToInt32) означает, что первым выполнится метод ReadLine, а введенная им строка символов будет затем передана методу ToInt32 для преобразования в число. Число, полученное в результате преобразования, будет присвоено переменной а.

Использование класса Convert дает нам возможность вводить с консоли значения любых типов, так как в этом классе имеются функции преобразования ко всем числовым типам С# и к логическому типу:

Метод

Тип числовых данных

Метод

Тип числовых данных

ToBoolean

bool

ToUIntl6

ushort

ToChar

char

ToUInt32

uint

ToSbyte

sbyte

ToUInt64

ulong

ToByte

byte

ToSingle

float

ToIntl6

short

ToDouble

double

ToInt32

int

ToDecimal

decimal

ToInt64

long

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

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

Оператор явного преобразования типов

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

float х = 1.4 + 5;

В языке С# имеется унарный оператор, позволяющий выполнять явное преобразование типов данных. Он используется следующим образом:

float х = 1.4 + (float)5;

Здесь при помощи конструкции (float) мы указали, что перед сложением необходимо преобразовать числовой литерал 5 в литерал с плавающей точкой типа float.

Вопросы, связанные с преобразованием типов данных в С# имеют достаточно важное значение, поэтому позже мы рассмотрим их более подробно.

Преобразование типов констант

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

Целочисленные литералы

25 0 767 6786867867 1233

Целочисленные литералы без дополнительных суффиксов могут представлять значения типов со знаком или без знака: byte, sbyte, int, uint, long, ulong.

В этом случае литерал соответствует типам byte, uint, ulong:

256u 1U 0U 

Суффиксы l или L применяются для создания литералов типа long и ulong:

2564124739362741 11 0L

Комбинируя в произвольном порядке суффиксы u, U, 1 и L, можно создавать литералы типа ulong:

25UL 145637365UL 76787234711u 7678723471LU 6410193764LU

Перед шестнадцатеричным числом помещается префикс 0х, например:

0xl 0x23BF 0xFFFF 0xFE67BCA0001

Литералы с плавающей точкой

Для обозначения знака числа используются символы + и -.

Вот примеры литералов с плавающей точкой типа float:

3F 6.52f 2el0F 1356.4560f -3.7E-10F

А эти литералы будут иметь тип double:

.25 3.3 6.52d 2el0 1356.4560D -3.7E-10D

Вот несколько примеров литералов типа decimal, применяемых обычно для обозначения денежных сумм:

.25m 3.3М 6.52m 2е10M 13573847420983746.45М


Алгоритмизация и решение задач

Понятие алгоритма

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

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

  •  Словесное описание.
  •  Блок-схема.
  •  Псевдокод.

Словесное описание

Это самая простая и естественная (с точки зрения обычного человека) форма записи алгоритма. В качестве исполнителя таких алгоритмов обычно выступают люди, а не компьютеры. Общая проблема таких алгоритмов – неоднозначность их исполнения. При изучении программирования этот способ, как правило, не используется.

Блок-схема

Блок-схема представляет собой графическое изображение последовательности выполняемых операторов. Она состоит из блоков, соответствующих различным операторам алгоритма, и соединенных линиями, определяющими порядок выполнения.

Блок-схемы могут рисоваться от руки или с помощью программ-редакторов, как предназначенных для создания документов (например, Microsoft Word), так и специально разработанных для создания различных схем (Microsoft Visio). Слева приведен пример блок-схемы, созданной с помощью MS Visio:

Достоинством блок-схемы является ее высокая наглядность. Однако рисование блок-схемы, особенно вручную, достаточно трудоемко, а ее последующая модификация, если это окажется необходимым, может превратиться в настоящую проблему.

Описание Блоков

//

Псевдокод

Псевдокод — это искусственный неформальный язык, который помогает программистам разрабатывать алгоритмы. Псевдокод, который мы здесь представляем, особенно полезен для разработки алгоритмов, которые преобразуются затем в структурные программы на языке С#. Псевдокод напоминает повседневный язык; он удобен и достаточно прост, хотя и не является подлинным языком программирования для компьютера.

Псевдокод является универсальным языком для записи алгоритма. Это означает, что алгоритм, записанный на псевдокоде, может быть потом переведен в программу на любом универсальном языке программирования, таком как Pascal, Visual Basic, C, C++ или C#. Поэтому, разрабатывая алгоритм на псевдокоде, необходимо избегать конструкций, специфических для какого-либо языка программирования. Иными словами, в псевдокоде алгоритма должно быть сформулировано, что именно вы хотите сделать и в каком порядке, а в программе – как именно это делается на выбранном Вами языке программирования.


Реализация алгоритмических конструкций в С#

Следование

Алгоритмическая конструкция «следование» на блок-схеме изображается в виде нескольких блоков, обычно расположенных один под другим, управление которым передается сверху вниз:

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

Начало

Ввод А и В

С = А + В

Вывод С

Конец

Для реализации в языке программирования алгоритмической структуры «следование» никаких специальных ключевых слов не требуется. Любая последовательность операторов, записанных друг за другом, будет выполняться также последовательно в том же самом порядке.

С# — язык, обладающий блочной структурой; другими словами, каждый оператор является частью некоторого блока кода. Эти блоки, для обозначения начала и конца которых используются фигурные скобки { и }, могут содержать произвольное количество операторов или не содержать их вовсе. Обратите внимание, что фигурные скобки не сопровождаются точками с запятой.

Ветвление 

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

Ветвление и условный оператор

Ветвление на блок-схеме алгоритма изображается в двух вариантах:

На псевдокоде первый вариант этой конструкции имеет вид:

Если <Условие> То

Блок Операторов 1

Иначе

Блок Операторов 2

Все-Если

Действует эта конструкция следующим образом. Если <Условие>, записанное после Если, истинно, выполняется <Блок Операторов 1>. В противном случае управление передается <Блоку Операторов 2>.

Второй вариант ветвления используется, если при ложности условия не нужно выполнять никаких действий. В этом случае управление передается блоку, расположенному после завершения <Блока Операторов 1>.

На псевдокоде этот вариант выглядит так:

Если <Условие> То

 Блок Операторов 1

Все-Если

В программе на C# ветвление реализуется с помощью условного оператора if, который в общем виде выглядит следующим образом:

if(<Выражение>)

<Оператор 1>

[else

<Оператор 2>]

Логическое выражение в скобках вычисляется при выполнении оператора и может иметь значения true или false. В зависимости от значения этого выражения выполняется либо <Оператор 1>, либо <Оператор 2>. Оператор if может содержать необязательную конструкцию else. Так как эта конструкция необязательная, то при описании синтаксиса оператора она заключается в квадратные скобки.

Если конструкция else отсутствует и выражение в скобках равно false, то <Оператор 1> просто пропускается и выполняется следующий после него оператор.

Вложенные условия

В любом блоке операторов, используемом в конструкции ветвления, может также использоваться ветвление. В этом случае эти конструкции называются вложенными:

На псевдокоде вложенные условия выглядят так:

Если <Условие 1> То

Блок Операторов 1

Иначе

 Если <Условие 2> То

 Блок Операторов 2

Иначе

 Блок Операторов 3

Все-Если

Все-Если

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

Применение логических операций

В предыдущей программе для определения принадлежности значения переменной inputNumber интервалу ]0, 100] мы использовали следующую конструкцию с логическим оператором И:

if(inputNumber > 0 && inputNumber <= 100)

System.Console.Write("Это число больше 0, но меньше или равно 100");

Аналогичного результата можно было бы добиться при помощи вложенного оператора if:

if(inputNumber > 0)

{

if(inputNumber <= 100)

{

System.Console.Write("Это число больше 0");

System.Console.Write(", но меньше или равно 100");

}

}

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

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

if(((inputNumber > 10) && (inputNumber <= 100)) ||

! ((х > 20) && (х <= 200)))

Здесь тело оператора if будет выполнено в том случае, если число inputNumber принадлежит интервалу ]10, 100] или число х не принадлежит интервалу ]20, 200].

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

Циклы

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

Циклы в блоксхемах

//

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

Алгоритмические структуры «Циклы» реализуется в C# с помощью четырех различных управляющих конструкций. В данном разделе рассказывается об использовании итерационных операторов while, do-while,  for и foreach.

Цикл Пока

Структура повторения «Цикл-Пока» позволяет программисту задать многократное выполнение действия до тех пор, пока некоторое условие остается истинным.

В общем случае в псевдокоде «Цикл-Пока» имеет вид:

Пока <Условие>

<Блок Операторов>

Все-Пока

На блок-схеме структура «Цикл-Пока» изображается следующим образом:

В C# структура «Делать-Пока» реализуется с помощью оператора do-while:

while( <Условие> )

{

<Операторы>

}

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

Цикл Делать-Пока

Структура повторения «Делать-Пока» подобна структуре «Цикл-Пока». В структуре «Цикл-Пока» условие продолжения цикла проверяется в начале цикла до выполнения его тела. Структура «Делать-Пока» проверяет условие продолжения цикла после выполнения тела цикла, поэтому тело цикла будет выполнено, по крайней мере, один раз.

На следующем рисунке представлена блок-схема структуры «Делать-Пока» и ее псевдокод:

Делать

<Блок Операторов>

Пока <Условие>

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

В C# структура «Делать-Пока» реализуется с помощью оператора do-while:

do

{

<Операторы>

}

while( <Условие> );

После завершения цикла do-while выполнение программы продолжается с оператора, следующего за предложением while. Обратите внимание, что при наличии только одного оператора в теле цикла в структуре do-while нет необходимости использовать фигурные скобки. Однако обычно фигурные скобки включаются во избежание путаницы между структурами do-while и while. Например, строка

while( <Условие> )

обычно рассматривается в качестве заголовка структуры while. Структура do-while без фигурных скобок вокруг ее тела, состоящего из одного оператора, выглядит как

do

<Оператор>

while( <Условие> );

Это может приводить к путанице. Последняя строка — while (условие); может быть неправильно истолкована читателем как структура while, содержащая пустой оператор. Поэтому структура do-while с одним оператором часто записывается так:

do

{

<Оператор>

}

while( <Условие> );

Цикл с параметром

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

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

Для К = 1 До N [Шаг 1]

<Операторы>

Все-Для-К

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

В C# цикл с параметром реализуется оператором for, который в общем виде записывается так:

for([Инициализация];[Условие];[Приращение])

<Оператор>

Оператор [Инициализация] выполняется один раз перед началом цикла. Перед каждой итерацией (т. е. перед каждым выполнением тела цикла <Оператор>) проверяется [Условие]. И, наконец, после каждой итерации выполняется оператор [Приращение].

Начальное значение переменной цикла задается в программе до оператора for или в операторе [Инициализация]. Способ  изменения переменной цикла определяется оператором приращения, а сравнение ее текущего значения с предельным значением — в блоке [Условие ].

Таким образом, параметр цикла анализируется перед выполнением тела цикла, а модифицируется после его выполнения.

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

Цикл вперед. Цикл назад.

Прерывание и возобновление цикла

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

На псевдокоде это выглядит так:

Пока <Условие 1>

<Блок Операторов 1>

Если <Условие 2>

 Выход из цикла

Все-Если

[<Блок Операторов 2>]

Все-Пока

На блок-схеме это изображается следующим образом:

В С# во всех видах циклов можно использовать специальные операторы прерывания цикла break и возобновления цикла continue.

С помощью оператора break можно в любой момент прервать выполнение цикла.

Оператор continue позволяет возобновить выполнение цикла с самого начала.

Возобновление цикла в блок-схеме и на псевдокоде выглядит так:

Пока <Условие 1>

[<Блок Операторов 1>]

Если <Условие 2>

 Продолжить цикл

Все-Если

<Блок Операторов 2>

Все-Пока

Оператор continue может также использоваться для пропуска некоторых значений переменной цикла или вводимых и обрабатываемых в цикле данных.

Оператор goto

Не использовать.

Бесконечный цикл с помощью goto:

loop: Console.WriteLine("a");

goto loop;

Особые виды циклов

Бесконечный цикл. Иногда требуется цикл, который не имеет условия завершения. Обычно в таких случаях предусматривается какой-либо способ прерывания цикла где-то в самом его теле; одной из возможностей будет оператор break. Как мы уже рассматривали ранее, можно оформить цикл следующим образом:

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

Console.WriteLine("Таблица умножения");

for (int i = 1; i < 10; i++)

{

for (int j = 1; j <= 10; j++)

{

Console.WriteLine("{0} * {1} = {2}", i, j, i * j);

}

Console.WriteLine();

}

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

for(int i = 0; i < n; i++)

{

//Тело цикла опущено

}

Но чаще «пустым» именуется цикл, у которого все необходимые действия выполняются в внутри скобок после for, а тело как таковое отсутствует:

int i, n = 123;

for (i = 1; i * i < n; i++)

{}

Console.WriteLine("{0} – первое число, квадрат которого больше {1}", i, n);

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

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

int n = 123;

for (int i = 1; i * i < n; i++)

{}

// Ошибка компиляции: Переменная i неизвестна

Console.WriteLine("{0} – первое число, квадрат которого больше {1}", i, n);

Двойные циклы. Нетрудно сделать так, чтобы в цикле существовали две управляющих переменных, которые изменяются синхронно в процессе повторения тела цикла. Приведенный ниже небольшой пример вычисляет сумму произведений 1*10 + 2*9 + 3*8+ 4*7 + 5*6 = 110.

int i, j;

int Sum = 0;

for (i = 1, j = 10; i <= j ; i++, j--)

{

Sum += i * j;

}

Console.WriteLine("Sum = {0}", Sum);

Циклы с альтернативным выходом. Если условие продолжения цикла содержит булево выражение с оператором И, тогда цикл может завершиться, если одно из условий в выражении станет равным false. Обычно необходимо знать, какое это было условие, поэтому непосредственно после цикла мы снова проверяем условия. Например, предположим, что мы хотим ввести 10 чисел, при этом цикл ввода надо завершить либо после ввода отрицательного числа, либо после ввода всех 10 чисел. Такой цикл будет иметь следующий вид:

int i, num = 0;

// Если не присвоить num начальное значение, компилятор даст ошибку!!!!

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

{

num = Convert.ToInt32(Console.ReadLine());

if (num < 0)

{

break;

}

}

if (num < 0)

Console.WriteLine("Отрицательное число {0}", num);

else //Цикл завершился на 10-м повторении, отрицательных чисел не было

Console.WriteLine("Нет отрицательных");


Сложные типы данных – массивы чисел и символов

Одномерные массивы

Массив это последовательность элементов.

Объявление массивов:

int[]    massiv1;

double[] massiv2;

char[]   massiv3;

Пара символов [ ] в этом контексте обозначает массив, перед скобками указывается тип элементов.

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

int[] myIntArray;

myIntArray = new int [5];

Размер может быть константой, переменной или выражением. По умолчанию массив после создания массив заполняется нулями.

Так же при создании можно описать полное содержимое в виде списка констант:

int[] myIntArray;

myIntArray = new int[] {5, 9, 10, 2, 99};

После инициализации можно начинать работу с элементами массива.

Обращение к элементам осуществляется по индексу, т.е. по их позиции в списке. Индекс первого элемента в массиве равен 0. Индекс последнего в таком случае получается равным размеру массива-1.

myIntArray[3]=777;

Присваивание значения 4му по счету элементу массива.

Работа с отдельным элементом массива ведется абсолютно так же как и с обычным целым числом.

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

int [] Numbers = new int [10];

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

{

Console.WriteLine(Numbers[i]);

}

Цикл пройдет 10 раз и выведет элементы от Numbers[0] до Numbers[9]. При этом все эти элементы будут равны 0, т.к массив был заполнен по умолчанию.

Массив можно заполнить значиниями несколькими способами. При небольшом количестве элементов массив можно инициализировать константами или ввести с клавиатуры.

int [] Numbers = new int [10];

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

{

Numbers[i]=Convert.ToInt32(Console.ReadLine());

}

Так же массив можно заполнить случайными числами(в лабе)

Использование оператора foreach

Хотя для циклической обработки массивов можно использовать любые циклические операторы, удобнее всего применять специализированный оператор foreach.

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

int sum = 0;

foreach(int number in Numbers)

{

sum += number;

}

WriteLine("Summa = {0}", sum);

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

Для-Каждого number В Numbers

 sum += number

Все-Для-Каждого

Обратите внимание, что для вычисления суммы достаточно знать только идентификатор массива Numbers, но нет необходимости знать его длину. К сожалению, оператор foreach не позволяет изменять содержимое элементов массива, но для просмотра массива (и, как вы узнаете позже, для просмотра содержимого контейнеров объектов других типов) он очень удобен. Как только вы попытаетесь изменить элемент массива в цикле foreach вы получите сообщение компилятора от ошибке:

Типы задач обработки одномерных числовых массивов

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

Попытаемся выделить основные типы задач, решаемых в рамках данного раздела. Рассмотрим алгоритмы решения элементарных задач, из которых, как правило, состоит решение реальных задач, встречающихся на практике.

Подсчет суммы или произведения элементов

Подсчет количества элементов, отвечающих заданному условию

Поиск элемента в массиве

Поиск последовательности элементов, отвечающих заданному условию

Перестановка элементов в массиве

Сдвиг элементов массива

Удаление и вставка элементов массива

Упорядочение элементов массива

Создание одного массива из другого

Поиск одинаковых и различных элементов массива

Слияние двух массивов в один

Многомерные массивы

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

Двумерный массив объявляется следующим образом:

<базовыйТип>[,] <имя>;

Описания массивов с большим числом размерностей требуют больше запятых.

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

double[,] hillHeight = new double [3,4];

Console.WriteLine(hillHeight[0,0]);

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

double[,] hillHeight = new {{1, 2, 3, 4 }, {2, 3, 4, 5 }, {3, 4, 5, 6}};

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

for (int i = 0; i < 3; i++)        

{

for (int j = 0; j < 10; j++)

 {

 a[i,j]=j;

}

}

Типы задач обработки двумерных числовых массивов

Обработка двумерных массивов по строкам

Обработка двумерных массивов по столбцам

Удаление строк или столбцов двумерного массива

Вставка строк или столбцов в двумерный массив

Квадратные матрицы и их обработка

Невыровненные (ступенчатые) массивы

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

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

Синтаксис, применяемый для объявления массивов массивов, подразумевает использование в объявлении массива нескольких пар квадратных скобок, например:

int[][] sArray;

К сожалению, инициализация подобных массивов оказывается не таким простым делом, как инициализация многомерных массивов.

Можно сначала инициализировать массив, содержащий другие массивы (чтобы избежать путаницы, мы будем называть вложенные массивы подмассивами), а затем, в свою очередь, инициализировать эти подмассивы:

sArray - new int[2][];

sArray [0] = new int[3];

sArray [1] = new int[4];

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

int[][] divisorslTol0 = {new int[] {1},

new int[] {1, 2},

new int[] {1, 3},

new int[] {1, 2, 4},

new int[] {1, 5},

new int[] {1, 2, 3, 6},

new int[] {1, 7},

new int[] {1, 2, 4, 8},

new int[] {1, 3, 9},

new int[] {1, 2, 5, 10}};

Поэтому нам придется проходить не только по самому массиву, но и непосредственно по каждому подмассиву:

foreach (int[] divisors in divisorslTol0)

foreach (int divisor in divisors)

{

Console.WriteLine(divisor);

}

Обработка массива символов

Символьный тип char неплохо подходит для работы с массивами букв. Каждая переменная char в C# занимает 2 байта и определяет один символ. Каждый символ имеет свой код, который определяется используемой кодировкой. В языке С# используется кодировка UNICODE.

Таким образом установливается взаимооднозначная зависимость символов с целыми положительными числами. Это позволяет проводить явное преобразование типа char в целочисленные типы, и наоборот.

char ch=' ';

int n=(int)ch;

int n=32;

char ch=(char)n;

Использование кодов символов позволяет сводить массивы символов к массивам целых чисел.  

В качестве примера приведем пример программы, которая выводит код каждого символа в введенной строке:

static void Main(string[] args)

{

char[] str = new char[80];

int i = 0;

     char bukva;

     bukva = Console.ReadKey().KeyChar;  // Считываем первый символ

     while(bukva != '.')

     {

 str[i] = bukva;

 i++;

// Считываем следующий символ

           bukva = Console.ReadKey().KeyChar;

 }

for (i = 0; i < str.Length; i++)        

{

      Console.WriteLine(str[i] + " = " + (int)str[i]);      

 }

 Console.ReadLine();

}

Ввод символа с консоли осуществляется с помощью метода ReadKey(). К сожалению, просто вызвать этот метод недостаточно, т.к. он возвращает не просто прочитанный с клавиатуры символ, а объект стандартного класса ConsoleKeyInfo. Это сделано разработчиками .NET для обеспечения работы со всеми клавишами, в том числе и функциональными. Нам эта возможность не нужна, достаточно получить код нажатого символа. Для этого нужно воспользоваться свойством KeyChar класса ConsoleKeyInfo:

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

Работа с текстовыми строками

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

Для представления текстовых строк в библиотеке классов Microsoft .NET Framework имеется класс System.String. Вместе с набором операций, методов и индексаторов этот  класс представляет собой мощное средство обработки строк.

Что же касается программ С#, то все операции с текстовыми строками выполняются только при помощи методов, предусмотренных в соответствующих классах библиотеки Microsoft .NET Framework.

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

string s = "abcdefghij";

Мы можем получить доступ к каждому символу строки и напечатать его с помощью такого цикла:

for(int i = 0; i < s.Length; i++)

{

Console.WriteLine("Element {0} = {1}", i, s[i]);

}

Строки не считаются массивами.

s[3] = 'х'; //Неверная попытка заменить букву d на букву х

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

Базовые операции со строками

Строки символов описываются, инициализируются и присваиваются друг другу подобно другим типам данных C#:

string svarl;    //Объявляем строковую переменную

string svar2 = "строка"; //Объявляем и инициализируем строку

svarl = svar2;   //Присваиваем значение строке

svarl = "строка";   //Присваиваем значение строке

svarl = svar2 + "строка"; //Сцепляем две строки

int len = svarl.Length;  //Получаем длину строки

Строковая переменная, если при ее описании не задается начальное значение, инициализируется специальным значением null (т. е. отсутствие значения). Это совсем не то же самое, что нулевая, или пустая строка. Приведенные ниже строки С# приведут к ошибке времени выполнения:

string si;  //Создается строка со значением null

si += "abc"; //Неверно, выполнено не будет!!

Это произойдет потому, что левый операнд в операции сцепления строк не имеет значения. Если, однако, переписать строки следующим образом:

string s2 = "";

s2 += "abc";

то они будут успешно выполнены, и переменная s2 получит в итоге значение "abc".

Методы типа string

Тип string включает целый ряд предопределенных методов. Выборка наиболее полезных методов приведена в форме для класса string. Полную информацию о методах string можно найти в описании класса System.String.

Существуют методы, проверяющие, начинается или заканчивается одна строка другой:

bool s1.StartsWith(string s2); // true, если s1 начинается с s2

bool s1.EndsWith(string s2);   // true, если s1 заканчивается строкой s2

Группа методов поиска с общим именем IndexOf позволяют определить позицию заданного символа внутри строки или одной строки внутри другой (ищется первое вхождение символа или строки, если такого нет, возвращается -1):

int s1.IndexOf(char ch);     //Ищет символ ch внутри s1

int s1.IndexOf(char ch, int pos);    //To же, но начиная с позиции pos

int s1.IndexOf(string s2);     //Ищет строку s2 внутри s1

int s1.IndexOf(string s2, int pos);  //To же, но начиная с позиции pos

Метод Substring позволяет выделить из строки любую ее часть:

string s1.Substring(int pos);  //Извлекает подстроку из s1, начиная с

// заданной позиции до конца строки

string s1.Substring(int pos, int len); //To же, но подстрока берется с 

// заданной позиции и имеет указанную длину

Существуют специальные методы для преобразования регистра символов строки:

string s1.ToLower(); //Копирует s1, преобразуя буквы в строчные

string s1.ToUpper(); //Копирует s1, преобразуя буквы в прописные

Три метода позволяют убрать из строки ненужные пробелы:

string s1.TrimStart(); //Копирует s1, убирая начальные пробелы

string s1.TrimEnd();   //Копирует s1, убирая завершающие пробелы

string s1.Trim();      //Копирует si, убирая начальные и завершающие пробелы

Можно при необходимости также добавить в строку пробелы справа или слева до необходимой длины length:

string s1.PadLeft(int length);  // Добавляет пробелы слева

string s1.PadRight(int length); // Добавляет пробелы справа

Существует также метод, который позволяет разбить строку на слова, ограниченные пробельными символами (пробелами или символами табуляции) и поместить результат в массив строк:

string[] s1.Split(); //Расщепляет s1 на слова, по одному на элемент массива

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

userResponse = userResponse.ToLower();

Форматирование текстовых строк

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

Что касается С#, то сам по себе этот язык не содержит средств форматирования строк. Однако богатейшие возможности такого форматирования предоставляются программисту в рамках библиотеки классов Microsoft .NET Framework. Первые шаги в изучении средств форматирования обычно связаны с использованием метода Console.WriteLine, например, при выводе в консольное окно шестнадцатеричных чисел.

Аналогичное форматирование доступно при формировании текстовых строк методом String.Format, не имеющим никакого отношения к консольному выводу. Это часто необходимо для разработки программ с оконным пользовательским интерфейсом, т.к. там, как правило, необходимо перед выводом преобразовывать числовые значения в текстовые строки. Эта задача и решается при помощи метода String.Format. Полное и детальное рассмотрение возможностей форматирования различных типов данных выходит за рамки данного курса, но основные приемы и методы форматирования чисел достаточно просто могут быть продемонстрированы на примере целых чисел.

Чтобы преобразовать целочисленное значение в текстовую строку с форматированием при помощи метода String.Format, необходимо задать этому методу так называемую строку формата (format string), а также передать в качестве параметров одно или несколько преобразуемых значений. В ответ данный метод возвратит отформатированную строку.

Строка формата задается методу String.Format в следующем виде:

{N[,М][:formatString]}

Здесь число N задает номер преобразуемого аргумента, передаваемого методу String.Format в качестве параметра. Необязательное число М задает ширину области текстовой строки (в символах), внутри которой необходимо поместить цифры преобразуемого значения. Если это число отрицательное, цифры числа выравниваются по левой границе данной области, а если положительное — по правой границе области.

И, наконец, строка formatString задает коды форматирования, которые существуют для каждого типа данных. Для форматирования целых чисел используются эквивалентные коды форматирования d и D, для вывода в шестнадцатеричной кодировке – коды х и Х.

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

using System;

namespace StringFormat

{

   class Program

   {

       static void Main(string[] args)

       {

           int iSignedNumber = 777;

           string result;

           result = String.Format("{0}", iSignedNumber);

           Console.WriteLine(result);

           result = String.Format("{0:x}", 0x23fabc);

           Console.WriteLine(result);

           result = String.Format("{0:X}", 0x23fabc);

           Console.WriteLine(result);

           result = String.Format("{0:d2}", iSignedNumber);

           Console.WriteLine(result);

           

           result = String.Format("{0:d8}", iSignedNumber);

           Console.WriteLine(result);

           result = String.Format("{0,5:d}", iSignedNumber);

           Console.WriteLine(result);

           result = String.Format("{0,-5:d}Этo счастливое число",

                         iSignedNumber);

           Console.WriteLine(result);

           Console.ReadLine();

       }

   }

}

Результат работы этой программы:

Функции

.

Описание и использование функций

.

Возвращаемые значения

Если функция должна обладать возвращаемым значением, то необходимо внести два изменения:

  •  В описании функции вместо ключевого слова void указать тип возвращаемого значения.
  •  По завершении всех вычислений в функции использовать ключевое слово return и передать возвращаемое значение вызывающей функции.

Синтаксис кода для рассматриваемого типа функций консольного приложения будет выглядеть следующим образом:

static <возвращаемыйТип> <имяФункции>()

{

. . .

return <возвращаемоеЗначение>;

}

Передача параметров

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

Массивы параметров

.

Передача параметров по ссылке и по значению

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

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

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

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

static void showDouble(ref int val)

     {

           val *= 2;

           Console.WriteLine("Удвоенное значение val = {0}", val);

     }

Вторично ключевое слово ref необходимо использовать при вызове функции (это обязательное требование, поскольку тот факт, что параметр передается по ссылке, является неотъемлемой частью описания этого параметра):

int myNumber = 5;

Console.WriteLine("До вызова функции myNumber = {0}", myNumber);

showDouble(ref myNumber);

Console.WriteLine("После вызова функции myNumber = {0}", myNumber);

В этом случае на консоль будет выведен следующий текст:

Выходные параметры

В дополнение к возможности передавать параметры по ссылке мы можем указать, что данный параметр является выходным: в описание такого параметра включается ключевое слово out, используемое так же, как и ключевое слово ref (в качестве модификатора параметра в описании функции и при вызове функции).

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

Имеются, однако, и существенные отличия. В то время как использовать переменную, которой не присвоено начальное значение, в качестве параметра типа ref недопустимо, она может применяться в качестве параметра типа out. Более того, параметр типа out будет рассматриваться как не имеющий начального значения самой функцией, в которой он используется. Это означает, что хотя передача переменной, которой присвоено некоторое значение, в качестве параметра типа out является допустимой, однако в процессе выполнения функции хранящееся в этой переменной значение будет утрачено.

Область действия переменных

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

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

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

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

Область действия переменных и управляющие конструкции

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

В качестве заключительного замечания необходимо упомянуть о "наилучшей практике". Вообще говоря, лучше всего объявлять и инициализировать переменные перед теми блоками кода, в которых они используются. Исключением из этого правила могут быть переменные циклов, объявление которых является составной частью самого цикла. Например:

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

{

. . .

}

Рекурсия

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

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

using System;

namespace Factorial

{

   class Program

   {

       static void Main(string[] args)

       {

           for (int i = 1; i < 10; i++)

           {

               Console.WriteLine("Факториал числа {0} = {1}",

i, Factorial(i));

           }

           Console.ReadLine();

       }

       static int Factorial(int x)

       {

           if(x == 1)

               return  1;

           else

               return x * Factorial(x - 1);

       }

   }

}

Рассмотрим практический пример. Реализуем перечислимый тип orientation для указания направления на стороны света:

using System;

namespace Orientation

{

   class Program

   {

       enum orientation : byte

       {

           north = 1,

           south = 2,

           east  = 3,

           west  = 4

       }

       static void Main(string[] args)

       {

           orientation myDirection = orientation.north;

           Console.WriteLine("myDirection = {0}", myDirection);

           Console.ReadLine();

       }

   }

}

Структуры

Еще одним важным типом переменной, который широко используется в C#, является struct (сокращение от слова structure — "структура"). Структуры вполне соответствуют своему названию: это структуры данных, которые составлены из информации различного характера, возможно, из данных различных типов. Они позволяют программистам описывать свои собственные типы переменных, для которых данная структура является базовой.

       struct point

       {

           public double x;

           public double y;

       }

Работа с текстовыми файлами

Текстовые файлы содержат символьную информацию, но, как и двоичные, тоже состоят из байтов. Поэтому потоки FileStream, BinaryWriter и BinaryReader можно использовать для записи в файлы и чтения из файлов текстовых строк, но лучше применять специально предназначенные для этого средства. Речь идет о потоках классов StreamWriter и StreamReader. Эти потоки чрезвычайно просты в использовании и удобны для работы с текстовыми файлами.

Основные приемы использования потоков StreamWriter и StreamReader демонстрируются в программе, исходный текст которой приведен ниже:

using System;

using System.IO;

namespace TextFile

{

   class Program

   {

       static string testFile = "mydata.txt";

       static void Main(string[] args)

       {

           if (File.Exists(testFile))

           {

               Console.WriteLine("Файл {0} уже существует", testFile);

               Console.ReadLine();

               return;

           }

           StreamWriter sw = File.CreateText(testFile);

           sw.WriteLine("Каждый охотник желает знать, где сидит фазан!");

           sw.WriteLine("Число \"Пи\" равно примерно {0}.", 3.1415926);

           sw.Close();

           StreamReader sr = File.OpenText(testFile);

           while (true)

           {

               string str = sr.ReadLine();

               if (str == null)

                   break;

               Console.WriteLine(str);

           }

           sr.Close();

           Console.WriteLine("Файл успешно создан");

           Console.ReadLine();

       }

   }

}

Работа с двоичными файлами

Программа Binary демонстрирует некоторые приемы работы с двоичными файлами при помощи классов FileStream, BinaryWriter и BinaryReader.

using System;

using System.IO;

namespace Binary

{

   class Program

   {

       static string testFile = "mydata.dat";

       static void Main(string[] args)

       {

           if(File.Exists(testFile))

           {

               Console.WriteLine("Файл {0} уже существует", testFile);

               Console.ReadLine();

               return;

           }

           FileStream fs = new FileStream(testFile, FileMode.CreateNew);

           BinaryWriter bw = new BinaryWriter(fs);

           bw.Write("Text string");

           for(uint i = 0; i < 20; i++)

           {

               bw.Write(i);

           }

           bw.Close();

           fs.Close();

           fs = new FileStream(testFile, FileMode.Open, FileAccess.Read);

           BinaryReader br = new BinaryReader(fs);

           string s = br.ReadString();

           Console.WriteLine(s);

           for (uint i = 0; i < 20; i++)

           {

               Console.Write("{0} ", br.ReadUInt32());

           }

           br.Close();

           fs.Close();

           Console.WriteLine("\nФайл успешно создан");

           Console.ReadLine();

       }

   }

}


Начало

Ввод А и В

С = А + В

Вывод С

Конец

Условие

Блок операторов 1

Блок операторов 2

Да

Нет

Условие

Блок операторов 1

Да

Нет

Условие 2

Блок операторов 2

Блок операторов 3

Да

Нет

Условие 1

Блок операторов 1

Да

Нет

Блок операторов

Условие

Нет

Да

Блок операторов

Условие

Нет

Да

К = 1

К < N

Операторы

К = К + 1

Нет

Да

Блок операторов

Условие 1 

Нет

Да

Условие 2 

Да

Нет

Блок операторов

Условие 1 

Нет

Да

Условие 2 

Нет

Да


 

А также другие работы, которые могут Вас заинтересовать

15153. Мнимое и подлинное безумие Чацкого 13.35 KB
  Мнимое и подлинное безумие Чацкого Комедия Горе от ума была написана в 1823 году А.С.Грибоедовым и имела уже тогда огромный успех в читательских кругах не только Москвы но и всей России. При жизни автора Горе от ума не была опубликовано зато многократно переписыва...
15154. Общество в жизни Татьяны, Онегина и автора 16.29 KB
  Общество в жизни Татьяны Онегина и автора Татьяна простая провинциальная девушка она не красавица но задумчивость и мечтательность выделяют ее среди других людей в обществе которых она чувствует себя одиноко так как они не способны понять ее. Дика печальна...
15155. Один в поле воин, если он-Чацкий 21.21 KB
  Один в поле воин если онЧацкий Образ главного героя комедии сочетает в себе все черты идеального человека: высоко развитое чувство собственного достоинства истинная культура и просвещенность нежелание мириться с несправедливым общественным устоем нена
15156. Основные мотивы лирики А.С. Пушкина 34.62 KB
  Основные мотивы лирики А. С. Пушкина Читая лирику А. С. Пушкина великий русский писатель Н. В. Гоголь задался вопросом: €œЧто же стало предметом поэзии А. С. Пушкина€ И сам отвечал: €œВсе стало предметом€. В своем творчестве поэт обращался к темам любви и дружбы его ...
15157. ГОСУДАРСТВО И ФУНКЦИИ ГОСУДАРСТВА 21.09 KB
  государство и Функции государства Государство особая организация общества объединённого общими социальными культурными интересами занимающая определённую территорию имеющая собственную систему управления сис
15158. День Святой Троицы 19.87 KB
  День Святой Троицы День Святой Троицы Пятидесятница Сошествие Святого Духа один из главных христианских праздников входящий в православии в число двунадесятых праздников. Православная церковь отмечает Троицу на 49 день после Пасх
15159. Камчатка 14.18 KB
  Камчатка полуостров на северовостоке Азии Россия. Омывается на З. Охотским морем на В. Тихим океаном и Беринговым морем. Дл. 1200 км шир. до 450 км площадь 370 тыс. км. Перешейком Парапольский Дол соединяется с материком. Зап. берег изрезан слабо на вост. берегу большие зали
15160. Культура России в конце XIX - начале XX века 22.77 KB
  Культура России в конце XIX начале XX века Конец XIX начало XX в. стали чрезвычайно плодотворным периодом в развитии отечественной ку...
15161. Западный фронт Первой мировой войны 58.13 KB
  Западный фронт Первой мировой войны Первая мировая война 28 июля 1914 11 ноября 1918 один из самых широкомасштабных вооружённых конфликтов в истории человечества. Это название утвердилось в историографии только после начала...