Использование прототипов функции. Прототипы функций

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

[СпецификаторКлассаПамяти] [СпецификаторТипа] ИмяФункции ([СписокФормальныхПараметров]) [,СписокИменФункций];

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

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

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

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

1. Функция возвращает значение типа, отличного от int.

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

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

В прототипе можно указать, что число параметров функции переменно, или что функция не имеет параметров.

Если прототип задан с классом памяти static, то и определение функции должно иметь класс памяти static. Если спецификатор класса памяти не указан, то подразумевается класс памяти extern.

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

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

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

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

Прототип функции имеет вид:

тип_результата имя_функции (список ) ;

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

Пример описания функции fun , которая имеет три параметра типа int , один параметр типа double и возвращает результат типа double :

double fun(int, int, int, double);

Пример описания для вышеприведенной функции Min:

int Min (int x, int y);

int Min (int, int);

Конец работы -

Эта тема принадлежит разделу:

Структура программы на языке СИ. Этапы выполнения программы

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

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

Что будем делать с полученным материалом:

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

Все темы данного раздела:

Алфавит языка Си
Алфавит языка Си включает: - прописные и строчные буквы латинского алфавита, а также знак подчеркивания (код ASCII 95); - арабские цифры от 0 до 9; - специальные символы:

Идентификаторы и ключевые слова
Идентификатор (в дальнейшем, для краткости - ID) – это имя программного объекта (константы, переменной, метки, типа, функции, модуля, поля в структуре). В иден

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

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

Этапы обработки программы
Язык Си относится к языкам высокого уровня, т.е. предназначенным для записи программы в форме, удобной для человека и не "привязанной" к конкретному типу машин. Ис

Роль препроцессора
Перед компиляцией программа на языке Си обрабатывается специ­альной программой – препроцессором, который работает под управле­нием директив. Препроцессорные директи

Основные типы данных
Данные в языке Си разделяются на две категории: простые (скалярные), будем их называть базовыми, и сложные (составные) типы данных. Тип данных определяет: внутреннее представлени

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

Целочисленные константы
Общий формат: ±n (+ обычно не ставится). Десятичные константы - последовательность цифр 0...9, первая из которых не должна быть 0. Например, 22 и 273 - обычные целые констант

Константы вещественного типа
Данные константы размещаются в памяти по формату double, а во внешнем представлении могут иметь две формы: 1) с фиксированной десятичной точкой, формат записи: ±n.m, где n

Символьные константы
Символьная константа - это символ, заключенный в одинарные кавычки: "A", "х" (занимает 1 байт). В языке Си используются и. специальные (управляющие) символы,

Строковые константы
Строковая константа представляет собой последователь­ность символов кода ASCII, заключенная в кавычки (”) . Во внутреннем представлении к строковым константам добавляется нулевой символ "", еще на

Операция присваивания
Формат операции присваивания: Операнд_1 = Операнд_2 Операндом_1 может быть только переменная. Этот (левый) операнд операции присваивания получил

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

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

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

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

Стандартные математические функции
Математические функции языка Си декларированы в файлах math.h и stdlib.h. В приведенных здесь функциях аргументы и возвращаемый результат имеют

Потоковый ввод-вывод
Поток – это абстрактное понятие, которое относится к любому переносу данных от источника к приемнику. Потоки С++ обеспечивают надежную работу как со стандартными (stdin, stdout), так

Консольные функции вывода данных на экран
Наряду с потоковым вводом-выводом, в консольных приложениях применяются и функции ввода-вывода языка Си. Их декларации приведены в заголовочных файлах stdio.h и c

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

Советы по программированию
1. Выбирайте тип для переменных с учетом диапазона их возможных значений и требуемой точности представления данных. 2. Старайтесь давать переменным ID (имена), отражающие их назначе

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

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

Советы по программированию
1. Выражение, стоящее в круглых скобках операторов if, while и do – while вычисляется по правилам стандартных приоритетов операций. 2. Если в какой-либо ветви вычислен

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

Одномерные массивы
В программе одномерный массив объявляется следующим образом: типID_массива [размер] = {список начальных значений}; тип – тип эл

Одномерные массивы. Нахождение суммы, произведения, количества
Задача 2. Найти сумму элементов массива. #include #include #include void main() {

Одномерные массивы. Удаление и вставка в массивах
Задача 11. Удалить из массива второй по счету элемент. Поскольку полное количество элементов в массиве задано в его объявлении, физически "удалить" элемент

Одномерные массивы. Обмен местами
Задача 14. Поменять местами первый и последний элемент массива. При обмене, чтобы не потерять одно из значений, потребуется дополнительная переменная:  

Одномерные массивы. Сортировка массива
Задача 16. Отсортировать массив по возрастанию (т.е. расположить его элементы в порядке возрастания). Для этой задачи придумано множество различных алгоритмов. Один

Одномерные массивы. Поиск совпадений
Задача 17. Найти в массиве элемент, повторяющийся наибольшее количество раз. (Если таких элементов несколько, вывести любой из них). for(max=i=0; i

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

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

Многомерные массивы. Работа со строками и столбцами
Строка или столбец матрицы аналогичны одномерному массиву. Поэтому к ним применимы все алгоритмы, рассмотренные для одномерных массивов. В применении же ко всей матрице это обычно требует

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

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

Операция sizeof
Данная операция позволяет определить размер объекта по ID или типу, результатом является размер памяти в байтах (тип результата int). Формат записи: sizeof(параметр

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

Регистры
Заметим, что кроме собственно ОЗУ, в компьютере имеются ячейки памяти, размещенные непосредственно в процессоре. Такие ячейки памяти называются регистрами. В процессоре обыч

Регистры. Ввод-вывод строк - массивов char
Для ввода с консоли строк - массивов char обычно используются две стандартные функции: scanf() (см. тему "Функции ввода-вывода"; специфик

Регистры. Поэлементная работа со строками
В языке С не допускается ни присваивание, ни сравнение массивов. Операции над строками могут быть выполнены либо непосредственно действиями над отдельными символами (как над элементами масси

Регистры. Перевод строк - массивов char в числа и наоборот
Функции преобразования строки S в число: - целое: int atoi(char *S); - длинное целое: long atol(char *S); - действительное: doub

Русификация консольных приложений
При работе в консольном приложении ввод-вывод выполняется в кодировке ASCII (см. тему "Кодирование символов", кодовые таблицы). В тексте же программы символы отображаются в принятой в Win

Б) Действия над типом String
Основными операциями с типом String являются: 1) Присваивание: S1=S2; 2) Сравнение: S1==S2, S1<=S2, S1!=S2 и т.д. Здесь знак <

А) Преобразование из массива char в String и наоборот
Как упоминалось выше, для преобразования массива char к типу String достаточно просто присвоить его переменной типа String: char c="Привет!"; String s=

Б) Преобразование из String в простую переменную типа char
При таком присваивании нужно указать номер символа в строке, который будет присвоен: String s="*"; char c=s; Обратное же присваивание н

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

Тип_результата имя_функции (список параметров)
{ код функции return выражение; } Параметры - это переменные, доступные внутри функции, значения которы

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

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

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

Декларация структурного типа данных
Структурный тип данных задается в виде шаблона, общий формат описания которого следующий: struct ID структурного типа { описание полей

Объявление структурных переменных
Как уже отмечалось само описание структуры не приводит к выделению под нее места в ОП. Теперь необходимо создать нужное количество переменных с приведенной структурой и сделать это можно двумя спос

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

Вложенные структуры
Структуры могут быть вложенными, т.е. поле структуры может cамо быть структурой, описание которой должно предшествовать описанию внешней структуры. Например, в структуре person, содержащей

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

Размещение структурных переменных в памяти
Элементы структур в общем случае размещаются в памяти последо­ва­тельно с учетом выравнивания начальных адресов полей. Выравнивание (align) означает, что ком

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

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

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

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

Открытие файла
Каждому файлу, с которым работает программа, присваивается внутреннее логическое имя, используемое в дальнейшем при обращении к нему. Логическое имя (идентификатор файла) - это указатель на файл, т

Закрытие файла
После окончания работы с файлом доступ к нему необходимо закрыть. Это выполняет функция fclose(указатель файла). Например, файл из предыдущего примера закрывается так: fclose (f);

Запись - чтение информации
Все действия по чтению-записи данных в файл можно разделить на три группы: - операции посимвольного ввода-вывода; - операции построчного и форматированного ввода-вывода;

А) Посимвольный ввод-вывод
В функциях посимвольного ввода-вывода происходит прием одного символа (байта) из файла или передача одного символа в файл: int fgetc(FILE *f) - считывает и возв

Б) Построчный и форматированный ввод-вывод
Эти функции служат для чтения/записи текста и обычно применяются для текстовых файлов. В функциях построчного ввода-вывода происходит пере­нос из файла, или в файл

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

Д) Сброс буфера файла
Заметим, что если после записи данных файл не был закрыт, часть «записанных» данных может не сохраниться. Это связано с тем, что данные вначале записываются в буфер файла, и

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

Перенаправление стандартного ввода-вывода
В консольном режимесуществует понятие стандартных файловых потоков: stdin - ввод (по умолчанию - клавиатура), stdout - выв

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

Дополнительные полезные функции
Рассмотрим некоторые функции, которые могут пригодиться для работы с файлами (они работают с любыми файлами, но чаще применяются к бинарным): int fileno(FILE *f)

Определение указателей
Как говорилось выше, машинная память состоит из байт. Все байты в памяти пронумерованы. Адресом байта называется его номер. (Нумерация при этом идет либо в пределах всей

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

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

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

Указатели на структуры
Указатели могут указывать и на структурный тип данных: struct Point{ int x,y; } r, *p; p=&r; Для обращения к полю ст

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

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

Создание одномерного динамического массива
В языке С размерность массива при объявлении должна задаваться константным выражением. При необходимости работы с массивами перемен­ной размерности нужно объявить вместо массива указат

Создание двуxмерного динамического массива
Операция new способна выделить память лишь под одномерный массив. А как быть, если массив двумерный? Наиболее удобный способ - это представить двумерный массив как массив из массивов

Операция typedef
Любому типу данных, как стандартному, так и определенному пользователем, можно задать новое имя с помощью операции typedef: typedef тип новое_имя; Вве

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

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

[СпецификаторКлассаПамяти] [СпецификаторТипа] ИмяФункции ([СписокФормальныхПараметров]) [,СписокИменФункций];

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

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

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

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

1. Функция возвращает значение типа, отличного от int.

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

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

В прототипе можно указать, что число параметров функции переменно, или что функция не имеет параметров.

Если прототип задан с классом памяти static, то и определение функции должно иметь класс памяти static. Если спецификатор класса памяти не указан, то подразумевается класс памяти extern.

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

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


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

int rus (unsigned char r); или rus (unsigned char);

#include

//прототип функции

void change (int &x, int &y);

//описание самой функции

void change (int &x, int &y)

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

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

Для чего нужны функции в C?

Функции в Си применяются для выполнения определённых действий в рамках общей программы. Программист сам решает какие именно действия вывести в функции. Особенно удобно применять функции для многократно повторяющихся действий.

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

Пример функции в Cи:

#include #include int main(void) { puts("Functions in C"); return EXIT_SUCCESS; }

Это очень простая программа на Си. Она просто выводит строку «Functions in C». В программе имеется единственная функция под названием main. Рассмотрим эту функцию подробно. В заголовке функции, т.е. в строке

int – это тип возвращаемого функцией значения;

main - это имя функции;

(void) - это перечень аргументов функции. Слово void указывает, что у данной функции нет аргументов;

return – это оператор, который завершает выполнение функции и возвращает результат работы функции в точку вызова этой функции;

EXIT_SUCCESS - это значение, равное нулю. Оно определено в файле stdlib.h;

часть функции после заголовка, заключенная в фигурные скобки

{
puts("Functions in C");
return EXIT_SUCCESS;
}

называют телом функции.

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

Как из одной функции в Cи вызвать другую функцию?

Рассмотрим пример вызова функций в Си:

/* Author: @author Subbotin B.P..h> #include int main(void) { puts("Functions in C"); int d = 1; int e = 2; int f = sum(d, e); printf("1 + 2 = %d", f); return EXIT_SUCCESS; }

Запускаем на выполнение и получаем:

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

Заголовок функции sum:

int sum(int a, int b)

здесь int - это тип возвращаемого функцией значения;

sum - это имя функции;

(int a, int b) - в круглых скобках после имени функции дан перечень её аргументов: первый аргумент int a, второй аргумент int b. Имена аргументов являются формальными, т.е. при вызове функции мы не обязаны отправлять в эту функцию в качестве аргументов значения перемнных с именами a и b. В функции main мы вызываем функцию sum так: sum(d, e);. Но важно, чтоб переданные в функцию аргументы совпадали по типу с объявленными в функции.

В теле функции sum, т.е. внутри фигурных скобок после заголовка функции, мы создаем локальную переменную int c, присваиваем ей значение суммы a плюс b и возвращаем её в качестве результата работы функции опрератором return.

Теперь посмотрим как функция sum вызывается из функции main.

Вот функция main:

Int main(void) { puts("Functions in C"); int d = 1; int e = 2; int f = sum(d, e); printf("1 + 2 = %d", f); return EXIT_SUCCESS; }

Сначала мы создаём две переменных типа int

Int d = 1; int e = 2;

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

int f = sum(d, e);

её значением будет результат работы функции sum, т.е. мы вызываем функцию sum, которая возвратит значение типа int, его-то мы и присваиваем переменной f. В качестве аргументов передаём d и f. Но в заголовке функции sum

int sum(int a, int b)

аргументы называются a и b, почему тогда мы передаем d и f? Потому что в заголовке функций пишут формальные аргументы, т.е. НЕ важны названия аргументов, а важны их типы. У функции sum оба аргумента имеют тип int, значит при вызове этой функции надо передать два аргумента типа int с любыми названиями.

Ещё одна тонкость. Функция должна быть объявлена до места её первого вызова. В нашем примере так и было: сначала объявлена функция sum, а уж после мы вызываем её из функции main. Если функция объявляется после места её вызова, то следует использовать прототип функции.

Прототип функции в Си

Рассмотрим пример функциив Си:

/* Author: @author Subbotin B.P..h> #include int sum(int a, int b); int main(void) { puts("Functions in C"); int d = 1; int e = 2; int f = sum(d, e); printf("1 + 2 = %d", f); return EXIT_SUCCESS; } int sum(int a, int b) { int c = 0; c = a + b; return c; }

В этом примере функция sum определена ниже места её вызова в функции main. В таком случае надо использовать прототип функции sum. Прототип у нас объявлен выше функции main:

int sum(int a, int b);

Прототип - это заголовок функции, который завершается точкой с запятой. Прототип - это объявление функции, которая будет ниже определена. Именно так у нас и сделано: мы объявили прототип функции

int f = sum(d, e);

а ниже функции main определяем функцию sum, которая предварительно была объявлена в прототипе:

Int sum(int a, int b) { int c = 0; c = a + b; return c; }

Чем объявление функции в Си отличается от определения функции в Си?

Когда мы пишем прототип функции, например так:

int sum(int a, int b);

то мы объявляем функцию.

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

Int sum(int a, int b) { int c = 0; c = a + b; return c; }

то мы определяем функцию.

Оператор return

Оператор return завершает работу функции в C и возвращает результат её работы в точку вызова. Пример:

Int sum(int a, int b) { int c = 0; c = a + b; return c; }

Эту функцию можно упростить:

Int sum(int a, int b) { return a + b; }

здесь оператор return вернёт значение суммы a + b.

Операторов return в одной функции может быть несколько. Пример:

Int sum(int a, int b) { if(a > 2) { return 0;// Первый случай; } if(b < 0) { return 0;// Второй случай; } return a + b; }

Если в примере значение аргумента a окажется больше двух, то функция вернет ноль (первый случай) и всё, что ниже комментария «// Первый случай;» выполнятся не будет. Если a будет меньше двух, но b будет меньше нуля, то функция завершит свою работу и всё, что ниже комментария «// Второй случай;» выполнятся не будет.

И только если оба предыдущих условия не выполняются, то выполнение программы дойдёт до последнего оператора return и будет возвращена сумма a + b.

Передача аргументов функции по значению

Аргументы можно передавать в функцию C по значению. Пример:

/* Author: @author Subbotin B.P..h> #include int sum(int a) { return a += 5; } int main(void) { puts("Functions in C"); int d = 10; printf("sum = %d\n", sum(d)); printf("d = %d", d); return EXIT_SUCCESS; }

В примере, в функции main, создаём переменную int d = 10. Передаём по значению эту переменную в функцию sum(d). Внутри функции sum значение переменной увеличивается на 5. Но в функции main значение d не изменится, ведь она была передана по значению. Это означает, что было передано значение переменной, а не сама переменная. Об этом говорит и результат работы программы:

т.е. после возврата из функции sum значеие d не изменилось, тогда как внутри функции sum оно менялось.

Передача указателей функции Си

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

/* Author: @author Subbotin B.P..h> #include int sum(int *a) { return *a += 5; } int main(void) { puts("Functions in C"); int d = 10; printf("sum = %d\n", sum(&d)); printf("d = %d", d); return EXIT_SUCCESS; }

В этом варианте программы я перешел от передачи аргумента по значению к передаче указателя на переменную. Рассмотрим подробнее этот момент.

printf("sum = %d\n", sum(&d));

в функцию sum передается не значение переменной d, равное 10-ти, а адрес этой переменной, вот так:

Теперь посмотрим на функцию sum:

Int sum(int *a) { return *a += 5; }

Аргументом её является указатель на int. Мы знаем, что указатель - это переменная, значением которой является адрес какого-то объекта. Адрес переменной d отправляем в функцию sum:

Внутри sum указатель int *a разыменовывается. Это позволяет от указателя перейти к самой переменной, на которую и указывает наш указатель. А в нашем случае это переменная d, т.е. выражение

равносильно выражению

Результат: функция sum изменяет значение переменной d:

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

C/C++ в Eclipse

Все примеры для этой статьи я сделал в Eclipse. Как работать с C/C++ в Eclipse можно посмотреть . Если вы работаете в другой среде, то примеры и там будут работать.

Последнее обновление: 31.10.2015

Паттерн Прототип (Prototype) позволяет создавать объекты на основе уже ранее созданных объектов-прототипов. То есть по сути данный паттерн предлагает технику клонирования объектов.

Когда использовать Прототип?

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

    Когда нежелательно создание отдельной иерархии классов фабрик для создания объектов-продуктов из параллельной иерархии классов (как это делается, например, при использовании паттерна Абстрактная фабрика)

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

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

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

Class Client { void Operation() { Prototype prototype = new ConcretePrototype1(1); Prototype clone = prototype.Clone(); prototype = new ConcretePrototype2(2); clone = prototype.Clone(); } } abstract class Prototype { public int Id { get; private set; } public Prototype(int id) { this.Id = id; } public abstract Prototype Clone(); } class ConcretePrototype1: Prototype { public ConcretePrototype1(int id) : base(id) { } public override Prototype Clone() { return new ConcretePrototype1(Id); } } class ConcretePrototype2: Prototype { public ConcretePrototype2(int id) : base(id) { } public override Prototype Clone() { return new ConcretePrototype2(Id); } }

Участники

    Prototype : определяет интерфейс для клонирования самого себя, который, как правило, представляет метод Clone()

    ConcretePrototype1 и ConcretePrototype2 : конкретные реализации прототипа. Реализуют метод Clone()

    Client : создает объекты прототипов с помощью метода Clone()

Рассмотрим клонирование на примере фигур - прямоугольников и кругов:

Class Program { static void Main(string args) { IFigure figure = new Rectangle(30,40); IFigure clonedFigure = figure.Clone(); figure.GetInfo(); clonedFigure.GetInfo(); figure = new Circle(30); clonedFigure=figure.Clone(); figure.GetInfo(); clonedFigure.GetInfo(); Console.Read(); } } interface IFigure { IFigure Clone(); void GetInfo(); } class Rectangle: IFigure { int width; int height; public Rectangle(int w, int h) { width = w; height = h; } public IFigure Clone() { return new Rectangle(this.width, this.height); } public void GetInfo() { Console.WriteLine("Прямоугольник длиной {0} и шириной {1}", height, width); } } class Circle: IFigure { int radius; public Circle(int r) { radius = r; } public IFigure Clone() { return new Circle(this.radius); } public void GetInfo() { Console.WriteLine("Круг радиусом {0}", radius); } }

Здесь в качестве прототипа используется интерфейс IFigure, который реализуется классами Circle и Rectangle.

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

Public IFigure Clone() { return this.MemberwiseClone() as IFigure; }

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

В то же время надо учитывать, что метод MemberwiseClone() осуществляет неполное копирование - то есть копирование значимых типов. Если же класс фигуры содержал бы объекты ссылочных типов, то оба объекта после клонирования содержали бы ссылку на один и тот же ссылочный объект. Например, пусть фигура круг имеет свойство ссылочного типа:

Class Point { public int X { get; set; } public int Y { get; set; } } class Circle: IFigure { int radius; public Point Point { get; set; } public Circle(int r, int x, int y) { radius = r; this.Point = new Point { X = x, Y = y }; } public IFigure Clone() { return this.MemberwiseClone() as IFigure; } public void GetInfo() { Console.WriteLine("Круг радиусом {0} и центром в точке ({1}, {2})", radius, Point.X, Point.Y); } }

В этом случае при изменении значений в свойстве Point начальной фигуры автоматически бы изменилось соответствующее значение и у клонированной фигуры:

Circle figure = new Circle(30, 50, 60); Circle clonedFigure=figure.Clone() as Circle; figure.Point.X = 100; // изменяем координаты начальной фигуры figure.GetInfo(); // figure.Point.X = 100 clonedFigure.GetInfo(); // clonedFigure.Point.X = 100

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

Using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; //........................ class Program { static void Main(string args) { Circle figure = new Circle(30, 50, 60); // применяем глубокое копирование Circle clonedFigure=figure.DeepCopy() as Circle; figure.Point.X = 100; figure.GetInfo(); clonedFigure.GetInfo(); Console.Read(); } } //......................... class Point { public int X { get; set; } public int Y { get; set; } } class Circle: IFigure { int radius; public Point Point { get; set; } public Circle(int r, int x, int y) { radius = r; this.Point = new Point { X = x, Y = y }; } public IFigure Clone() { return this.MemberwiseClone() as IFigure; } public object DeepCopy() { object figure = null; using (MemoryStream tempStream = new MemoryStream()) { BinaryFormatter binFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone)); binFormatter.Serialize(tempStream, this); tempStream.Seek(0, SeekOrigin.Begin); figure = binFormatter.Deserialize(tempStream); } return figure; } public void GetInfo() { Console.WriteLine("Круг радиусом {0} и центром в точке ({1}, {2})", radius, Point.X, Point.Y); } }

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