Презентацию к лекции Вы можете скачать здесь.
Функция - это законченный фрагмент кода, к которому можно обратиться по имени (вызвать функцию). Функция может получать аргументы и возвращать в вызывающий код вычисленное в ней значение. Программу, состоящую из функций, можно рассматривать в укрупненном виде - на уровне их взаимодействия.
Использование функций является первым шагом к повышению степени
Разделение программы на функции позволяет избежать
Любая программа на С++ состоит из функций, одна из которых должна иметь имя main (с нее начинается выполнение программы).
Функция начинает выполняться в момент вызова. Любая функция должна быть объявлена и определена. Как и для других величин, объявлений может быть несколько, а определение только одно.
Объявление функции (прототип, заголовок, сигнатура) задает ее имя, тип
Определение функции содержит, кроме объявления, тело функции, представляющее собой
[ класс ] тип имя ([ список_параметров ])[throw ( исключения )]{ тело функции }
Рассмотрим составные части определения.
extern и static :extern - глобальная видимость во всех модулях программы (по умолчанию);static - видимость только в пределах модуля, в котором определена функция.void.В определении, в объявлении и при вызове одной и той же функции типы и порядок следования параметров должны совпадать.
На имена параметров ограничений по соответствию не накладывается.
Функцию можно определить как встроенную с помощью модификатора , который рекомендует компилятору вместо ставится перед типом функции. Он применяется для коротких функций, чтобы снизить накладные расходы на вызов.
носит рекомендательный характер и выполняется компилятором по мере возможности.
Тип
Для вызова функции в простейшем случае нужно указать ее имя, за которым в круглых скобках через запятую перечисляются имена передаваемых аргументов.
void, она может входить в состав выражений или, в частном случае, располагаться в правой части
Пример функции, возвращающей сумму двух целых величин:
#include <iostream>
using namespace std;
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); // вызов функции
}
int sum(int a, int b) // определение функции
{return (a+b);}
Пример функции, выводящей на экран поля переданной ей структуры:
#include <iostream>
using namespace std;
struct Worker
{
char fio[30];
int date, code;
float salary;
};
void print_worker(Worker); //объявление функции
int main()
{
Worker stuff[100];
... /* формирование массива stuff */
for (int i = 0; i<100; i++)
print_worker(stuff[i]); /* вызов функции */
}
void print_worker(Worker w) //определение функции
{cout << w.fio << ' '
<< w.date << ' '
<< w.code << ' '
<< w.salary;}
Все величины, описанные внутри функции, а также ее параметры, являются локальными. Областью их действия является функция. При вызове функции, как и при входе в любой блок, в стеке выделяется память под локальные
При выходе из функции соответствующий участок :
#include <iostream>
using namespace std;
void f(int a)
{
cout << "n m\n";
while (a--)
{
static int n = 0;
int m = 0;
cout << n++ << ' '
<< m++ << '\n';
}
}
int main(){ f(3);}
n размещается в сегменте данных и инициализируется один раз при первом выполнении оператора, содержащего ее определение. Программа выведет на экран:
n m 0 0 1 0 2 0
При совместной работе функции должны обмениваться информацией. Это можно осуществить с помощью глобальных переменных, через параметры и через возвращаемое функцией значение.
Нужно стремиться к тому, чтобы функции были максимально независимы, а их интерфейс полностью определялся
Возврат из функции в вызвавшую ее функцию реализуется оператором
return [ выражение ];
Функция может содержать несколько операторов return. Если функция описана как void, выражение не указывается. Выражение, указанное после return, неявно преобразуется к типу возвращаемого функцией значения и передается в точку вызова функции.
Примеры:
int f1(){ return 1; } //правильно
void f2(){ return 1; } //неправильно, f2 не должна возвращать значение
double f3(){ return 1; } //правильно, 1 преобразуется к типу double
ВНИМАНИЕ
Нельзя возвращать из функции указатель на локальную переменную.
Пример:
int* f()
{
int a = 5;
return a; // нельзя!
}
Механизм параметров является основным способом обмена информацией между вызываемой и вызывающей функциями. В операторе вызова функции записывают аргументы функции, а в заголовке описания функции перечисляют параметры.
В С++
При передаче по значению объявление параметра похоже на
тип имя
Такой параметр считается отдельной
параметр = выражение
Выражение вычисляется; если тип полученного значения не соответствует типу параметра, то выполняется (если это возможно)
Никакие изменения значения параметра внутри функции не отражаются на значении переменной-аргумента, так как параметр является
При передаче по ссылке объявление параметра представляет собой объявление ссылки без инициализации:
тип имя
Параметр-ссылка локальна в функции: ни в заголовке, ни в ее теле не должно быть объявлено других параметров и переменных с таким же именем. Инициализация параметра-ссылки выполняется во время вызова функции. При этом способе передачи параметров в качестве аргумента может задаваться только L -значение.
В самом простом варианте на месте аргумента, соответствующего параметру-ссылке, задается
Этот способ
void swap( int a, int b ) // определение функции обмена
{
int t = a; a = b; b = t; }
// ...
int x = 5, y = 6;
swap( x, y ); // вызов функции обмена
Функция фактически работает с исходными переменными x и y. Изменение функцией нелокальных переменных называется побочным эффектом.
Использование параметров-ссылок вместо передачи по значению более эффективно, поскольку не требует времени и памяти для копирования аргументов в
Пример передачи параметров:
#include <iostream>
using namespace std;
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;
}
void f(int i, int* j, int k)
{ i++; (*j)++; k++; }
Результат работы программы:
i j k 1 2 3 1 3 4
Первый параметр ( i ) передается по значению. Его изменение в функции не влияет на исходное значение. Второй параметр ( j ) передается по адресу с помощью указателя, при этом для передачи в функцию адреса k ) передается по адресу с помощью ссылки.
Если требуется запретить изменение параметра, используется модификатор :
int f(const char*); char* t(char* a, const int* b);
СОВЕТ
Рекомендуется указывать
Параметры, передаваемые в функцию, могут быть любого типа (например, вещественного, структурой, перечислением, объединением, указателем), кроме массива или функции, которые передаются с помощью указателей.
Массив всегда передается по адресу. При этом информация о количестве элементов массива теряется, и следует передавать его
#include <iostream>
using namespace std;
int sum(const int* mas, const int n);
int const n = 10;
void main()
{
int marks[n] = {3, 4, 5, 4, 4};
cout << "Сумма элементов массива: "
<< sum(marks, n);
}
int sum(const int* mas, const int n)
/*варианты: int sum(int mas[], int n) или */
/*int sum(int mas[n], int n) (n должна быть константой) */
{ int s = 0;
for (int i = 0 ; i<n; i++) s += mas[i];
return s;}
При передаче многомерных массивов все размерности, если они не известны на этапе
Внутри функции массив интерпретируется как одномерный, а его индекс пересчитывается в программе. В приведенном ниже примере с помощью функции подсчитывается сумма элементов двух двумерных массивов. b известна на этапе a память выделяется динамически:
#include <cstdio> using namespace std;
int sum(const int *a, const int nstr, const int nstb);
void 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));
}
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;
}
Для того чтобы работать с двумерным массивом естественным образом, можно применить альтернативный способ выделения памяти:
#include <iostream>
using namespace std;
int sum(const int **a, const int nstr, const int nstb);
void main()
{
int nstr, nstb;
cin >> nstr >> nstb;
int **a;
a = new int* [nstr];
for (int i = 0; i<nstr; i++)
a[i] = new int [nstb];
/* * формирование матрицы a */
cout << sum(a, nstr, nstb);
}
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][j];
return s;
}
В этом случае память выделяется в два этапа: сначала под столбец указателей на строки
Функцию можно вызвать через указатель на нее. Для этого объявляется указатель соответствующего типа и ему с помощью операции взятия адреса присваивается адрес функции:
void f(int a ){ /* * */ } //определение функции
void (*pf)(int); //указатель на функцию
...
pf = f; /* указателю присваивается адрес */
/*функции (можно написать pf = f;) */
pf(10); /* функция f вызывается через указатель pf */
/*(можно написать (*pf)(10) ) */
Для того чтобы сделать программу более читаемой, при описании указателей на функции используют переименование типов ( typedef ). Можно объявлять массивы указателей на функции (это может быть полезно, например, при реализации меню):
/* описание типа PF как указателя на функцию с одним параметром типа int */
typedef void (*Pf)(int);
/* описание и инициализация массива указателей */
PF menu[]={new, open, save}
menu[1](10); //вызов функции open
Указатели на функции передаются в
void fun(PF pf) /* функция fun получает в качестве*/
/* параметра указатель типа PF */
{* pf(10); *} //вызов функции, переданной через указатель
Тип указателя и тип функции, которая вызывается посредством него, должны совпадать в точности.
Чтобы упростить
int f(int a, int b = 0); void f1(int, int = 100, char* = 0); void err(int errValue = errno); // errno - глобальная переменная f(100); f(a, 1); // варианты вызова функции f // варианты вызова функции f1 f1(a); f1(a, 10); f1(a, 10, "Vasia"); f1(a,,"Vasia") // неверно!
Любая программа на С++ содержит обращения к
Чтобы использовать их в программе, требуется с помощью # включить в исходный текст программы
Сами библиотечные функции хранятся в скомпилированном виде и подключаются к программе на этапе
Ниже приведен краткий обзор основных функций.
Ввод/вывод в С++ реализуется либо с помощью функций, унаследованных от библиотек С, либо с помощью потоков С++. Смешивать эти два способа в одной программе не рекомендуется.
Для использования функций ввода/вывода в стиле С необходимо подключить к программе
Работа с потоком начинается с его открытия. Поток можно открыть для чтения и/или записи в двоичном или текстовом режиме. Функция открытия потока имеет формат:
FILE* fopen(const char* filename, const char* mode);
При успешном открытии потока функция возвращает указатель на предопределенную структуру типа FILE, содержащую всю необходимую для работы с NULL в противном случае. Первый параметр - имя открываемого файла в виде С-строки, второй - режим
"r" - файл открывается для чтения;
"w" - открывается пустой файл для записи (если файл существует, он стирается);
"a+" - файл открывается для добавления информации в его конец;
"r+" - файл открывается для чтения и записи (файл должен существовать);
"w+" - открывается пустой файл для чтения и записи (если файл существует, он стирается);
"a" - файл открывается для чтения и добавления информации в его конец.
Режим открытия может также содержать символы t (b (двоичный режим), отличающиеся обработкой символов перехода на новую строку.
По умолчанию файл открывается в текстовом режиме, при котором комбинация символов "
Пример:
FILE *f = fopen("d:\\cpp\\data.txt", "rb+");
Указатель f используется в дальнейших операциях с потоком. Его передают функциям ввода/вывода в качестве параметра.
При открытии потока с ним связывается область памяти, называемая буфером. При выводе вся информация направляется в буфер и накапливается там до заполнения буфера или до закрытия потока. Чтение осуществляется блоками, равными размеру буфера, и данные читаются из буфера.
Существует пять предопределенных потоков, которые открываются в начале работы программы: stdin, stdout, stderr, стандартный дополнительный поток stdaux и стандартная печать stdprn.
Ввод/вывод в поток можно осуществлять различными способами: в виде последовательности байтов, в виде символов и строк или с использованием форматных преобразований. Для каждого вида операций определен свой набор функций.
Операции ввода/вывода выполняются начиная с текущей позиции потока, определяемой положением указателя потока. Указатель устанавливается при открытии на начало или
Ниже перечислены основные функции ввода/вывода потока.
fread и fwrite.getc, fgetc, из стандартного потока stdin - getchar.putc, fputc, в стандартный поток stdout - putchar.fgets, из стандартного потока stdin - gets.fputs, в стандартный поток stdout - puts.fscanf, из стандартного потока stdin - scanf, из строки - sscanf.fprintf, в стандартный поток stdout - printf, в строку - sprintf.Поток закрывается либо при завершении программы, либо явным образом с помощью функции fclose:
int fclose(FILE*);
Перед закрытием
Приведенная ниже программа построчно считывает данные из s, формирует из них структуру mon и записывает ее в двоичном режиме в выходной файл. Далее иллюстрируется считывание из этого файла произвольной записи.
#include <cstdio>
#include <cstdlib>
#include <cconio>
#include <iostream>
using namespace std;
int main()
{
FILE *fi, *fo;
if((fi = fopen("d:\\c\\binfile.txt", "r"))==0)
{
cout << "Ошибка открытия входного файла";
return 1;
};
if((fo = fopen("d:\\c\\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;
while (fgets(s, dl, fi))
{
// Преобразование строки в структуру:
strncpy(mon.type, s, 19); //Описание strncpy см. в след. разделе
mon.opt = atoi(s[20]); //Описание atoi см. в след. разделе
mon.rozn = atoi(s[25]);
strncpy(mon.comm, s[30], 40);
fwrite(mon, sizeof mon, 1, fo);
}
fclose(fi);
int i; cin >> i; // Номер записи
// Установка указателя текущей позиции файла на запись i
fseek(fo, (sizeof mon)*i, SEEK_SET);
fread(mon, sizeof mon, 1, fo);
cout << "mon.type "
<< mon.type << " opt "
<< mon.opt << " rozn "
<< mon.rozn << endl;
fclose(fo);
}
Допустим, что в файле хранятся сведения о
Строка представляет собой С++ есть две возможности работы со строками: функции, унаследованные из библиотеки С (С++ string, предоставляющий более широкие возможности представления, обработки и контроля строк.
Библиотека С содержит функции копирования строк ( , ), сравнения ( , ), объединения строк ( , ), поиска ),
поиска вхождения символа ( , , ), определения ) и др.
В
double atof(const char* p); //преобразует переданную строку в double int atoi(const char* p); //преобразует переданную строку в int long atol(const char* p); //преобразует переданную строку в long
Пробелы и табуляции в начале строки пропускаются. Преобразование прекращается при встрече недопустимого символа или конца строки. Если строку нельзя преобразовать в число, возвращается 0.
Для работы с символами в
| Имя | Проверка на принадлежность символа множеству |
|---|---|
isalnum |
букв и цифр (A-Z, a-z, 0-9) |
isalfa |
букв (A-Z, a-z) |
iscntrl |
|
isdigit |
цифр (0-9) |
isgraph |
Печатаемых символов, кроме пробела ( isalfa | isdigit | ispunct ) |
islower |
букв нижнего регистра (a-z) |
isprint |
Печатаемых символов |
ispunct |
знаков пунктуации |
isspace |
символов-разделителей |
isupper |
букв верхнего регистра (A-Z) |
isxdigit |
шестнадцатеричных цифр (A-F, a-f, 0-9) |
С++ унаследовал из С стандартные , ), , ), квадратный корень ( sqrt ), степень ( pow ),
значения тригонометрических функций ( sin, , tan, sinh, и т. п.), ), логарифм ( log, log10 ), дробную и целую часть числа ( modf ), fmod ) и некоторые другие.
С увеличением объема программы становится неудобно хранить ее в одном файле. Разбиение программы на функции является первым шагом в повышении уровня
Получившиеся в результате
Модуль содержит данные и функции их обработки. Для того чтобы использовать модуль, нужно знать только его интерфейс. Интерфейсом модуля являются заголовки всех функций и описания доступных извне
С++ поддерживается с помощью директив
Директива # <имя_файла> вставляет содержимое указанного файла в ту точку исходного файла, где она записана. Включаемый файл также может содержать #.
Поиск файла, если не указан
# является простейшим средством обеспечения согласованности объявлений в различных файлах, включая в них информацию об интерфейсе из .h и могут содержать:
В
Директива # определяет подстановку в тексте программы. Она используется для определения:
/* Все вхождения имени заменяются на текст подстановки */ #define имя текст_подстановки
#define имя( параметры ) текст_подстановки
#ifdef и #ifndef. Формат:#define имя
Примеры:
#define M 1000 #define Vasia "Василий Иванович" #define MAX(a,b) ((x)>(y)?(x):(y)) #define __cplusplus
Параметры используются при макроподстановке, например, если в тексте программы используется вызов макроса y = MAX(sum1, sum2);, он будет заменен на
y=((sum1)>(sum2)?(sum1):(sum2));
Использования
Каждый программный объект имеет
Блок. Идентификаторы, описанные внутри блока, являются локальными. Область
Файл. Идентификаторы, описанные вне любого блока, функции, класса или пространства имен, имеют глобальную видимость и время жизни и могут использоваться с момента их определения.
Функция. Единственными идентификаторами, имеющими такую
Прототип функции. Идентификаторы, указанные в списке параметров прототипа (
Класс. Элементы структур, объединений и классов (за исключением
Поименованная область. С++ позволяет явным образом задать .
В каждой
struct Node
{
int Node;
int i;
}Node;
В данном случае противоречия нет, поскольку имена С++ определено четыре раздельных класса идентификаторов.
typedef ) и констант перечислений в пределах одной Любая функция автоматически видна во всех модулях программы. Если требуется ограничить .
Для того чтобы сделать доступной в нескольких модулях переменную или константу, необходимо:
extern .Другой способ - поместить это объявление в
Все описания одной и той же переменной должны быть согласованы.
Пример описания двух one. и two. с помощью заголовочного файла my_header.h:
// 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; ...
Если переменная описана как , область ее действия ограничивается файлом, в котором она описана.
Поименованные области служат для логического группирования объявлений. Чем больше программа, тем более актуально использование поименованных областей. Простейшим примером применения является отделение кода, написанного одним человеком, от кода, написанного другим.
Объявление поименованной области (ее также называют пространством имен) имеет формат:
namespace [ имя_области ]{ /* Объявления */}
Поименованная область может объявляться неоднократно, причем последующие объявления рассматриваются как расширения предыдущих.
Если имя области не задано, компилятор определяет его самостоятельно с помощью уникального идентификатора, различного для каждого модуля. Объявление объекта в непоименованной области равнозначно его описанию как глобального с модификатором .
Нельзя получить доступ из одного файла к элементу неименованной области другого файла.
Пример.
namespace demo{
int i = 1;
int k = 0;
void func1(int);
void func2(int) { /* ... */}
}
namespace demo{ // Расширение
// int i = 2; Неверно - двойное определение
void func1(double); // Перегрузка
void func2(int); // Верно (повторное объявление)
}
В объявлении поименованной области могут присутствовать как объявления, так и определения. Логично помещать в нее только объявления, а определять их позднее с помощью имени области и оператора доступа к
void demo::func1(int) { /* ... */}
Это применяется для разделения интерфейса и реализации. Таким способом нельзя объявить новый элемент пространства имен.
Объекты, объявленные внутри области, являются видимыми с момента объявления. К ним можно явно обращаться с помощью имени области и оператора доступа к
demo::i = 100; demo::func2(10);
Если имя часто используется вне своего пространства, можно объявить его доступным с помощью оператора using:
using demo::i;
После этого можно использовать имя без явного указания области.
Если требуется сделать доступными все имена из какой-либо области, используется оператор using :
using namespace demo;
Операторы using и using можно использовать и внутри объявления поименованной области, чтобы сделать в ней доступными объявления из другой области.
Объекты . Например, объявления стандартных средств ввода/вывода С в
// stdio.h
namespace std
{
int printf(const char**);
...
}
using namespace std;
Это обеспечивает совместимость сверху вниз. Для тех, кто не желает присутствия неявно доступных имен, определен новый
// cstdio
namespace std{
int printf(const char**);
...
}
Если в программу включен файл <cstdio>, нужно указывать имя пространства имен явным образом:
std::printf("*");
Механизм пространств имен вместе с директивой # обеспечивают необходимую при написании больших программ гибкость путем сочетания логического группирования связанных величин и ограничения доступа к ненужным средствам.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.