Logo GenDocs.ru


Поиск по сайту:  


Лекции по информатике - файл all lec.doc


Лекции по информатике
скачать (398.8 kb.)

Доступные файлы (1):

all lec.doc1495kb.21.01.2007 21:18скачать

содержание

all lec.doc

1   ...   4   5   6   7   8   9   10   11   ...   14
Реклама MarketGid:
^

Обработка данных. Операторы


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

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

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

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

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

^

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


При математических вычислениях используются следующие арифметические операторы:
Оператор Функция оператора

+ сложение

– вычитание

* умножение

/ деление

% получение остатка от деления нацело
Операторы часто используются в инструкциях, требующих выполнения математических операций и присваивания полученного результата переменной в качестве значения. Ниже приводится пример расчета накладных расходов с помощью арифметических операторов. В этом примере не имеет смысла присваивать переменной точное значение в виде литерала, например:
sales_tax = 4500;
напротив, величину накладных расходов требуется вычислить. При записи математических операций имя переменной всегда помещают слева от знака «равно», а арифметические операторы— справа:
sales_tax = amount * tax_rate;

price = cost + shipping + insurance;

per_unit = total / count;
Эти инструкции говорят компилятору, что следует выполнить три операции:

  • присвоить переменной sales_tax значение, полученное в результате умножения значения переменной amount на значение переменной tax_rate;

  • присвоить переменной price значение, полученное из суммы значений трех переменных: cost, shipping и insurance;

  • присвоить переменной per_unit значение, полученное в результате деления total на count.

Компьютер выполнит математические операции, указанные в правой части, а потом присвоит полученное значение переменной в левой части. Что касается правой части выражения, то она может содержать любые комбинации переменных, констант и литералов, например:
sales_tax = amount * 0.06;

price = 56.90 + shipping + 7.87;

per_unit = 156.65 / 12.50;
Оператор % используется для расчета остатка от деления нацело. Если вы используете оператор деления (/) для целочисленных данных, то результат деления тоже всегда будет целым числом. Например, при делении 12 на 5 (12/5) вы получите 2, а не 2.4. Дробная часть, равная в данном случае 0.4, при делении целых чисел отбрасывается.

Разумеется, нередко возникает необходимость узнать значение остатка от деления. Пока мы имеем дело с целыми числами, мы не можем использовать значение 0.4, так как это число относится к типу float. Мы же определили результат деления как целое. В этом случае получается, что число 12 состоит из двух чисел 5, а лишняя двойка просто игнорируется. Число 2 в данном случае является остатком от деления нацело, для получения которого и используется оператор %. Остаток от деления нацело также всегда является целым числом.
^

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


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

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

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

result =(2+3)* (3+4);

C++ вычисляет данное выражение в следующем порядке:

result = (2 + 3) * (3 + 4);
= (5) * (3 + 4);
= 5 * (7);
=5*7;
= 35;

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

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

знак-унарной-операции операнд.

Бинарное выражения состоит из двух операндов, разделенных знаком бинарной операции:

операнд1 знак-бинарной-операции операнд2.

Тернарное выражение состоит из трех операндов, разделенных знаками тернарной операции (?) и (:), и имеет формат:

операнд1 ? операнд2 : операнд3 .

По количеству операндов, участвующих в операции, операции подразделяются на унарные, бинарные и тернарные.

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

- арифметическое отрицание (отрицание и дополнение);

побитовое логическое отрицание (дополнение);

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

* разадресация (косвенная адресация);

& вычисление адреса;

+ унарный плюс;

++ увеличение (инкремент);

-- уменьшение (декремент);

sizeof размер.

Унарные операции выполняются справа налево.

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

В отличие от унарных, бинарные операции, список которых приведен в табл, выполняются слева направо.
Таблица

^ Знак операции

Операция

Группа операций

*

Умножение

Мультипликативные

/

Деление

%

Остаток от деления

+

Сложение

Аддитивные

-

Вычитание

<<

Сдвиг влево

Операции сдвига

>>

Сдвиг вправо

<

Меньше

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

<=

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

>=

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

==

Равно

!=

Не равно

&

Поразрядное И

Поразрядные операции

|

Поразрядное ИЛИ

^

Поразрядное исключающее ИЛИ

&&

Логическое И

Логические операции

||

Логическое ИЛИ

,

Последовательное вычисление

Последовательного вычисления

=

Присваивание

Операции присваивания

*=

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

/=

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

%=

Остаток от деления с присваиванием

-=

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

+=

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

<<=

Сдвиг влево с присваиванием

>>=

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

&=

Поразрядное И с присваиванием

|=

Поразрядное ИЛИ с присваиванием

^=

Поразрядное исключающее ИЛИ с присваиванием

Левый операнд операции присваивания должен быть выражением, ссылающимся на область памяти (но не объектом, объявленным с ключевым словом const).
^

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


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

Счетчики

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


variable = variable + 1
Компьютер сначала вычислит значение в правой части, а потом присвоит полученное значение переменной в левой части. Таким образом, одна и та же переменная никогда не будет иметь два значения одновременно. С точки зрения компьютера, смысл выражения можно передать так:
Новое значение переменной равно старому значению плюс 1
Давайте проследим за работой алгоритма счетчика. У нас есть переменная count, которой присвоено начальное значение, равное нулю:
int count;

count=0;
Теперь вступает в действие алгоритм
count = count + 1;
Компьютер выполняет эту инструкцию так:
count = 0 + 1
К начальному значению переменной count, которое равно 0, добавлен литерал, имеющий значение 1. В результате вычислений получено значение 1, которое теперь присваивается переменной count. Значение переменной изменяется с 0 на1. Затем та же процедура повторяется снова:
count = count + 1;
С каждым новым выполнением этой операции, значение переменной count возрастает на единицу (инкремент). Разумеется, можно присвоить переменной любое начальное значение и увеличивать его на любое отличное от единицы число.

Чтобы считать в сторону уменьшения, достаточно слегка изменить алгоритм:
variable = variable – 1
Теперь при каждом выполнении операции значение переменной будет уменьшаться на единицу (декремент).

Счетчики используют настолько часто, что в языке Си существуют специальные операторы инкремента и декремента переменной. Оператор ++variable увеличивает значение переменной на единицу еще до выполнения соответствующей инструкции. Оператор выполняет то же действие, что и инструкция
variable = variable + 1;


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

Необходимо помнить, что оператор инкремента реально изменяет значение переменной. Проверьте, понимаете ли вы разницу между оператором инкремента ++count и выражением, приведенным в следующей строке:
printf("Второе значение переменной

count равно %d\n", count+1);
Выражение count+1 не изменяет значения, присвоенного переменной count. В результате выполнения этой инструкции значение переменной, увеличенное на единицу, только отображается на экране, но не заносится в память.

Оператор инкремента можно использовать так же, как и выражение, значение которого будет присвоено переменной, стоящей в левой части инструкции присваивания. Например, следующая инструкция увеличивает значение переменной count, а затем присваивает полученное значение переменной number:
number = ++count;
те же операции можно было выполнить и так:
count = count + 1;

number = count;
Оператор ++ можно использовать с именем переменной как инструкцию:
++number;
Если же знаки ++ помещены справа от имени переменной,
variable++;
то приращение значения переменной произойдет после завершения соответствующей инструкции.

Оператор декремента работает аналогичным образом, но уменьшает значение переменной на единицу. Синтаксис использования оператора таков:
--variable
уменьшает значение переменной на 1 до выполнения инструкции
variable—
уменьшает значение переменной на 1 после выполнения инструкции.

Аккумуляторы


Аккумулятор также увеличивает значение переменной. Но, в отличие от счетчика, который всегда увеличивает значение переменной на одну и ту же величину, аккумулятор может иметь произвольный шаг (и способ) изменения при каждой новой операции. В общем виде синтаксис аккумулятора таков:
variable = variable + other_variable;


Аккумулятор получил такое название оттого, что он накапливает значение переменной.

Посмотрите на следующий пример:
int total, number;

total = 0;

scanf("%d", &number);

total = total + number;
Допустим, что переменной number присвоено значение 10. После выполнения инструкции
total=total+number;
переменная total приобретает значение 10, так как компьютер выполнил операцию сложения, используя следующие значения:
total=0+10;
Теперь предположим, что снова происходит ввод данных с помощью функции scanf() и выполняется новая операция суммирования, но на этот раз переменной number присвоено значение 15:
scanf("%d", &number);

total = total + number;
теперь компьютер использует в вычислениях следующие значения:
total = 10 + 15;
Произошло накопление значений переменной number.

^ Операторы присваивания

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


Оператор

Пример

Эквивалент

+=

total += amount

total = total + amount

–=

total –= discount

total = total – discount

*=

total *= tax_rate

total = total * tax_rate

/=

total /= count

total = total / count

%=

total %=count

total = total % count


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

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

Преобразования типов данных


Разнообразие типов данных в языке C++ дает программисту возможность выбирать вариант, соответствующий конкретной потребности. Однако такое разнообразие, с дру­гой стороны, усложняет задачу компьютера. Например, сложение двух чисел типа short может выполняться с помощью иных машинных команд, чем сложение двух чисел типа long.

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

Можно использовать и данные разных типов в правой и левой частях выражения. Отображаемое на экране значение будет определяться в зависимости от типа переменной в левой части выражения. Это показано в следующем примере:
main()

{

int total;

float cost, shipping;

cost = 56.09;

shipping = 4.98;

total = cost + shipping;

printf("Общая стоимость составляет

сумму %d долларов", total);

}
В операции сложения участвуют две переменные типа float (cost и shipping), но полученный результат присваивается целочисленной переменной total. Если сложить эти числа на калькуляторе, то в результате получим 61.07, но так как переменная total— целочисленная, то и результат будет преобразован в целое число. Использование указателя формата %d задает отображение на экране целого числа 61. Обратите внимание, вначале выполняется математическое действие, а затем происходит присваивание значения. Если бы заданные значения преобразовывались в целые числа до их сложения, то результат оказался бы равен 60 (56+4).

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

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

Рассмотрим общие арифметические преобразования.

1. Операнды типа float преобразуются к типу double.

2. Если один операнд long double, то второй преобразуется к этому же типу.

3. Если один операнд double, то второй также преобразуется к типу double.

4. Любые операнды типа char и short преобразуются к типу int.

5. Любые операнды unsigned char или unsigned short преобразуются к типу unsigned int.

6. Если один операнд типа unsigned long, то второй преобразуется к типу unsigned long.

7. Если один операнд типа long, то второй преобразуется к типу long.

8. Если один операнд типа unsigned int, то второй операнд преобразуется к этому же типу.

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


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

Возможные проблемы

Данные с плавающей точкой большей размерности в данные с плавающей точкой меньшей размерности

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

Данные с плавающей точкой

в целочисленные данные

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

неопределенным

Целочисленные данные большей размерности в целочисленные данные меньшей размерности, например, тип long в тип short

Исходное значение может не укладываться в

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


^

Функции языка C++


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

Декларации и дефиниции функций


С использованием функций в языке СИ связаны три понятия - определение функции (описание действий, выполняемых функцией), объявление функции (задание формы обращения к функции) и вызов функции.

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

{ тело_функции;

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

Обратите внимание! Тело функции всегда заключено в фигурные скобки – блок.
В языке СИ нет требования, чтобы определение функции обязательно предшествовало ее вызову. Определения используемых функций могут следовать за определением функции main, перед ним, или находится в другом файле.

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

Например, обе декларации идентичны:
void my_func (void);

void my_func ();
Функция имеет имя my_func, не имеет формальных параметров и ничего не возвращает.
Замечание! Если декларация функции записана следующим образом:

my_func (void);

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

Если объявление функции не задано, то по умолчанию строится прототип функции на основе анализа первой ссылки на функцию, будь то вызов функции или определение. Однако такой прототип не всегда согласуется с последующим определением или вызовом функции. Рекомендуется всегда задавать прототип функции. Это позволит компилятору либо выдавать диагностические сообщения, при неправильном использовании функции, либо корректным образом регулировать несоответствие аргументов устанавливаемое при выполнении программы.
Дефиниция функции – это полная запись функции с заголовком (но БЕЗ точки с запятой!) и телом.

^

Формальные и фактические параметры. Вызов функций


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

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

В наших примерах функций my_func и pause пока нет ни формальных, ни фактических параметров. Вызовы таких функций имеют вид:
my_func();

pause();
Обратите внимание! Служебное слово типа данных здесь НЕ пишется НИКОГДА!

В простейших случаях количество и тип фактических параметров должен совпадать с количеством и типом формальных.
^

Возврат функцией значений


Очень часто от функции требуется, чтобы она не только выполняла определенную работу (например, вывод сообщения с помощью printf), но и возвращала результат вычислений в вызывающую функцию. Для обеспечения возврата результата в вызывающий код в функции должен использоваться оператор return, который имеет следующий формат:
return(result);
Тип функции определяет тип возвращаемого ею значения (int,float, char и т.д.). Например, если функция возвращает значение типа int, то имени функции должно предшествовать то же самое имя типа:
int some_function(int value)

{

// Оператор функции

}
Следующая функция i_cube возвращает куб от целого значения, определенного в качестве параметра. Например, если в функцию передается 5, то i_cube будет возвращать значение равное 5*5*5 или 125:
int i_cube (int value)

{

return(value * value * value);

}
Как можно видеть, для возврата результата вычислений в вызывающий код функцией используется оператор return. В вызывающей функции возвращаемое функцией значение может быть присвоено переменной или использовано при вызове некоторой другой функции (такой кик printf) следующим образом:
result = i_cube(5);
printf("Kyб от 5 равен %d\n", i_cube(5));
Если функция должна возвращать значение другого типа (float, double, char и т.д.), то этот тип необходимо задавать при объявлении функции. В следующей программе функция average_value используется для вычисления среднего арифметического трех значений типа int. При этом значение, которое возвращается функцией, имеет тип float:
#include <stdio.h>
float average_value(int a, int b, int c)

{

return ((a + b + с) / 3.0);

}
void main(void)

{

printf("Среднее от 100, 133 и 155 равно %f\n",

average_value(100, 133, 155));

}
Как можно видеть, тип возвращаемого функцией значения определяется в заголовке функции:
float average_value(int a, int b, int с)
Примечание: Если компилятору не сообщается тип возвращаемого функцией значения, то в качестве такового по умолчанию считается int.

Встречая оператор return, Си немедленно завершает выполнение функции, возвращая указанное значение в вызывающую функцию. Любые операторы, следующие в функции за оператором return, игнорируются. Выполнение программы продолжается с оператора, следующего за оператором вызова данной функции.
По мере увеличения количества созданных функций, вероятно, встретятся функции, которые не должны возвращать значения. Если функция не должна возвращать значение, следует объявить ее типа void следующим образом:


void my_function (int age, char *name);
Если в дальнейшем в программе будет предпринята попытка использовать значение функции

result = my_function (32, "Jamsa");
то компилятор выдаст ошибку.
^

Переменные в функциях


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

Автоматические (локальные) переменные


Некоторые функции нуждаются в собственных переменных и константах.

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

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

Например, в следующей функции use_abc объявляется три локальные переменные a, b и с:
void use_abc(void)

{

int a, b, с;
а=3;

b=а+1;

с = а + b;
printf("a содержит %d b содержит %d с содержит %d\n ", а, b, с);

}

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

^

Внешние (глобальные) переменные


Внешние переменные — это переменные, которые может использовать любая функция программы. В некоторых языках они носят название глобальных переменных. Для того чтобы создать внешнюю переменную, ее следует определить перед функцией main():
int temp;

main()
Здесь переменная temp определена как внешняя, так что с ней могут работать все функции, содержащиеся в программе.
main()

{

printf("Введите значение температуры: ");

scanf("%d", &temp);

convert();

freeze();

boil();

}

convert()

{

float celsius;

celsius = (5.0 / 9.0) * (temp - 32);

printf("%d градусов по шкале Фаренгейта

соответствует %6.2f градусам \

по шкале Цельсия\n", temp, celsius);

return(0);

}

freeze()

{

printf("Это составляет %d градусов

от точки замерзания воды\n", temp-32);

return(0);

}
В действительности, в зависимости от выбора места определения глобальной переменной, можно контролировать набор функций, имеющих доступ к этой переменной. Другими словами, можно управлять областью действия глобальных переменных. При определении в программе глобальной переменной эта переменная может использоваться в функциях, определения которых располагаются после объявления этой переменной и до конца исходного файла. Функции, определения которых располагаются до определения глобальной переменной, не имеют доступа к этой переменной. Например, рассмотрим следующую программу, в которой определяется глобальная переменная title:
#include <stdio.h>
void unknown_title(void)

{

printf("Название книги %s\n ", title);

}

char title[] = "1001 совет по C/C++";
void main(void)

{

printf("Название: %s\n", title);

}

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

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

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

Использование автоматических переменных и передача значений в качестве аргументов позволит вам более успешно управлять ходом программы. Значение автоматической переменной может изменить только функция, в которой она определена. Если результат работы программы оказался неправильным, вам достаточно ввести дополнительные функции printf() для отображения значения каждой локальной переменной.
^

Статические переменные


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

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

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

{

static int count;

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

^

Передача параметров по значению


Когда параметр передается в функцию, по умолчанию используется техника, известная как передача параметра по значению, при которой функция обеспечивается копией значения параметра. При передаче параметра по значению любые изменения, выполняемые в функции над значением данного параметра, существуют только внутри самой функции. Когда функция завершается, значение переменной, переданной по значению, оказывается не измененным. Например, в следующей программе функции display_and_change передаются три параметра (переменные a, b и с):
#include <stdio.h>
void display_and_change(int first, int second, int third)

{

printf("Начальные значения функции %d %d %d\n",

first, second, third);
first += 100;

second +=100;

third += 100;
printf("Конечные значения функции %d %d %d\n",

first, second, third);

}
void main(void)

{

int a=l, b=2, с = 3;
display_and_change(a, b, c);
printf("Конечные значения main %d %d %d\n", a, b ,c);

}
После компиляции и выполнения программы на экран выводится:
C:\> NOCHANGE <ЕNTER>

Начальные значения функции 123

Конечные значения функции 101 102 103

Конечные значения main 123
Как можно видеть, изменения, которые выполняются в функции для переменных, действенны только в самой функции. После завершения функции значения переменных в main остаются неизмененными.

При передаче параметров в функцию их значения размещаются в стеке. Для случая переменных a, b и с в стеке будут содержаться значения 1,2 и 3 соответственно. Любые изменения, выполняемые в функции над значениями параметров, в действительности изменяют значения ячеек стека.

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



^

Передача параметров по ссылке


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

Для того чтобы функция могла возвращать модифицированное значение своего параметра, при вызове такой функции должен использоваться способ, называемый передача параметра по ссылке. Различие между передачей параметра по значению и по ссылке заключается в том, что в первом случае функция получает копию значения параметра, а во втором - адрес значения переменной. Таким образом, при получении параметра по ссылке функция может производить изменения значения, сохраненного по данному адресу, и эти изменения остаются в силе и после завершения функции. Для организации передачи параметров функции по ссылке в программе должны использоваться указатели. Подробно этот тип переменных обсуждается в разделе "Массивы, указатели и структуры". Здесь же будем рассматривать указатель просто как адрес памяти. Для присваивания указателю адреса переменной используется операция получения адреса (&). Для доступа к значению, расположенному по месту памяти, задаваемому указателем, применяется операция "звездочка" (*).
int x;

int &y=x;
Идентификатор y назначает другое, альтернативное имя ячейке, названной x. Если прибавить к значению переменной x некоторое число, то оно будет прибавлено и к переменной y.
#include <iostream.h>

#include <conio.h>

void summa(int &number1, int &number2)

{

number1++;

number2++;

}

void main()

{

int a=10;

int b=15;

cout<<"Before upd "<<a<<" "<<b;

summa(a,b);

cout<<"\nAfter upd "<<a<<" "<<b;

getch();

}
Результат

Before upd 10 15

After upd 11 16
Так как number1 и a – это одна и та же ячейка, то изменение number1 автоматически приведет к изменению переменной a (аналогично number2 и b).

^

Значения параметров по умолчанию


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

Обеспечить значения по умолчанию для параметров функции очень легко. Вы просто присваиваете значение параметру с помощью оператора присваивания С++ прямо при объявлении функции, как показано ниже:
void some_function(int size=12, float cost=19.95) //--->Значения по умолчанию

{
   // Операторы функции
}
Следующая программа присваивает значения по умолчанию параметрам a, b и c внутри функции show_parameters. Затем программа четыре раза вызывает эту функцию, сначала не указывая параметров вообще, затем указывая значение только для а, потом значения для а и b и, наконец, указывая значения для всех трех параметров:
#include <iostream.h>

void show__parameters (int a=1, int b=2, int c=3)

{
   cout << "a" << a << " b " << b << " с " << с << endl;
}

void main(void)

{
   show_parameters();
   show_parameters(1001);
   show_parameters(1001, 2002);
   show_parameters(1001, 2002, 3003);
}
Когда вы откомпилируете и запустите эту программу, на вашем экране появится следующий вывод:
С:\> DEFAULTS <ENTER>

а 1 b 2 с 3

а 1001 b 2 с 3

а 1001 b 2002 с 3

а 1001 b 2002 с 3003
Как видите, если необходимо, функция использует значения параметров по умолчанию.
^ Правила для пропуска значений параметров

Если программа опускает значения параметров при вызове функции, функция использует значения по умолчанию. Если программа опускает определенный параметр для функции, обеспечивающей значения по умолчанию, то следует опустить и все последующие параметры. Другими словами, вы не можете опускать средний параметр. В случае предыдущей программы, если требовалось опустить значение параметра b в show_parameters, программа также должна была опустить значение параметра с. Вы не можете указать значение для а и с, опуская значение b.
По мере увеличения количества созданных программ и функций, вероятно, встретятся функции, которые вообще не имеют параметров. При определении такой функции (и ее прототипа) следует применить ключевое слово void. С помощью void компилятору (и другим программам) сообщается о том, что функция не использует параметров:
int my_function(void);
Если в дальнейшем в программе будет сделана попытка использовать такую функцию с параметрами, то компилятор выдаст ошибку.
^

Перегрузка функций


При определении функций в своих программах вы должны указать тип возвращаемого функцией значения, а также количество параметров и тип каждого из них. При программировании на языке С, когда у вас была функция, например, с именем add_values, которая работала с двумя целыми значениями, а вы хотели бы использовать подобную функцию для сложения трех целых значений, вам следовало создать функцию с другим именем. Например, вы могли бы использовать add_two_values и add_three_values. Аналогично если вы хотели использовать подобную функцию для сложения значений типа float, то вам была бы необходима еще одна функция с еще одним именем. Чтобы избежать дублирования функции, C++ позволяет вам определять несколько функций с одним и тем же именем. В процессе компиляции C++ принимает во внимание количество аргументов, используемых каждой функцией, и затем вызывает именно требуемую функцию. Предоставление компилятору выбора среди нескольких функций называется перегрузкой.

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

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

Например, следующая программа перегружает функцию с именем add_values. Первое определение функции складывает два значения типа int. Второе определение функции складывает три значения. В процессе компиляции C++ корректно определяет функцию, которую необходимо использовать:
#include <iostream.h>

int add_values(int a,int b)

{
   return(a + b);
)

int add_values (int a, int b, int c)

(
   return(a + b + c);
)

void main(void)

{
   cout << "200 + 801 = " << add_values(200, 801) << endl;
   cout << "100 + 201 + 700 = " << add_values(100, 201, 700) << endl;
}
Как видите, программа определяет две функции с именами add_values Первая функция складывает два значения типа int, в то время как вторая складывает три значения. Вы не обязаны что-либо предпринимать специально для того, чтобы предупредить компилятор о перегрузке, просто используйте ее. Компилятор разгадает, какую функцию следует использовать, основываясь на предлагаемых программой параметрах.

Одним из наиболее общих случаев использования перегрузки является применение функции для получения определенного результата, исходя из различных параметров. Например, предположим, что в вашей программе есть функция с именем day_of_week, которая возвращает текущий день недели (0 для воскресенья, 1 для понедельника, ..., 6 для субботы). Ваша программа могла бы перегрузить эту функцию таким образом, чтобы она верно возвращала день недели, если ей передан юлианский день в качестве параметра, или если ей переданы день, месяц и год:
int day_of_week(int julian_day)

{
   // Операторы
}

int day_of_week(int month, int day, int year)

{
   // Операторы
}

Рекурсия


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

Рекурсивной функцией называется функция, которая для выполнения определенной операции вызывает саму себя. Процесс вызова функцией самой себя называется рекурсией. По мере возрастания сложности программ и функций может оказаться, что при определении некоторых операций удобно использовать сами эти операции. В таких случаях имеет смысл создавать рекурсивные функции. Классическим примером рекурсивной обработки является вычисление факториала. Факториал значения 1 равен 1. Факториал значения 2 равен 2*1. Факториал значения 3 равен 3*2*1. Аналогично, факториал значения 4 равен 4*3*2*1. Этот процесс может быть продолжен до бесконечности. Если внимательно приглядеться к процессу вычисления факториала, то можно увидеть, что факториал, например, от 4 равен 4-кратному значению факториала от 3 (3*2*1). Аналогично, факториал от 3 равен 3-кратному значению факториала от 2 (2*1). Факториал от 2 равен двукратному значению факториала от 1 (1). В таблице демонстрируется вычисление факториала.
Таблица. Вычисление факториала

Значение

Вычисление

Результат

Факториал

1

1

1

1

2

2*1

2

2*Factorial(l)

3

3*2*1

6

3* Factorial(2)

4

4*3*2*1

24

4* Factorial(3)

5

5*4*3*2*1

120

5* Factorial(4)


В следующей программе представлена рекурсивная функция factorial, которая используется для вычисления факториала значений от 1 до 5:
#include <stdio.h>
int factorial (int value)

{

if (value ==1)

return(1);

else

return(value * factorial(value-1));

}
void main(void)

{

int i;

for (i =1; i <= 5; i++)

printf("Факториал от %d равен %d\n", i, factorial(i));

}
Как можно видеть, функция factorial возвращает значение, которое основывается на результате вызова самой этой функции.

При выполнении этой функции первым делом осуществляется проверка значения параметра на равенство 1. Если значение равно 1, то функция возвращает 1. В противном случае, в качестве результата возвращается значение, равное произведению значения входного параметра и факториала от числа, равного значению параметра минус 1. Например, предположим, что функция вызывается со значением 3. Тогда в качестве результата функцией будет возвращено 3*factorial(3-1). Обнаруживая в операторе return вызов функции factorial, компилятор организует повторный вызов, на этот раз со значением 3-1 или 2. Поскольку значение не равно 1, результатом выполнения функции будет 2*factorial(2-1). Наконец, при следующем вызове функции значение параметра равно 1, поэтому в качестве результата вызвавшей программе (функции) возвращается значение 1.

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

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

^

Встроенные функции


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

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

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

Когда вы определяете в своей программе функцию, компилятор C++ переводит код функции в машинный язык, сохраняя только одну копию инструкций функции внутри вашей программы. Каждый раз, когда ваша программа вызывает функцию, компилятор C++ помещает в программу специальные инструкции, которые заносят параметры функции в стек и затем выполняют переход к инструкциям этой функции. Когда операторы функции завершаются, выполнение программы продолжается с первого оператора, который следует за вызовом функции. Помещение аргументов в стек и переход в функцию и из нее вносит издержки, из-за которых ваша программа выполняется немного медленнее, чем если бы она размещала те же операторы прямо внутри программы при каждой ссылке на функцию. Например, предположим, что следующая программа CALLBEEP.CPP вызывает функцию show_message, которая указанное число раз выдает сигнал на динамик компьютера и затем выводит сообщение на дисплей:
#include <iostream.b>

void show_message(int count, char *message)

{
   int i;
   for (i = 0; i < count; i++) cout << '\a';
   cout << message << endl;
}

void main(void)

{
   show_message(3, "Учимся программировать на языке C++");
   show_mes sage(2, "Встроенные функции");
}
Следующая программа NO_CALL.CPP не вызывает функцию show_message. Вместо этого она помещает внутри себя те же операторы функции при каждой ссылке на функцию:
#include <iostream.h>

void main (void)

{
   int i;
   for (i = 0; i < 3; i++) cout << '\a';
   cout << " Учимся программировать на языке C++" << endl;
   for (i = 0; i < 2; i++) cout << '\a';
   cout << " Встроенные функции " << endl;
}
Обе программы выполняют одно и то же. Поскольку программа NO_CALL не вызывает функцию show_message, она выполняется немного быстрее, чем программа CALLBEEP. В данном случае разницу во времени выполнения определить невозможно, но, если в обычной ситуации функция будет вызываться 1000 раз, вы, вероятно, заметите небольшое увеличение производительности. Однако программа NO_CALL более запутана, чем ее двойник CALL_BEEP, следовательно, более тяжела для восприятия.

При создании программ вы всегда должны попытаться определить, когда лучше использовать обычные функции, а когда лучше воспользоваться встроенными функциями. Для более простых программ предпочтительно использовать обычные функции. Однако, если вы создаете программу, для которой производительность имеет первостепенное значение, вам следовало бы уменьшить количество вызовов функций. Один из способов уменьшения количества вызовов функций состоит в том, чтобы поместить соответствующие операторы прямо в программу, как только что было сделано в программе NO_CALL. Однако, как вы могли убедиться, замена только одной функции внесла значительную путаницу в программу. К счастью, C++ предоставляет ключевое слово inline, которое обеспечивает лучший способ.
^ Использование ключевого слова inline

При объявлении функции внутри программы C++ позволяет вам предварить имя функции ключевым словом inline. Если компилятор C++ встречает ключевое слово inline, он помещает в выполнимый файл (машинный язык) операторы этой функции в месте каждого ее вызова. Таким образом, можно улучшить читаемость ваших программ на C++, используя функции, и в то же время увеличить производительность, избегая издержек на вызов функций. Следующая программа INLINE.CPP определяет функции тах и min как inline:
#include <iostream.h>

inline int max(int a, int b)

{
   if (a > b) return(a);
   else return(b) ;
}

inline int min(int a, int b)

{
   if (a < b) return(a);
   else return(b);
}

void main(void)

{
   cout << "Минимум из 1001 и 2002 равен " << min(1001, 2002) << endl;
   cout << "Максимум из 1001 и 2002 равен " << max(1001, 2002) << endl;
}
В данном случае компилятор C++ заменит каждый вызов функции на соответствующие операторы функции. Производительность программы увеличивается без ее усложнения.

^

Обработка символьных данных

Вывод в C/C++


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

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

Функции, используемые для вывода данных, зависят от типа данных и способа их представления. Наиболее прост вывод строк и символьных данных.
^

Функция puts()


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

puts("Всем привет!");

строковая константа:

#define MESSAGE "Всем привет"

main()

{

puts(MESSAGE);

}

строковая переменная:

char greeting[]="Всем привет";

main()

{

puts(geering);

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

Большинство компиляторов выполняют перевод строки после выполнения функции puts(). Это значит, что после того, как данные выведены на экран монитора, курсор автоматически переходит в начало следующей строки.
^

Функция putchar()


Функция putchar() предназначена для вывода единичного символа на экран. Параметром функции может являться:
символьный литерал:

putchar('H');

символьная константа:

#define INITIAL 'H'

main()

{

putchar(INITIAL);

}

символьная переменная:

main()

{

char letter;

letter='G';

putchar(letter);

}
С помощью функции putchar() можно отображать только один символ. Инструкция
putchar('Hi');
приведет к ошибке компиляции.

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

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

Функция printf()


Функции puts() и putchar() используются довольно часто, но, к сожалению, их возможности несколько ограничены. Ни одна из них не может обеспечить вывод числовых данных, и обе они имеют только один аргумент (параметр). Это означает, что обе функции могут отобразить только один объект.

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

В простейшем случае функцию printf() можно использовать вместо функции puts() для вывода строки:
#define MESSAGE "Привет!"

main()

{

printf(MESSAGE);

printf("Добро пожаловать в мой мир! ");

}
Так же как и puts(), функция printf() будет выводить на экран строки, заключенные в кавычки, и значения строковых констант и переменных.

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

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


%d

целое число

%u

беззнаковое целое число

%f

вещественное число типа float или double

%e

вещественное число в экспоненциальной форме

%g

вещественное число, отображаемое по формату %f или %e, в зависимости от того, какая форма записи является более короткой

%c

символ

%s

строка


Таким образом, первая часть инструкции printf() записывается так:
printf("%d")
Знак процента говорит компилятору, что за ним последует указатель формата (чтобы отобразить сам символ процента, напишите его дважды: printf("%%");).

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

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

Простейший пример использования функции printf() приведен ниже:
printf("%d", 12);
В процессе выполнения этой инструкции значение 12 будет подставлено на место указателя формата. В нашем примере мы на самом деле передали библиотечной функции printf() два параметра: строку формата и числовой литерал12.

Строка формата может содержать и обыкновенный текст с включенными в него указателями формата. Например, взгляните на инструкцию:
printf("Мне исполнилось %d лет", 12);
Строкой формата в этом примере является запись

"Мне исполнилось %d лет"
Указатель формата, %d, говорит о том, что мы хотим вставить число между словами "Мне исполнилось" и словом "лет". Когда компилятор подставит число12 на место указателя формата, мы увидим следующую фразу:
Мне исполнилось 12 лет
В этом примере функции передается одновременно и строковый литерал, и числовое значение.

В данном случае тот же результат можно получить, передавая всю фразу целиком, как параметр, одной из функций:
printf("Мне исполнилось 12 лет");

puts("Мне исполнилось 12 лет");
Но чтобы комбинировать текст с числовыми константами или переменными, следует использовать именно функцию printf() и указатели формата, как, например, в программе:
main()

{

int age;

age = 12;

printf("Мне исполнилось %d лет", age);

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

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

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

Если вы хотите перевести курсор на следующую строку, вы должны добавить в строку формата управляющий код «новая строка» \n:
printf("Стоимость составляет %f за %d штук\n", amount, count);
^ Форматированный вывод

Функцию printf() можно использовать для управления форматом данных. Определять величину пробелов и количество выводимых символов можно с помощью указателей ширины поля.

Без указателя ширины поля числа с плавающей точкой, например, будут выводиться с шестью знаками после точки. Поэтому в результате выполнения инструкции
printf("Стоимость составляет %f за

%d штук", amount, count);
и появляется строка:
Стоимость составляет 45.580000 за 5 штук
В зависимости от особенностей системы и от того, как ведется расчет среднего значения, может появиться и что-нибудь в таком роде:
Стоимость составляет 45.579998 за 5 штук
Мы используем указатель ширины поля для того, чтобы придать желаемый вид числам и тексту, выводимым на экран.

Чтобы определить число знаков после точки, используется указатель %.nf, где число n определяет количество знаков.

Например, если написать:
printf("Стоимость составляет %.2f", amount);
то при выводе значения переменной с типом float оно будет иметь только два знака после точки:

Стоимость составляет 45.58
Можно также определить общую ширину поля, то есть размер (в символах) пространства, занимаемого выводимым числом, если использовать следующий формат:
%N.nf
где N— это общая ширина поля.

Если задать инструкцию
printf("Стоимость составляет %8.2f", amount);

появится строка

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

Если ширина поля, заданного указателем, оказывается меньше количества символов, составляющих число, Си, тем не менее, выведет число целиком, просто игнорируя в данном случае указатель ширины поля. Выполнение инструкции
printf("Стоимость составляет %2.2f", amount);
приведет к появлению сообщения
Стоимость составляет 45.58
Указатель ширины поля может работать как с символьными, так и со строковыми данными. Дополнительные пробелы помещаются перед текстом, сдвигая строку к правому краю воображаемой «коробочки». Например, если строковая переменная, называемая message, имеет значение «Привет», то инструкция
printf("Я позвонил, чтобы сказать %8s", message);
отобразит на экране следующую строку
Я позвонил, чтобы сказать Привет
при выводе значения строковой переменной message перед ним появятся два дополнительных пробела.
^

Выбор правильных средств вывода информации


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

Примечание: рассмотренные выше функции вызываются из библиотеки stdio.h.

Чтобы вывести на экран обычный текст или символы, можно использовать функции puts() или putchar(). Так как эти функции не имеют никаких возможностей форматирования данных, они работают быстрее, и их коды занимают меньший объем на диске, чем коды функции printf(). Имея дело с функцией puts(), прежде всего проверьте, добавляет ли компилятор код «новая строка» автоматически. Если он не делает этого, а вы не проверите сразу, потом вам придется потратить довольно много времени на редактирование программы.

Функция printf() работает медленнее и требует большего объема памяти, но она идеально подходит в тех случаях, когда вам требуется выводить числовые данные, форматировать строки или комбинировать текст и числовые переменные в одной строке. Работая с функцией printf(), следует тщательно следить за тем, чтобы указатели формата соответствовали литералам, константам и переменным в списке данных.
^

Вывод в Си++


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

В Си++ существует стандартный поток вывода cout, позволяющий в сочетании с двумя символами «меньше» (<<), которые называются оператором вставки, отображать литералы или значения констант и переменных без использования указателей формата.

Используя один стандартный поток вывода cout, можно отобразить несколько аргументов. Между собой аргументы разделяются операторами вставки. Например, инструкция
int age;

age = 43;

cout << "Вам исполнилось " << age << " года.";

отображает текст

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

Так же, как и функция printf(), cout не добавляет никаких команд новой строки после отображения данных. Чтобы перейти к новой строке, там, где вы хотите ее начать, надо добавить управляющий код \n.

^

Ввод в C/C++


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

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

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

Функция gets()


Функция gets() вводит строку в переменную. Параметром функции является имя переменной. Рассмотрим такую программу:
main()

{

char name[15];

gets(name);

puts(name);

}
Функция gets() будет рассматривать первые 14 символов, введенные с клавиатуры, как значение строковой переменной с именем name. Вы помните, что Си отводит строковой переменной столько элементов памяти, сколько указано в максимальном значении при определении переменной, а так как один элемент необходим для нулевого символа (\0), реально можно ввести на один символ меньше. Если вы хотите ввести в переменную name строку, состоящую из 15 символов, то укажите в квадратных скобках максимальное значение 16:
char name[16];
На время работы функции gets() выполнение программы приостанавливается. Она ждет, пока пользователь что-то напечатает. Для того чтобы напечатанные данные стали значением переменной, после ввода информации надо нажать клавишу Enter. Как только это сделано, строка, введенная пользователем, присваивается переменной в качестве значения, а курсор переходит на следующую строку на экране. При нажатии Enter Си добавляет в строку нулевой символ.

Когда вы вводите символы при выполнении инструкции gets(), они отображаются на экране монитора (в режиме эха), при этом не используются никакие функции вывода, и символы на самом деле не будут введены в компьютер, пока не нажата клавиша Enter.

Рассмотрим более подробный пример:
main()

{

char name[25];

printf("Пожалуйста, введите Ваше имя: ");

gets(name);

printf("Подтвердите, Ваше имя: %s", name);

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

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

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

После нажатия клавиши Enter Си присваивает введенные символы переменной name и вставляет нулевой символ в конце строки. Затем программа переходит к выполнению второй функции printf() и на экране появляется вторая надпись (предположим, что вас зовут Петр Иванов):
Пожалуйста, введите Ваше имя: Петр Иванов

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

Функция getchar()


Функция getchar() вводит с клавиатуры единичный символ. Для большинства компиляторов безразлично, к какому типу (char или int) вы отнесете вводимый символ, что обусловлено способом определения символьной переменной в K&R-стандарте языка Си.

Для ввода символа можно использовать оба этих формата:
int letter; char letter;

letter = getchar(); letter = getchar();
Обратите внимание на то, что вызов функции getchar() осуществляется не так, как вызов функций, которые мы рассматривали раньше. Вместо того чтобы поставить имя функции в начало строки инструкции, мы относим его к переменной с помощью знака «равно». Эта запись означает: «Присвоить переменной letter значение, полученное в результате выполнения функции getchar()». Практически, функция getchar() рассматривается программой как значение переменной (рис.5.3). При выполнении этой инструкции вызывается функция getchar() и осуществляется ввод символа, который присваивается переменной. Функция getchar() не имеет аргумента. Поэтому круглые скобки после имени функции остаются пустыми.

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

Некоторые компиляторы Си и Си++ используют функцию getch(), которая вводит символ без последующего нажатия Enter. Может оказаться, что при работе с getchar(), эти компиляторы требуют нажатия Enter после ввода символа.

Для чего может понадобиться ввод единичного символа? Вероятно, вам приходилось видеть программы, в которых необходимо ответить «Да» или «Нет» в ответ на запрос, или выбрать один из пунктов предложенного меню. Функция getchar() идеально подходит в этих случаях, ведь при работе с ней нет необходимости нажимать Enter, ввод одного из предложенных на выбор символов позволяет немедленно продолжить выполнение программы.

Функцию getchar() можно использовать для приостановки выполнения программы, что может оказаться весьма полезно в некоторых ситуациях. Приведем простой пример. На экран можно одновременно вывести ограниченное количество строк (обычно 25). Если в программе используется длинный ряд инструкций puts(), при выполнении которых надо вывести больше строк, чем может поместиться на экране, первые появившиеся строки информации быстро уйдут за верхний край, и пользователь просто физически не успеет ознакомиться с их содержанием до того, как на этом месте появятся другие.

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

getchar();
В этом примере функция getchar() приостанавливает выполнение программы до тех пор, пока пользователь не нажмет клавишу. Совершенно необязательно этой клавишей должна быть именно Enter, но нажатие любой другой приведет к появлению на экране соответствующего символа, а это может запутать пользователя. Функция getchar() в данном случае не связана ни с какой переменной. Если для продолжения выполнения программы будет нажата, например, клавиша Y, соответствующий символ появится на экране, но не будет присвоен в качестве значения ни одной переменной.
^

Функция scanf()


Функция scanf() является многоцелевой функцией, дающей возможность вводить в компьютер данные любых типов. Название функции отражает ее назначение— функция сканирует (просматривает) клавиатуру, определяет, какие клавиши нажаты, и затем интерпретирует ввод, основываясь на указателях формата (SCAN Formatted characters). Так же, как и функция printf(), scanf() может иметь несколько аргументов, позволяя тем самым вводить значения числовых, символьных и строковых переменных в одно и то же время.

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

Указатели формата аналогичны тем, которые используются функцией printf():


%d

целые числа

%u

беззнаковые целые числа

%f

вещественные числа, float

%e

вещественные числа в экспоненциальной форме

%g

вещественные числа в наиболее коротком из форматов %f или %e

%c

символы

%s

строки

%o

целые числа в восьмеричной системе счисления

%x

целые числа в шестнадцатеричной системе счисления
1   ...   4   5   6   7   8   9   10   11   ...   14

Реклама:





Скачать файл (398.8 kb.)

Поиск по сайту:  

© gendocs.ru
При копировании укажите ссылку.
обратиться к администрации
Рейтинг@Mail.ru