Будь умным!


У вас вопросы?
У нас ответы:) SamZan.net

модульного программирования Учебнометодическое пособие для студентов специальности 2204

Работа добавлена на сайт samzan.net:

Поможем написать учебную работу

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

Предоплата всего

от 25%

Подписываем

договор

Выберите тип работы:

Скидка 25% при заказе до 9.11.2024

Федеральное агентство железнодорожного транспорта

Уральский государственный университет путей сообщения

Кафедра «Прикладная информатика»

А. В. Кибардин

ПРОГРАММИРОВАНИЕ НА ЯЗЫКЕ С++

Часть 1

Основы структурного и модульного программирования

Учебно-методическое пособие для студентов  

специальности 220401 – «Мехатроника»


Екатеринбург

2011

УДК 004.43 (075.8)

К38

      

Кибардин, А. В.

К38   Программирование на языке С++. Часть 1. Основы структурного и модульного программирования: учеб.-метод. пособие / А. В. Кибардин.

Екатеринбург:    УрГУПС, 2011. 64 с.

Пособие предназначено для изучения основ программирования на алгоритмическом  языке С++.

Ориентировано на студентов специальности 220401 – «Мехатроника», а также на студентов, аспирантов, слушателей ФПК и подготовительного отделения, обучающихся основам современных информационных технологий.

Библиогр.:   6 назв.  Табл. 4

УДК 004.43 (075.8)

Пособие рекомендовано к печати на заседании кафедры "Прикладная      информатика", протокол №69 от 14.10.2010 г.

Автор:   А. В. Кибардин, доцент кафедры «Прикладная информатика»,

УрГУПС

Рецензенты:  Г.Б. Смирнов, проф., д-р техн. наук (кафедра вычислительной техники, УрФУ им. первого Президента России Б.Н. Ельцина)

А. Ф. Шориков, проф., д-р физ.-мат. наук (кафедра информационных систем в экономике, Уральский государственный экономический университет, г. Екатеринбург)

© Уральский государственный университет     путей сообщения (УрГУПС), 2011


ОГЛАВЛЕНИЕ

[1]
ВВЕДЕНИЕ

[2] 1. Структурное программирование

[3]
Состав языка

[3.1] Алфавит языка

[3.2] Идентификаторы

[3.3] Зарезервированные (ключевые) слова

[3.4] Знаки операций

[3.5] Константы

[3.6] Комментарии

[4] Типы данных

[4.1] Основные типы данных

[4.2] Тип void

[5] Структура программы

[6] Переменные и выражения

[6.1] Переменные

[6.2] Операции

[6.3] Примеры операций

[6.4] Выражения

[7] Функции ввода-вывода

[7.1] Основные функции ввода-вывода в стиле С

[7.2] Спецификации формата

[7.3] Модификаторы формата

[7.4] Вывод в стиле С++

[8] Операторы

[8.1] Оператор «выражение»

[8.2] Условный оператор if

[8.3] Оператор  switch

[8.4] Цикл с предусловием

[8.5] Цикл с постусловием

[8.6] Цикл с параметром for

[8.7] Операторы передачи управления

[8.7.1] Оператор безусловного перехода

[8.7.2] Оператор break

[8.7.3] Оператор continue

[8.7.4] Оператор return

[9] Указатели и ссылки

[9.1] Инициализация указателей

[9.2] Операции с указателями

[9.3] Ссылки

[10] Массивы

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

[10.2] Динамические массивы

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

[10.4] Задание

[10.5] Написать программу, вычисляющую среднее арифметическое для каждого столбца и каждой строки двумерного числового массива A[4, 5].

[10.6] Строки

[11] Типы данных, определенные пользователем

[11.1] Переименование типов

[11.2] Перечисления  

[11.3] Структуры

[12]
2.  Модульное программирование

[13] Функции

[13.1] Объявление и определение функций

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

[13.3] Возвращаемое значение

[13.4] Параметры функции

[13.5] Передача массивов в качестве параметров

[13.6] Передача имен функций в качестве параметров

[13.7] Параметры со значениями по умолчанию

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

[13.9] Правила описания перегруженных функций

[13.10] Шаблоны функций

[13.11]

[14] Функции стандартной библиотеки

[14.1] Функции ввода/вывода

[14.1.1] Открытие потока

[14.1.2] Ввод/вывод

[14.1.3] Закрытие потока

[14.1.4] Обработка ошибок

[14.2] Функции работы со строками и символами

[14.3] Математические функции

[15] Директивы препроцессора

[15.1] Директива  #include

[15.2] Директива #define

[15.3] Директивы условной компиляции

[15.4] Директива #undef

[15.5] Предопределенные макросы

[16] Области действия и пространства имен

[16.1] Внешние объявления

[16.2] Поименованные области

[17]
БИБЛИОГРАФИЧЕСКИЙ СПИСОК


ВВЕДЕНИЕ

В настоящем учебно-методическом пособии рассматриваются основы структурного и модульного программирования на алгоритмическом языке высокого уровня С++. Материал излагается в соответствии со стандартом языка ISO/IEC 14882 (1998).

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

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


1. Структурное программирование

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

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

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


Состав языка

Алфавит языка

Алфавит языка включает:

строчные и прописные латинские буквы и знак подчеркивания;

арабские цифры;

специальные знаки;

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

Из символов алфавита формируются лексемы языка:

идентификаторы:

ключевые (зарезервированные) слова;

знаки операций;

константы;

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

Идентификаторы

Идентификатор –  имя объекта программы. В имени могут использоваться латинские буквы, цифры и знак подчеркивания. Прописные и строчные буквы различаются, например, max, MAX, Max – три различных имени. Первым символом не может быть цифра, но может быть знак подчеркивания. Нельзя использовать внутри имени пробелы. Длина идентификатора не ограничена по стандарту, но некоторые компиляторы налагают на нее ограничения.

!!! В качестве имени нельзя использовать зарезервированные слова.

Зарезервированные (ключевые) слова

Ключевые слова – это зарезервированные идентификаторы. Их можно использовать только в том смысле, в котором они определены. В табл. 1 представлен список зарезервированных (ключевых) слов языка С++.

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

Знак операции – это один или несколько символов, определяющих действие над данными (операндами). Внутри знака операции пробелы не допускаются. Операции делятся на унарные (имеющие один операнд), бинарные (два операнда) и тернарную (три операнда). Один и тот же знак может интерпретироваться по- разному, в зависимости от контекста.

         Таблица 1                             

Список ключевых слов С++

Ключевое слово

Ключевое слово

Ключевое слово

Ключевое слово

asm

auto

bool

break

case

catch

char

class

const

const_cast

continue

default

delete

do

double

dynamic_cast

else

enum

explicit

extern

export

false

float

for

friend

goto

if

inline

int

long

mutable

namespace

new

operator

private

protected

public

register

reinterpret_cast

return

short

signed

sizeof

static

static_cast

struct

switch

template

this

threw

true

try

tyopedef

typeid

typename

union

unsigned

using

virtual

void

volatile

wchar_t

while

Константы

Константы – это неизменяемые величины. Различают целые, вещественные, символьные и строковые константы.

Примеры

8, 0, 223196 – целые (десятичные)

охА, 0хВ8 – целые шестнадцатеричные

5.7     .001      35. – вещественные

'A'   'z'   'db' – символьные (один или два символа)

"Ivan" – строковая.

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

кодов, не имеющих графического изображения (\ а – звуковой сигнал);

символов апострофа ('), обратной косой черты (\), знака вопроса (?) и кавычки (").

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

Таблица 2

Управляющие символы

Управляющие последовательности символов

Значение

\a

\b

\f

\n

\r

\t

\v

\\

\’

\"

\?

Звуковой сигнал

Возврат на шаг

Перевод формата (страницы)

Перевод строки

Возврат каретки

Горизонтальная табуляция

Вертикальная табуляция

Обратная косая черта

Апостроф

Кавычка

Вопросительный знак

        

Управляющие последовательности используются и в строковых константах. Например, если внутри строки необходимо записать кавычку, ее предваряют косой чертой: "Издательство \" Наука \"". 

Комментарии

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

// Это комментарий

Для комментирования части строки используются двойные символы /* и */:

/*Это комментарий*/.

Типы данных

Тип данных определяет:

внутреннее представление данных в памяти ЭВМ;

множество значений, которые могут принимать величины этого типа;

операции и функции, которые можно применять к величинам этого типа.

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

Основные типы данных

Для описания основных (стандартных) типов определены следующие ключевые слова:

 int (целый);

char (символьный);

wchar_t (расширенный символьный);

bool (логический);

float (вещественный);

double (вещественный с двойной точностью).

Первые четыре типа называют целочисленными (целыми), последние два − типами с плавающей точкой.

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

 short (короткий);

long (длинный);

signed (знаковый);

unsigned (беззнаковый).

Размер типа int не определен стандартом, а зависит от компьютера и компилятора. Для 16-разрядного процессора под величины этого типа отводится 2 байта, для 32-разрядного − 4 байта. Спецификатор short перед int указывает компилятору, что под число требуется выделить 2 байта.

При использовании спецификатора signed старший бит числа интерпретируется как знак ( 0 − положительное число, 1 − отрицательное). Спецификатор unsigned позволяет представлять только положительные числа. По умолчанию все целые типы считаются знаковыми, т. е. спецификатор signed можно опускать.

Под величину символьного типа char отводится количество байт, достаточное для размещения любого символа из набора символов для данного компьютера. Как правило, это 1 байт. Тип char может быть со знаком и без знака. В величинах со знаком можно хранить значения от -127 до +127. При использовании спецификатора unsigned значения этого типа могут находиться в пределах от 0 до 255.

Расширенный символьный тип wchar_t предназначен для работы с набором символов, для кодировки которых недостаточно 1 байта.

Логический тип bool может принимать значения только true (истина) и false (ложь). Внутренне представление  false − 0. Любое другое значение интерпретируется как  true.

Cтандарт C++ определяет три типа данных для хранения вещественных чисел: double, float и long double. В IBM  совместимых компьютерах величины типа float занимают 4 байта, double − 8 байтов, а long double − 10 байтов.

Тип void

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

Структура программы

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

тип_возвращаемого_результата   имя_функции ([параметры])

{ операторы, составляющие тело функции

}

В определении функции следует отметить следующие моменты:

1. Если функция не должна возвращать результат, указывается тип void.

2. Тело функции является блоком и заключается в фигурные скобки.

3. Функции не могут быть вложенными.

4. Каждый оператор заканчивается точкой с запятой (кроме составного оператора).

Пример. Структура программы, содержащей функции main, f1 и f2.

Директивы препроцессора

Описания

int main ( ) {

операторы главной функции

}

int f1 ( ) {

операторы функции f1

}

int f2 ( ) {

операторы функции f2

}

Программа может состоять из нескольких модулей (исходных файлов).

Переменные и выражения

Переменные

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

Общий вид оператора описания переменных:

[класс памяти]  [const]  тип  имя [инициализатор];

Квадратные скобки [ ] указывают на необязательный параметр, т. е. параметр, который может опускаться. Класс памяти может принимать значения: auto, extern, static и register (см. далее). Модификатор const показывает, что значение переменной изменять нельзя. Такую переменную называют константой. При описании переменной можно присвоить начальное значение, это называется инициализацией. Инициализатор можно записывать либо со знаком равенства, либо в круглых скобках. Константа должна быть инициализирована при объявлении.

Примеры

short int a=1;

const char c='c';

char s, sf='f'; //инициализация относится только к sf

int t(54);

float d=0.22, x(3), sum;

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

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

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

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

Класс памяти определяет время жизни и область видимости программного объекта. Если класс памяти не указан явно, он определяется компилятором.

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

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

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

auto  автоматизированная переменная. Время жизни от начала до конца блока;

extern − переменная определена в другом месте программы (в другом файле или дальше по тексту);

static − статическая переменная. Время жизни постоянное;

register − аналогично auto.

Примеры

int a;   // 1 глобальная переменная

int main ( ) {

int b;   // 2 локальная переменная

extern int x;  // 3 определена в другом месте

static int c;  // 4 статическая локальная

a=1;   // 5 присваивание глобальной переменной

int a;   // 6 локальная переменная а

a=2;   // 7 присваивание локальной переменной а

return 0;

}

int x=4;   // 8 определение и инициализация х

!!! Имя переменной должно быть уникальным в своей области действия (т. е. в одном блоке нельзя описать две переменные с одинаковыми именами).

Операции

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

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

Таблица 3

Основные операции С++

Операция

Описание

1

2

Унарные операции

++

увеличение на 1

--

уменьшение на 1

sizeof

размер данного в байтах

~

поразрядное отрицание

!

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

-

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

+

унарный плюс

&

взятие адреса

*

разадресация

new

выделение памяти

delete

освобождение памяти

(type)

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

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

*

умножение

/

деление

%

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

+

сложение

-

вычитание

<<

сдвиг влево

>>

сдвиг вправо

<

меньше

<=

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

>

больше

>=

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

==

равно

&

поразрядная конъюнкция (и)

^ 

поразрядное исключающее или

| 

поразрядная дизъюнкция (или)

&&

логическое и

|| 

логическое или

?: 

условная операция (тернарная)

=

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

*=

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

                  

  

 Окончание табл. 3

1

2

/=

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

%=

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

+= 

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

-=

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

<<=

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

>>=

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

&=

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

|=

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

^=

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

,

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

Примеры операций

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

Примеры

...

int x = 3, y = 3;

printf ("Значение префиксного выражения: % d \ n", ++ x);

printf ("Значение постфиксного выражения: % d \ n", y ++ );

...

На экране:

Результат префиксного значения: 4

Результат префиксного значения: 3

Операция определения размера sizeof предназначена для вычисления размера объекта или типа в байтах и имеет две формы:

sizeof выражение

sizeof (тип)

Операция  sizeof (float) вернет значение 4 (длина в байтах типа float).

Операция sizeof (х + 0.1) вернет значение 8 (если х описана как float) − эта величина получается потому, что  вещественные константы по умолчанию описываются как double, т. е. имеют длину 6 байтов.

Операции присваивания могут использоваться в программах как законченные операции. В сложных операциях присваивания (+=, -= и т. д.) при вычислении значения в правой части используется значение из левой части:

...

int a = 3, b = 5, c =4;

c = b + c;

a += b; // это более компактная запись a = a + b

...

Условная операция ? : − это тернарная операция. Ее формат:

операнд_1 ? операнд_2 : операнд_3

Первый операнд оценивается с точки зрения эквивалентности его нулю (операнд, равный нулю, оценивается как false, не равный нулю −  как true). Если результат вычисления операнда 1 равен true, то результатом условной операции   будет значение операнда 2, иначе − операнда 3. Условная операция является сокращенной формой оператора if.

Пример

int a = 11, b = 4, max;

max = (b > a)? b : a;

printf ("Наибольшее число: % d", max);

На экране будет напечатано:

Наибольшее число: 11

Выражения

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

Примеры

(a + 0.12) / 6

x && y || ! z

(t + sin (x) - 1.5e4) / (2*k + 2 )

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

Примеры

a = b = c; // Данная запись означает a = (b = c)

a + b + c; // Это (a + b) + c

Результат вычисления выражения характеризуется значением и типом. Например, если a, b − переменные целого типа:

int a = 2, b =5;

то выражение  a + b имеет значение 7 и тип  int.

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

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

− изменяющие внутреннее представление  величины;

− изменяющие только интерпретацию внутреннего представления.

К первому типу относится, например, преобразование целого типа в вещественное; ко второму − преобразование знакового целого в беззнаковое.

В программе можно задавать преобразования типов явным образом (см. тему «Преобразования типов» далее).

Функции ввода-вывода

Основные функции ввода-вывода в стиле С

int scanf (const char* format, ...)  // ввод 

int printf (const char* format, ...)  // вывод

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

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

# include <stdio.h>

int main ( ) {

int i;

printf ("Введите целое число\n");

scanf ("%d", & i);

printf ("Вы ввели%d", i);

return 0;

}

Пояснения

В этой программе в первой строке подключается заголовочный файл, содержащий описания функций ввода-вывода. Функция  printf  в четвертой строке выводит сообщение на экран и переходит на новую строку в соответствии с управляющей последовательностью \n. Функция scanf заносит введенное с клавиатуры число в переменную i (знак & означает операцию получения адреса). Последовательность символов %d является спецификацией формата десятичных чисел.

Спецификации формата

cаргумент рассматривается как отдельный символ.

d, iаргумент преобразуется к десятичному виду.

e, Eаргумент преобразуется в десятичную форму в экспоненциальном виде.

f аргумент преобразуется в десятичную форму с фиксированной десятичной точкой. 

g, G используется  формат %e или %f, который короче; незначащие нули не печатаются. 

Oаргумент преобразуется в беззнаковую восьмеричную форму.

pвывод указателя в шестнадцатеричном формате.

s аргумент является строкой. 

u аргумент преобразуется в беззнаковую десятичную форму.

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

% − выводится символ %.

Модификаторы формата

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

- % -minC или %minC

- % -min.precisionC  или % min.precisionC  

С − это спецификации формата; min − число, задающее минимальную ширину поля. Смысл модификатора precision, задаваемого десятичным числом, зависит от спецификации формата, с которой он используется:

− при выводе строки (спецификация %S) precision указывает максимальное число символов для вывода;

− при выводе вещественного числа (спецификации %f или %e) precision указывает количество цифр после десятичной точки;

− при выводе целого числа (спецификации %d или %i) precision указывает минимальное количество выводимых цифр. Если число представляется меньшим числом цифр, чем указано в precision, выводятся ведущие нули.

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

Вывод в стиле С++

Для ввода-вывода в стиле С++ используются стандартные объекты-потоки cin для ввода с клавиатуры и cout для вывода на экран, а также операции помещения в поток << и чтения из потока >> (см. методические указания «Программирование на яыке С++. Часть 4. Стандартная библиотека»).

Пример

# include <iostream.h>

int main ( ) {

int i;

cout << "Введите целое число \n";

cin >> i;

cout << "Вы ввели число" << i;

return 0;

}

Операторы

Оператор «выражение»

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

Примеры

i ++;  // операция инкремента

a = d + c; // присваивание 

fun (i, k); // вызов функции

Условный оператор if

Условный оператор if используется для разветвления процесса вычислений на два направления. Формат оператора:

 if (выражение) оператор_1; [else оператор_2;]

Сначала вычисляется выражение. Если оно не равно нулю (имеет значение true), выполняется первый оператор, иначе − второй. Ветвь else может отсутствовать (квадратные скобки в данном случае означают необязательный параметр). Если в какой-либо ветви требуется  выполнить несколько операторов, их необходимо заключить в блок (в фигурные скобки). Блок может содержать любые операторы, в том числе и описания.

Примеры

if (a < 0) b = 1;

if (a ++) b ++;

if (b > a) max = b; else max = a;

if (a < b && a > d) b ++; else {b = a; a =0;}

Распространенная ошибка при записи условных операторов − использование в выражениях вместо проверки на равенство (  == ) простого присваивания      ( = ), например:

if (a = 1) b =0; 

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

Вторая ошибка − неверная запись проверки на принадлежность диапазону. Например, чтобы проверить условие  0 < x < 1, нельзя его записать в условном операторе непосредственно:

if ( 0 < x < 1)...;

Здесь тоже нет синтаксической ошибки. Правильный способ записи:

If (0<x && x<1) ...;

Задание

Написать программу, вычисляющую корни квадратного уравнения. Для вычисления квадратного корня воспользуйтесь функцией  sqrt (заголовочный файл <math.h>).

Оператор  switch

Оператор switch (переключатель) предназначен для разветвления процесса вычислений на несколько направлений. Формат оператора:

switch (выражение ) {

case константное_выражение_1 : [список_операторов_1]

case константное_выражение_2 : [список_операторов_2

...

case  константное_выражение_N : [список_операторов_N]

[default : операторы]

}

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

Пример. Программа, печатающая название нажатой пользователем цифры:

# include <iostream.h>

int main ( ) {

char op;

cout << "Нажмите любую цифру"; cin >>op;

switch (op) {

case '0' : cout << "Это цифра 0"; break;

case '1' : cout << "Это цифра 1"; break;

// Здесь надо поместить остальные ветви оператора switch

case '9' : cout << "Это цифра 9"; break;

default : cout << "Это не цифра ";

}

return 0;

}

Пояснения

Первая строка программы − это директива препроцессора. Она подключает заголовочный файл iostream.h, содержащий описания объектов-потоков ввода cin и вывода cout.

Оператор break прерывает дальнейшее выполнение ветвей переключателя.

Цикл с предусловием

Формат оператора:

while (выражение) оператор;

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

Задание

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

Цикл с постусловием

Формат оператора:

do оператор while (выражение) ;

Сначала выполнятеся простой или составной оператор, составляющий тело цикла, а затем вычисляется выражение. Если оно истинно, тело цикла выполняется снова. Цикл завершается, когда выражение станет равно false.

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

Формат оператора:

for (инициализация; выражение; модификации ) оператор;

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

for (int i = 0, j = 2; ...

int k, m;

for (k = 1, m = 0; ...

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

Выражение определяет условие выполнения цикла: если его результат, приведенный к типу bool, равен true, цикл выполняется. Цикл с параметром реализован как цикл с предусловием.

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

Тело цикла представляется простым или составным оператором.

Пример. Оператор, вычисляющий сумму чисел от 1 до 100:

int s=0;

for (int i = 1; i <=100; i ++) s += i;

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

Задание

Написать программу, вычисляющую таблицу значений функции y=sin(x),     a<=х<=b, где границы интервала a, b и шаг изменения х  задает пользователь с клавиатуры. Программу написать тремя способами, используя разные операторы цикла

Операторы передачи управления

Оператор безусловного перехода

Формат:

goto метка;

В теле той же функции, где использован оператор goto, должна присутствовать одна конструкция вида:

метка: оператор;

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

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

Оператор break

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

Оператор continue

Оператор перехода к следующей итерации цикла continue пропускает все операторы, оставшиеся до конца тела цикла, и передает управление на начало следующей итерации.

Оператор return

Оператор возврата из функции return завершает работу функции и передает управление в точку ее вызова.

Формат оператора:

return [выражение];

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

Указатели и ссылки

При выполнении оператора определения переменной, например,

int i =10;

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

В С++ различают три вида указателей − указатели на объект, на функцию и на void, отличающиеся свойством и набором операций.

Указатели на функции  рассмотрены в теме «Функции »методических указаний «Программирование на языке С++. Часть 2. Модульное программирование».

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

тип * имя;

где тип может быть любым, кроме ссылки  и битового поля (см. далее);    имя − это имя объекта. Звездочка относится непосредственно к имени объекта, поэтому, чтобы объявить несколько указателей, звездочку необходимо ставить перед именем каждого из них.

Примеры

int *a;

float *b, *c, *d;

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

Указатель может быть константой или переменной, а также указывать на константу или переменную.

Примеры

int i; // целая переменная

const int ci; // целая константа

int *pi; // указатель на целую переменную

const int *pci; // указатель на целую константу

int *const cp=&i; // указатель-константа на целую переменную

const int *const cpc=&ci; // указатель-константа на целую константу

Инициализация указателей

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

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

Существуют следующие способы инициализации указателей:

1. Присваивание указателю адреса существующего объекта:

int a = 5;

int *p = &a;

int *p1(&a); // то же, что и предыдущая запись

int *r = p;

2. Присваивание указателю адреса области памяти в явном виде:

char *vp = (char*)0xD8000000;

здесь 0xD8000000 − шестнадцатеричная константа; (char*) − операция приведения типа: константа преобразуется к типу "указатель на тип char".

3. Присваивание пустого значения:

int *s = 0;

int *p = NULL;

здесь NULL − это константа-указатель, равный 0.

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

int *n = new int;

int *m = new int (10); // используется операция выделения памяти

int *u = (int*) malloc (sizeof (int)); // используется функция из библиотеки С

Освобождение памяти, выделенной с помощью операции new, должно осуществляться с помощью операции delete, а памяти, выделенной функцией malloc, − с помощью функции free. При этом переменная-указатель сохраняется и может повторно быть инициализирована.

Примеры

delete n;

delete m;

free (u);

Операции с указателями

С указателями могут выполняться следующие операции: разадресация, или косвенное обращение к объекту ( * ), присваивание, сложение с константой, вычитание, инкремент (++), декремент (--), сравнение, приведение типов. При работе с указателями часто используется операция получения адреса (&).

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

char a;

char *p = new char;

*p = 'A'; a = *p;

unsigned long int A = 147483647;

unsigned short int *pi = (unsigned short int*)&A;

В последней строке происходит явное приведение типов.

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

Ссылки

Ссылка представляет собой синоним имени, указанного при инициализации ссылки. Ссылки можно рассматривать как указатель, который всегда разыменовывается. Формат объявления ссылки: тип & имя;

где тип − это тип величины, на которую указывает ссылка; &  оператор ссылки, указывающий, что следующее за ним имя является именем переменной ссылочного типа.

Примеры

int kol;

int & pal = kol; // ссылка pal − альтернативное имя kol

const char & cr = '\n'; // ссылка на константу

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

1. Переменная-ссылка должна явно инициализироваться при ее описании.

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

3. Тип ссылки должен совпадать с типом величины, на которую он ссылается.

4. Не разрешается определять указатели на ссылки, создавать массивы ссылок и ссылки на ссылки.

Ссылки чаще всего применяются в качестве параметров функций и типов возвращаемых функциями значений (см. методические указания «Программирование на языке С++. Часть 2. Модульное программирование»).

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

Массивы

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

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

float a[10];

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

int b[5] = {3, 2, 1} // элементы b[3] и b[4] обнуляются

Размерность массивов предпочтительнее задавать с помощью именованных констант:

const int n = 5;

int marks[n]={4, 4, 5, 3, 4};

Динамические массивы

Динамические массивы создаются с помощью операции new, при этом необходимо указать тип и размерность, например:

int n = 100;

float *p = new float [n];

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

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

p[5] или через указатель *(p+5).

Альтернативный способ создания динамического массива − использование функции malloc библиотеки С:

int n =100;

float *q = (float*)malloc(n*sizeof(float));

Память, зарезервированная под динамический массив с помощью new [ ], должна освобождаться с помощью оператора delete [ ], а память, выделенная с помощью функции malloc, − посредством функции free:

delete [ ] p;

free (q);

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

Инкремент перемещает указатель к следующему элементу массива, декремент − к предыдущему. Фактически значение указателя изменяется на величину sizeof (тип). Если указатель на определенный  тип увеличивается или уменьшается на константу, его значение изменяется на  величину этой константы, умноженной на размер объекта данного типа, например:

short int *p = new short [5];

p++; // значение p увеличивается на 2 (байта)

long *q = new long [5];

q++; // значение q увеличивается на 4 (байта)

Выражение (*p)++ инкрементирует значение, на которое ссылается указатель.

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

*p++ = 10;

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

*p = 10; p ++;

Задания

  1.  Написать программу, вычисляющую сумму элементов числового массива из 10 элементов и их среднее арифметическое.
  2.  Написать программу, находящую максимальный и минимальный элементы числового массива (размерность массива задает пользователь).
  3.  Написать программу, сортирующую элементы числового массива в порядке возрастания их значений (размерность массива задает пользователь).

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

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

int matr [6] [8];

Здесь задается описание двумерного массива из 6 строк и 8 столбцов. В памяти такой массив располагается в последовательных ячейках построчно. Для доступа к элементу массива указываются все его индексы: matr [i] [j] или *(matr[i] + j), или *(*matr + i ) +j).

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

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

int mass1 [3] [2] = {{1, 1}, {0, 1}, {1, 0}};

int mass2 [3] [2] = {1, 1, 0, 1, 0, 1};

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

int nstr = 5;

int **m = (int**) new int [nstr] [10];

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

int nstr, nstb;

cout << "Введите количество строк и столбцов";

cin >> nstr >> nstb;

int **a = new int *[nstr];

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

a[i] = new int [nstb];

...

Задание

Написать программу, вычисляющую среднее арифметическое для каждого столбца и каждой строки двумерного числового массива A[4, 5].

Строки

Строка представляет собой массив символов, заканчивающийся нуль-символом. Нуль-символ − это символ с кодом, равным нулю, что записывается в виде управляющей последовательности '\0'. По положению нуль-символа определяется фактическая длина строки. Строку можно инициализировать строковым литералом.

Пример

char str[10] = "Привет";

// выделено 10 элементов с номерами от 0 до 9

Если строка при определении инициализируется, ее размерность можно опускать:

char str[ ] = "Привет";

Оператор

char *str = "Привет";

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

Объявление:

сhar *str [10];

Задает массив из десяти строк.

Операция присваивания значения одной строки другой не определена и может выполняться с помощью цикла или функций стандартной библиотеки (см. раздел "Функции работы со строками и символами" методических указаний “Программирование на языке С++. Часть 2” ).

В стандартной библиотеке С++ определен класс string, который обеспечивает индексацию, присваивание, сравнение и ряд других операций со строками (см. раздел «Строки»в методических указаниях «Программирование на языке С++. Часть 4»).

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

Пример

char str [100];

cin >> str;

При вводе с клавиатуры строки «один два три» в переменную str поместится текст «один». При необходимости ввода строки целиком (т. е. до символа '\n') следует использовать методы потоковых классов get и getline (см. тему «Потоковые классы»).

Пример. Программа, запрашивающая пароль у пользователя:

# include <stdio.h>

# include <string.h> //подключаем библиотеку работы со строками

int main ( ) {

char s[5], passw[ ] = "bond";

printf ( "Введите пароль:\n");

gets(s); // функция ввода строки

if (strcmp(s, passw) = = 0) printf ("\n Пароль верен\n");

// strcmp( ) − функция сравнивает две строки

else printf ("\n Пароль не верен\n");

return 0;

}

Задание

Написать программу, выполняющую частотный анализ строки, введенной пользователем (т. е. вычисляющей, сколько раз в  строку входит данный символ). Рапорт по каждому символу выдавать  только один раз (например,  символ «a» встречается в строке несколько раз, рапорт должен быть выдан один раз).

Типы данных, определенные пользователем

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

Переименование типов 

Типу можно задать новое имя с помощью ключевого слова typedef:

typedef  тип  новое_имя [размерность];

Примеры

typedef unsigned int UINT;

typedef char Msg [100];

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

UINT i, j;

Msg str [10];

Перечисления  

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

enum [имя_типа] {список констант};

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

Пример

enum digit {one = 1, two = 2, three = 3};

enum Err {ERR_READ, ERR_WRITE, ERR_CONVERT};

Err error;

digit dig;

Константам ERR_READ, ERR_WRITE, ERR_CONVERT присваиваются (автоматически) значения 0, 1, 2 соответственно.

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

Структуры

Структура может содержать элементы различных типов:

struct [имя_типа] {

тип_1 элемент_1;

тип_2 элемент_2;

тип_n элемент_n;

} [список описателей];

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

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

Пример

struct {

char fio [30];

int date, code;

double salary;

} stuff [100], *ps; // определение массива структур и указателя на структуру

Пример

struct Worker {

char fio [30];

int date, code;

double salary;

}; // описание типа Worker

Worker staff [100], *ps; // описание массива и указателя типа Worker

Для инициализации структуры значения ее элементов перечисляют в фигурных скобках в порядке их описания:

struct {

char fio [30];

int date, code;

double salary;

} worker = {"Иванов", 31, 215, 3400.50};

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

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

Доступ к полям структуры выполняется с помощью операций выбора . (точка) при обращении к полю через имя структуры и -> при обращении через указатель:

Worker staff [100], worker;

strcpy(worker.fio,"Ivanov"); // используем функцию копирования строки

// из библиотеки C (<string.h>) для передачи значения в строковое поле

stuff[1].code = 215;

Worker *ps = new Worker; // Задание указателя типа Worker

ps ->salary = 1200.12; // Вариант (*ps).salary = 1200.12;

Задание

Написать программу, позволяющую ввести в память следующую информацию о  сотрудниках организации (объем списка задает пользователь) :

(ФИО, должность, возраст) и выполняющую поиск сотрудников по заданной должности или возрасту.


2.  Модульное программирование

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

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

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

Функции

Объявление и определение функций

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

[класс ] тип имя_функции ([список параметров ])

[throw (исключения)]

{тело функции}

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

extern − глобальная видимость во всех модулях программы (по умолчанию);

static − видимость только в пределах модуля, в котором определена функция.

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

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

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

Пример. Функция, возвращающая сумму двух целых величин:

# include <iostream.h>

int sum (int a, int b);          // объявление функции

int main ( ) {

int a = 2, b = 3, c,d;

c = sum(a, b);                    // вызов функции

cin >> d;

cout << sum(c,d);

return 0;

}

int sum (int a, int b) {       // определение функции

return a+b;

}

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

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

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

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

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

Механизм возврата из функции в вызывающую ее функцию реализуется оператором  return [выражение];

Функция может содержать несколько операторов return. Если функция описана как void, выражение не указывается. Оператор return можно опускать для функции типа void, если возврат из нее происходит перед закрывающей скобкой, и для функции main.

Примеры

int f1 ( ) {return 1;} // Правильно

void f2 ( ) {return 1;} // Неправильно

Параметры функции

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

Существует два способа передачи параметров в функцию − по значению и по адресу.

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

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

Пример

# include <iostream.h>

void f (int i, int *j, int & k);

int main ( ) {

int i = 1, j = 2, k= 3;

cout << "i  j  k \n";

cout << i << '  '<< j << '  ' << k << '\n;

f(i, &j, k);

cout << i << '  '<< j << '  ' << k << '\n;

return 0;

}

void f(int i, int *j, int &k) {

i + +; (*j)+ +; k+ +;

}

На экране будет напечатано:

i   j   k

1 2  3

1 3  4

Первый параметр функции f(i) передается по значению. Его изменение в функции не влияет на исходное значение. Второй параметр (j) передается по адресу с помощью указателя. Третий параметр (k) передается по адресу с помощью ссылки.

Если требуется запретить изменение параметра внутри функции, используется модификатор const:

int f(const char *a);

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

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

Пример

# include <iostream.h>

int sum (const int *mas, const int n);

int const n = 10;

int main ( ) {

int marks [n] = {3,4,5,4,4};

cout << "Сумма элементов массива" << sum (marks,n);

return 0;

}

int sum (const int *mas, const int n);

// Варианты заголовка: int sum (int mas [ ], int n);

// или  int sum (int mas[n], int n);

int  s = 0;

for (int i = 0; i<n; i + +;) s +=mas[ i ];

return s;

}

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

Пример

include <stdio.h>

include <stdlib.h>

int sum (const int *a, const int nstr, const int nstb);

int main ( ) {

int b[2] [2] = {{2,2},{4,3}};

printf ("Сумма элементов b: %d \ n",sum (&b[0][0],2,2));

int i, j, nstr, nstb, *a;

printf( "Введите количество строк и столбцов а: \n");

scanf("%d%d", &nstr, &nstb);

a=(int*) malloc(nstr*nstb*sizeof(int));

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

for (j=0; j<nstb; j++) scanf ("%d",&a[i * nstb + j]);

printf("Сумма элементов a: %d \n",sum(a, nstr, nstb));

return 0;

}

int sum (const int *a, const int nstr, const int nstb) {

int i, j, s =0;

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

for (j = 0; j<nstb; j++) s +=a[i*nstb+j];

return s;

}

!!! Двумерный массив внутри функции интерпретируется как одномерный, а его индекс пересчитывается в программе, поэтому возможно такое обращение к элементу с номерами i и j: a[i*nstb + j].

Альтернативный способ работы с двумерным массивом − использование операций new и delete.

Пример

# include <iostream.h>

int sum (int **a, const int nstr, const int nstb);

int main( ) {

int nstr, nstb;

cin >> nstr >> nstb;

int **a, i, j;

a = new int *[nstr];

for (i =0; i<nstr; i++) a[ i ]=new int [nstb];

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

for (j =0; j<nstb; j++) cin >> a[ i ][ j ];

cout << sum (a, nstr,nstb);

return 0;

}

int sum (int **a, const int nstr, const int nstb) {

int i, j, s =0;

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

for (j =0; j<nstb; j++) s += a[ i ][ j ];

return s;

}

Задания

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

Передача имен функций в качестве параметров

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

void f(int  a) { /* Тело функции */} // Определение функции

void (*pf) (int); // указатель на функцию

...

pf = &f; // указателю присваивается адрес функции

pf(10); // функция f вызывается через указатель pf

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

# include <iostream.h>

typedef void (*pf) (int); // Описание типа PF как указателя на функцию

void f1(PF pf) { // Функция f1 получает в качестве параметра указатель типа PF

pf(5); // Вызов функции, переданной через указатель

}

void f(int i) { cout << i; }

int main ( ) {

f1(f);

return 0;

}

Параметры со значениями по умолчанию

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

int f (int a, int b=0);

void f1 (int, int = 0, char*=0);

...

f(100);

f(a,1);

...

f1(a); f1(a,10); f1(a, 10, "Ivan");

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

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

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

Пример. Функции, возвращающие наибольшее из двух значений:

int max (int, int);

char * max (char*, char*);

int max (int, char*);

int max (char*, int);

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

void f(int a, int b, char *c, char *d) {

cout<<max(a,b)<<max(c,d)<<max(a,c)<<max(c,b);

}

При вызове функции max компилятор выбирает соответствующий типу фактических параметров вариант функции. Если точного соответствия не найдено, выполняются продвижения порядковых типов в соответствии с общими правилами, например bool и char в int, float  в double и т. п. Далее выполняются стандартные преобразования типов, например int в double или указателей в void*. Следующим шагом является выполнение преобразований типа, заданных пользователем, а также поиск соответствий за счет переменного числа аргументов функций. Если соответствие на одном и том же этапе может быть получено более чем одним способом, вызов считается неоднозначным и выдается сообщение об ошибке.

Неоднозначность может проявиться:

− при преобразовании типа;

− при использовании параметров-ссылок;

− при использовании аргументов по умолчанию.

Пример. Неоднозначность при преобразовании типа:

# include <iostream.h>

float f(float i) {

cout << "fuction float f(float i)"<<endl; // манипулятор endl переводит строку 

// при использовании   потоков ввода-вывода

return i;

}

doudle f(double i) {

cout << "fuction double f(double i)"<<endl;

return i*2;

}

int main ( ) {

float x = 10.09;

double y = 10.09;

cout << f(x) << endl; // вызывается f(float )

cout << f(y) << endl; // вызывается f(double)

/* cout << f(10) << endl; − неоднозначность: как преобразовать 10: во float или double? */

return 0;

}

Для устранения этой неоднозначности требуется явное приведение типа для константы 10.

Пример неоднозначности при использовании параметров-ссылок: если одна из перегружаемых функций объявлена как Int f(int a, int b), а другая int f( int a, int &b), то компилятор не сможет узнать, какая из этих функций вызывается. так как нет синтаксических различий между вызовом функции, которая получает параметр по значению, и вызовом функции, которая получает параметр по ссылке.

Пример. Неоднозначность при использовании аргументов по умолчанию:

# nclude <iostream.h>

int f(int a) { return a;}

int f(int a, int b =1) {return a*b;}

int main ( ) {

cout << f(10,2); // вызывается f(int, int)

// cout << f(10); - неоднозначность: что вызывается - f(int) или f(int, int)?

return 0;

}

Правила описания перегруженных функций

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

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

Функции не могут быть перегружены, если описание их параметров отличается только модификатором const или использованием ссылки (например, int и  const int  или int и int &).

Задание

Разработать функцию, вычисляющую максимальный элемент:

– в одномерном числовом массиве, состоящем из целых чисел;

– одномерном числовом массиве, состоящим из вещественных чисел;

– массиве строк (для этого массива функция должна находить строку с максимальной длиной).

Шаблоны функций

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

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

Формат простейшей функции-шаблона имеет следующий вид:

template <class Type> заголовок {

/* тело функции */

}

Вместо слова Type может использоваться произвольное имя.

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

template <class A, class B, int i > void f( ) {…}.

Пример

Функция, сортирующая массив из n элементов любого типа.

template <class Type>

void sort_vybor (type *b, int n) {

Type a; // буферная переменная для обмена элементами

for (int i = 0; i<n-1; i++) {

int imin = i;

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

if (b[j] < b[imin]) imin = j;

a = b[i]; b[i] = b[imin]; b[imin] = a;

}

}

Главная функция, вызывающая этот шаблон, может иметь вид:

#include <iostream.h>

template <class Type>

void sort_vybor (type *b, int n);

int main ( ) {

const int n=20;

int i, b[n];

for (int i = 0; i<n; i++) cin >> b[i];

sort_vybor(b, n); // сортируем целочисленный массив

for (int i = 0; i<n; i++) cout << b[i] << "   ";

cout << endl;

double a[5] = {0.22, 17, -0.08, 0.21. 42.5};

sort_vybor(a, 5); // сортируем вещественный массив

for (int i = 0; i<5; i++) cout << a[i] << "   ";

return 0;

}

template <class Type>

void sort_vybor (type *b, int n) {

Type a;

for (int i = 0; i<n-1; i++) {

int imin = i;

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

if (b[j] < b[imin]) imin = j;

a = b[i]; b[i] = b[imin]; b[imin] = a;

}

}

Пример

Явное задание аргументов шаблона при вызове:

template <class X, class Y, class Z> void f (Y, Z);

void g ( ) {

f <int, char*, double> ("Vasia", 3.0);

f <int, char*> ("Vasia", 3.0); //Z определяется как double

f <int> ("Vasia", 3.0); // Y определяется как char*, Z как double

// f ("Vasia", 3.0);  // ошибка: X определить невозможно

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

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

void sort_vybor <int>(int *b, int n) {

// Тело специализированного варианта функции

}


Функции стандартной библиотеки

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

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

Функции ввода/вывода

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

Для использования функций ввода/вывода в стиле С необходимо подключить к программе заголовочный файл <stdio.h> или <cstdio>. При вводе/вывод данные рассматриваются как поток байтов. Физически поток представляет собой файл или устройство, например клавиатуру или дисплей, рассматривающиеся как частный случай файла.

Открытие потока

Работа с потоком начинается с его открытия. Поток можно открыть для чтения и/или записи в двоичном или текстовом режиме. Функция открытия потока имеет формат:

FILE *fopen(const *filename, cоnst char *mode);

Функция возвращает указатель на предопределенную структуру типа FILE в случае успешного открытия потока, в противном случае − на NULL. Первый параметр функции − путь и имя файла в виде с-строки, второй − режим открытия файла:

"r" − файл открывается только для чтения;

"w" − создается пустой файл для записи (если файл существует, он стирается);

"a" − для добавления информации в конец файла;

"r+" − для чтения и записи (файл должен существовать);

"w+" − открывается пустой файл для чтения и записи;

"a+" − для чтения и добавления информации.

Режим открытия может также содержать символы "t" (текстовый режим) или "b" (двоичный режим).

Пример

FILE *f = fopen("d:\\users\\data.dat", "rb+");

Указатель f используется в дальнейших операциях с потоком. Его передают функциям ввода/вывода в качестве параметра.

Ввод/вывод

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

Операции ввода/вывода выполняются, начиная с текущей позиции потока, определяемой положением указателя потока. Указатель устанавливается при открытии на начало или конец файла (в соответствии с режимом открытия) и изменяется автоматически после каждой операции ввода/вывода. Текущее положение указателя можно получить с помощью функций ftell и fgetpos и задать явным способом с помощью функций fseek и fsetpos:

int  fgetpos (FILE *f, fpos_t *pos);

возвращает текущую позицию в файле f и копирует значение по адресу pos. возвращаемое значение имеет тип fpos_t.

int ftell (FILE *f);

возвращает текущую позицию в файле f как длинное целое.

int fseek ( FILE *f, long off, int org);

функция перемещает текущую позицию в файле f на позицию off, отсчитываемую от значения org, которое должно быть одной из трех констант:

SEEK_CUR − от текущей позиции указателя;

SEEK_END − от конца файла;

SEEK_SET − от начала файла.

int fsetpos (FILE *f, const fpos_t *pos);

функция перемещает текущую позицию в файле f на позицию *pos, предварительно полученную с помощью функции fgetpos.

 Чтение и запись потока байтов выполняют функции fread и fwrite:

size_t fread (void *buffer, size_t  size, size_t  count, FILE *stream);

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

size_t fwrite (void *p, size_t  size, size_t  n, FILE *f);

функция записывает n элементов длиной size байт из буфера, заданного указателем p в поток f. Возвращает число записанных элементов.

Чтение символа из потока выполняют функции getc и fgetc; из стандартного потока stdingetchar:

int getc (FILE *f);

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

int fgetc (FILE *f);

то же, что и предыдущая функция.

int getchar (void);

возвращает очередной символ в форме int из стандартного ввода. Если символ не может быть прочитан, то возвращается EOF.

Запись символа в поток putc, fputc; в стандартный поток stdoutputchar.

int putc (int ch, FILE *f);

записывает символ ch в поток f. При ошибке возвращает значение EOF, иначе − записанный символ.

int fputc (int ch, FILE *f);

аналогична предыдущей функции.

int putchar (int ch);

выводит символ ch на стандартное устройство вывода; если вывод успешен, возвращает значение ch, иначе EOF.

Чтение строки из потока fgets; из стандартного потока stdingets:

char * gets (char *s);

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

char *fgets (char *s, int n, FILE *f);

читает не более n-1 байт из потока f в строку s, прекращая чтение при обнаружении символа новой строки или конца файла. Символ новой строки не отбрасывается, а помещается в конец строки. Прочитанная строка дополняется ограничителем строки ('\0'). При обнаружении ошибки или конца файла возвращается NULL, в противном случае − указатель на s.

Запись строки в поток fputs; в стандартный поток stdoutputs.

int fputs (const char *s, FILE *f);

записывает строку символов s в поток f. Символ конца строки не записывается. При ошибке возвращает значение EOF, иначе − неотрицательное число.

int  puts (const char *s);

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

Форматированный ввод из потока fscanf; из стандартного потока stdinscanf, из строки − sscanf.

int fscanf (FILE *f, const char *fmt [, par1, par2, ...]);

вводит строку параметров par1, par2, ...в формате, определенном строкой fmt из файла f. Возвращает число переменных, которым присвоено значение.

int scanf (const char *fmt [, par1, par2, ...]);

вводит строку параметров par1, par2, ...в формате, определенном строкой fmt со стандартного устройства ввода. Возвращает число переменных, которым присвоено значение.

int sscanf (const char *buf, char *format [, par1, par2, ...]);

вводит данные из строки, переданной ей первым параметром; format − строка формата, в соответствии с которым происходит преобразование данных.

Форматированный вывод в поток fprintf; в стандартный поток stdoutprintf; в строку − sprintf.

int fprintf (FILE *f, const char *fmt, ...);

записывает в  поток f переменные, список которых обозначен многточием, в формате fmt. Возвращает число записанных символов.

int printf (const char *fmt, ...);

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

int sprintf (char *buffer, const char *format [, argument, ...]);

выводит в строку buffer значения переменных из списка в формате format.

Закрытие потока

Поток закрывается либо при завершении программы, либо явным образом с помощью функции fclose:

int fclose (FILE *);

Обработка ошибок

При работе с файлами используются функции feof и ferror:

int feof (FILE *) − возвращает не равное нулю значение, если достигнут конец файла; в противном случае возвращает нуль.

 int ferror(FILE *) − возвращает не равное нулю значение, если обнаружена ошибка ввода/вывода; в противном случае возвращает нуль.

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

#include <iostream.h>

#include <stdio.h>

#include <stdlib.h>

# include <string.h>

int main ( ) {

FILE *fi,fo;

if ((fi=fopen("d:\\users\\file.txt", "r")) = =0) {

cout << "Ошибка открытия входного файла"; return 1;}

if ((fo=fopen("d:\\users\\binfile.out", "w+b")) = =0) {

cout << "Ошибка открытия выходного файла"; return 1;}

const int dl=80;

char s[dl];

struct {

char type[20];

int opt, rozn;

char comm[40];

} mon;

int kol =0; // количество записей в файле

while (fgets(s, dl, fi)) { // преобразование строки s в  структуру mon

strncpy (mon.type, s, 19); // копируем первые 20 символов из s в mon.type

mon.type[19]='\0';

mon.opt=atoi(&s[20]); // превращаем строку из пяти символов в целое число

mon.rozn=atoi(&s[25]);

strncpy(mon.comm, &s[30], 40); // копируем в поле комментария оставшиеся

// символы

fwrite(&mon, sizeof mon,1, fo);

kol++;}

fclose (fi);

fclose (fo);

rerurn 0;

}

Задание

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

– просмотр справочника;

– поиск информации по телефону или фамилии;

– добавление новых данных;

– редактирование данных;

– удаление данных;

Функции работы со строками и символами

Строка представляет собой массив символов, заканчивающийся нуль-символом. В С++ есть две возможности работы  со строками: функции, унаследованные из библиотеки С (заголовочный файл <string.h> или <cstring>), и библиотечный класс С++ string.

Библиотека С содержит функции копирования строк (strcpy, strncpy), сравнения (strcmp, strncmp), объединения строк (strcat, strncat), поиска подстроки (strstr), поиска вхождения символа (strchr, strnchr, strbrk) определения длины  строки strlen и другие.

char *strcat (char *s1, char *s2);

добавляет s2 к s1 и возвращает s1. В конец результирующей строки добавляется нуль-символ.

char *strchr (char *s, int ch);

возвращает указатель на первое вхождение символа ch в строку s, если его нет, возвращает NULL.

int *strcmp ( char *s1, char *s2);

сравнивает строки ss2 и возвращает отрицательное (если s1<s2), нулевое ( s1=s2) или положительное значение (s1>s2 ).

char *strcpy (char *s1, char *s2);

копирует s2 в s1и возвращает s1.

size_t strlen(char*s);

возвращает длину строки(без завершающего символа).

char *strncat (char *s1, char *s2, size_t  n);

добавляет не более n символов из s2 к s1 и возвращает s1.

int strncmp (char *s1, char *s2, size_t  n);

сравнивает s1 и первые n символов строки s1. Возвращает такие же значения, что и strcmp.

char *strncpy (char *s1, char *s2, size_t  n);

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

char *strchr (char *s, int ch);

возвращает указатель на первое вхождение символа ch в s справа, если его нет, возвращает NULL.

char *strstr (char *s1, char *s2);

выполняет поиск первого вхождения подстроки s2 в s1. В случае  удачного поиска возвращает указатель на элемент из s1, с которого начинается s2, и NULL − в противном случае.

double strtod (const char *str, char **end);

преобразует строку символов в числовое значение и возвращает его. При переполнении возвращает +/-HUGE_VAL. При невозможности выполнить преобразование или исчезновении порядка возвращает 0. В обоих последних случаях end указывает на символ, на котором преобразование завершается.

В заголовочном файлe <stdlib.h> содержатся полезные функции преобразования строк в числа:

double atof (const char *p);

преобразует переданную строку в double.

int atoi ( const char *p);

преобразует переданную строку в int.

long atol ( const char *p);

преобразует переданную строку в long.

Пробелы и табуляции в начале строки пропускаются. Преобразование прекращается при встрече недопустимого символа или конца строки. Если строку нельзя преобразовать в число, возвращается 0. Если число выходит за пределы диапазона данного типа, переменной  errno (заголовочный файл <cerrno>) присваивается значение ERANGE и возвращается допустимое число.

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

int sprintf (char *buffer, const char *format [, argument,...]);

выводит в строку buffer значения переменных, перечисленных в списке, в формате, определенном строкой format.

Для работы с символами в  стандартной библиотеке (заголовочный файл <ctype.h>) есть следующие функции (табл. 4):

Таблица 4

Функции  для обработки символов

Имя функции

Проверка на принадлежность символа множеству

isalpha

букв  (A-Z, a-z, 0-9)

isalnum   

букв и цифр(A-Z, a-z, 0-9)

isdigit 

цифр (0-9)

isxdigit

шестнадцатеричных цифр (0-9, AF, af)

iscntrl   

управляющих символов (с кодами 0..31 и 127)

islower

букв нижнего регистра (a-z)

isupper

букв верхнего регистра (A-Z)

isgraph   

печатаемых символов, кроме пробела  

ispunct

знаков пунктуации

isprint   

печатаемых символов   

isspace 

символов-разделителей   

Функции принимают величину типа int и возвращают результат true, если условие выполняется.

Следующие функции переводят символ ch в нижний и верхний регистр соответственно:

int tolower (int ch);

int to upper (int ch);

Задание

Написать программу – «Текстовый редактор». Предусмотреть следующие функции:

– просмотр текста;

– добавление новых строк;

– редактирование строк;

– удаление строк.

Математические функции

С++ унаследовал из С стандартные математические функции, описание которых находится в заголовочном файле <math.h> (<cmath>). Они позволяют получить абсолютное значение (abs, fabs), округленное число (ceil, floor), квадратный корень (sqrt), степень (pow), значения тригонометрических функций (sin, cos, tan, asin, acos, atan и др.), экспоненту (exp), логарифм (log, log10), дробную и целую части числа (modf), остаток от деления (fmod) и ряд других.

Ошибки индицируются установкой errno из <errno.h> в значение EDOM для ошибок из области определения и ERANGE для ошибок выхода за пределы определения.

Функции

int abs (int num);

long labs(long int num);

double fabs(double x);

возвращают модуль целого и дробного числа соответственно.

Функции

double acos(double x);

double asin(double x);

double atan(double x);

double atan2(double x);

возвращают значение арккосинуса, арксинуса, арктангенса и арккотангенса соответственно. Значение возвращается в радианах.

double cos(double x);

double sin(double x);

double tan(double x);

double tan2(double x);

возвращают косинус, синус, тангенс, котангенс аргумента, заданного в радианах.

div_t div (int numerator, int denomerator);

делит целое numerator на целое denomerator, возвращая результат в структуре, содержащей частное от деления и остаток:

struct div_t {

int quot;

int rem;

}

double exp(double x);

возвращает результат возведения е в степень х.

double floor (double x);

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

double log (double x);

double log10 (double x);

возвращают значение натурального и десятичного логарифмов соответственно.

double modf (double x, double *intptr);

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

double pow (double x, double y);

вычисляет степень х основания у.

int rand(void);

генерирует случайное число в диапазоне от 0  до RAND_MAX (константа описана в <stdlib.h>. Для инициализации генератора псевдослучайных чисел используется функция randomize( ) – ее необходимо поставить в начале программы.

double sqrt(double x);

возвращает квадратный корень из х.

Задание

Написать программу, моделирующую игру в кости.

Директивы препроцессора

Препроцессором называется первая фаза компиляции. Инструкции препроцессора называются директивами. Они должны начинаться с символа #.

Директива  #include

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

Директива #include является простейшим средством согласованности объявлений в различных файлах, она включает в  них информацию об интерфейсе из заголовочных файлов.

Заголовочные файлы имеют расширение .h и могут содержать:

– определения типов, констант, встроенных функций, шаблонов, перечислений;

– объявления функций, данных, имен, шаблонов;

– пространства имен (см. далее);

–  директивы препроцессора;

– комментарии.

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

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

Директива #define

Данная  директива определяет подстановку в тексте программы. Она используется для определения:

- символических констант:

#define имя   текст_подстановки;

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

#define имя(параметры)  текст_подстановки;

- символов, управляющих условной компиляцией. Эти символы используются вместе с директивами #ifdef   #ifndef. Формат:

#define имя;

Примеры

#define VASIA "Василий"

#define MAX(x,y)  ((x)>(y)? (x): (y))

Имена рекомендуется писать большими буквами, чтобы зрительно отличать их от имен переменных и функций. С макросом работают как с обычной функцией, например, макрос MAX может быть вызван как y=MAX(sum1,sum2); в тексте программы он будет заменен на выражение y= ((sum1)>(sum2)? (sum1): (sum2));

!!! Макросы и символические константы унаследованы из языка С, при написании программ на С++ лучше их избегать. Вместо символических констант лучше использовать const и enum, а вместо макросов − встроенные функции или шаблоны. Встроенную функцию определяют с помощью модификатора inline, который помещается перед типом функции. Код встроенной функции помещается в каждую точку вызова, это сокращает расходы на вызов функции.

Директивы условной компиляции

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

Формат директивы #if:

#if константное_выражение

...

[ #elif константное_выражение

...]

[ #elif константное_выражение

...]

[# else

...]

#endif

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

Пример

#if 0   //временно закомментирован фрагмент кода

int i, j;

double x,y;

#endif

Директивы #ifdef и  #ifndef часто применяются для того, чтобы управлять компиляцией в зависимости от того, определен ли с помощью директивы #define указанный в ней символ (хотя бы как пустая строка).

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

#ifndef HEADER_INCLUDED

# include "myheader.h"

#define HEADER_INCLUDED

#endif

Директива #undef

Данная директива удаляет определение символа.

 Предопределенные макросы

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

_DATE_ − содержит строку с текущей датой в формате день месяц год.

_FILE_ − содержит строку с полным именем текущего файла.

_LINE_ − текущая строка исходного текста.

_TIME_ − текущее время.

Пример

printf("Текущая дата -%s\n", _DATE_);

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

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

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

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

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

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

Класс. Элементы структур, объединений и классов (за исключением статических элементов) являются видимыми лишь в пределах класса.

Поименованная область. С++ позволяет явным образом задать область определения имен как часть глобальной области с помощью оператора namespace.

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

struct Node {

int Node;

int i;

} Node;

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

– имена переменных, функций, типов, определенных пользователем, и констант перечислений в пределах одной области видимости;

– имена типов перечислений, структур, классов и объединений;

– имена элементов каждой структуры, класса и объединения;

– имена меток.

Внешние объявления

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

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

– определить ее в одном модуле как глобальную;

– в других модулях объявить ее как внешнюю с помощью модификатора extern.

Другой способ − поместить это объявление в заголовочный файл и включить его (файл) в нужные модули.

Пример. Описание двух глобальных переменных в файлах one.cpp и two.cpp с помощью заголовочного файла.

// my_header.h

extern int a;

extern double b;

...

____________

// one.cpp

#include "my_header.h"

int a;

...

____________

// two.cpp

#include "my_header.h"

double b ;

...

____________

Обе переменные видны в файлах one.cpp и two.cpp.

Если переменная описывается как static, область ее действия ограничивается файлом, в котором она описана.

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

Поименованные области

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

Объявление поименованной области имеет формат:

namespace [имя_области] {/*объявления*/}

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

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

Пример

namespace demo {

int i=1;

int k=0;

void func1(int);

void func2(int) {/*...*/}

}

namespace demo { // расширение

// int i=2;  // неверно − двойное определение

void func2(int); // повторное объявление

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

void demo::func1(int) {/*...*/}

Объекты, объявленные внутри области, являются видимыми с момента объявления. К ним можно явно обращаться с помощью имени области и оператора доступа к области видимости, например:

demo:: i=100; demo:: func2(10);

Если имя часто используется вне своего пространства, можно объявить его доступным с помощью оператора using:

using demo:: i;

После этого можно использовать имя без явного задания области.

Если требуется сделать доступными все имена из какой-либо области, используется оператор using namespace:

using namespace demo;

Операторы using и using  namespace можно использовать и внутри объявления поименованной области, чтобы сделать в ней доступными объявления из другой области:

namespace dam {

using demo:: i;

...

}

Задание

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


БИБЛИОГРАФИЧЕСКИЙ СПИСОК

  1.  Павловская Т. А. С / C++. Программирование на языке высокого уровня. – СПб, 2002.
  2.  Павловская Т. А. С / C++. Структурное программирование: практикум.  – СПб, 2002.
  3.  Павловская Т. А. С / C++. Объектно-ориентированное программирование: практикум. – СПб, 2002.
  4.  Культин Н. Б. С++ Builder. – СПб, 2004.
  5.  Кибардин А. В. Программирование на языке С++. Часть 1. Структурное программирование: методические указания. – Екатеринбург, 2006.
  6.  Кибардин А. В. Программирование на языке С++. Часть 2. Модульное программирование: методические указания. – Екатеринбург, 2006.


Для заметок

Учебное издание

Кибардин Алексей Владимирович

программирование на языке С++

часть 1

основы структурного и модульного программирования

Учебно-методическое пособие

для студентов

специальности 220401 – «Мехатроника»

   

Редактор С. В. Пилюгина

Подписано в печать 10.03.11  Формат 60x84/16

Бумага офсетная   Усл. печ. л. 3,7

Тираж 50 экз.   Заказ 8

Издательство УрГУПС

620034, Екатеринбург, ул. Колмогорова, 66

                  




1. рефератов по философии 1
2. Курсова робота Методи соціальнопедагогічної діяльності
3. і. В вектори компланарні
4.  Джон Стюарт Мілль про свободу порядок і соціальний прогрес
5. на тему- Понимание мимики и жестов Выполнил- ст
6. Дифференциация звуков и букв ЧЦ
7. Иными словами при возможности использовать методический инструментарий задающий способы измерения перем
8. то со стороны. Приезжающие перестали спрашивать разрешения не фотографирование записи съемку и их отношен
9. I. Показной евангелизмГлава II.
10. Етнографічний район Карпат
11. Класифікація озер- за походженням озера поділяють на- екзогенні та ендогенні
12. Пояснительная записка к экзаменационному материалу по химии за курс 10 класса в 2012 ~ 2013 году Цель экзамена- п
13. Позволить себе иметь ~ это первый шаг к исполнению задуманного Совсем чутьчуть до поезда
14. тема конституционного права зарубежных стран
15. .11.13. Планирование обследования пациента с подозрением на заболевания органов мочевыделения Для исследов
16. розовую окраску При росте лактозоположительных бактерий их колонии окрашиваются в темнокрасный цвет с мет
17. Статья Системный подход к построению основных терминов и понятий образования
18. желтый Реакция ~ щелочная Относительная плотность 1022 Прозрачность ~ неполная Белок ~ 133 г-л
19. Тема уроку Розв'язування задач
20. Чародеи Действующие лица- Иван Пухов Саша Пестряков Алена Анжелика Петр Петрович Сата