Скачиваний:
2
Добавлен:
03.01.2024
Размер:
2.1 Mб
Скачать

СПбГУТ им. проф. М.А. Бонч–Бруевича Кафедра программной инженерии и вычислительной техники (ПИ и ВТ)

ПРОГРАММИРОВАНИЕ

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

Брайэн Керниган

Лекция 8: Функции

1.Функции в Си

2.Формальные и фактические параметры

3.Передача массивов в функцию

4.Функции с переменным числом параметров

5.Функции. Что ещё?..

Санкт–Петербург, 2021г.

 

 

 

Введение: Что такое функция?

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

В языке Си подпрограммы реализованы в виде функций.

Мы собираем все действия в одну функцию main и по несколько

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

 

раз копируем одни и те же действия, создаём десятки

Функции:

 

переменных с уникальными именами.

 

 

 

во-первых, помогут выделить в отдельные подпрограммы

Наши программы распухают и становятся всё менее и менее

 

дублирующийся код,

 

понятными, ветвления становятся всё длиннее и ветвистее.

 

 

 

 

во-вторых, помогут логически разбить программу на части,

 

Но из сложившейся ситуации есть выход!

 

в-третьих, с функциями в си связано много особенностей,

 

 

которые позволят использовать новые подходы к

 

Использование вспомогательных алгоритмов ─ ПОДПРОГРАММ

структурированию приложений.

 

 

 

 

Процедура( функция) представляет собой

 

 

Основная (вызывающая)

Есть выход:

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

 

 

Divide et impera –

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

 

 

программа

частей программы.

 

 

Разделяй и властвуй!

 

 

 

Имя процедуры в тексте программы называется вызовом.

 

 

 

 

 

 

Вызов подпрограммы

 

 

 

 

Подпрограмма

Вызов активирует процедуру (функцию) ─ начинают

 

 

 

выполняться её операторы.

 

 

 

 

 

 

Продолжение основной

 

После выполнения процедуры программа продолжается с

 

 

программы

Структурная

оператора стоящего за вызовом.

 

 

 

Отличие процедур от функций в том, что функции

 

 

 

декомпозиция

 

 

 

программы!

возвращают значение.

 

Подпрограммы применяются когда:

 

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

 

 

информацией.

 

часть алгоритма неоднократно повторяется в программе;

 

Это можно осуществить с помощью:

 

можно использовать фрагменты разработанных ранее

 

 

глобальных переменных;

 

 

алгоритмов;

 

 

 

 

через параметры;

 

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

 

 

с модульным принципом программирования.

через возвращаемое функцией значение.

1. Функции в Си

Что такое функция?

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

Функция может принимать параметры и возвращать значение.

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

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

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

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

Входные данные называют параметрами, а выходные —

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

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

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

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

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

Главное, что должно быть можно сделать с функцией — это возможность ее вызвать.

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

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

Определение (definition) функции содержит исполняемый код функции.

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

Функции, которые не возвращают значений, иногда называют

процедурами.

3

 

Функции в Си

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

Плюсы ПП:

Уменьшение размера памяти, занимаемой кодом программы

почти неактуально в настоящее время.

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

Исправление ошибок, оптимизация, расширение функциональности в ПП автоматически отражается на всех её вызовах

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

В языке Си подпрограммы называются функциями

Описание функции делится на:

Заголовок – тип результата, имя функции и список параметров функции

Если тип-результата есть void, то функция не возвращает результата – аналог процедуры в языке Паскаль

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

Вызов – это частный случай постфиксного выражения языка Си (A + B → A B +)

Вам уже знакомы функции:

Функции ввода-вывода – <stdio.h>:

printf() – форматный вывод в stdout

scanf() – форматный ввод (чтение данных) из потока stdin

getchar(), putchar(int c) и т.д.

Функции работы с файлами:

FILE *fopen(char *filename, char *mode)

int fflush(FILE *f)

int fclose(FILE *f) и т.д.

* * *

Функции работы со строками – <string.h> Математические функции – <math.h> Функции общего назначения – <stdlib.h>

Функции работы с датой и временем – <time.h>

** *

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

Перед использованием функция должна быть объявлена

Все функции в языке Си – глобальные, т.е. функция не

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

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

4

Функции в Си

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

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

Мы уже знакомы с многими функциями и знаем, как их вызывать – это функции библиотек stdio, stdlib, string, conio и пр.

Более того, main – это тоже функция.

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

Функция в Си определяется в глобальном контексте.

Синтаксис функции:

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

{

тело функции

}

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

Значительно проще написать решение маленьких задач по отдельности, чем реализовать весь процесс целиком.

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

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

1.Определение функции (описание действий, выполняемых функцией)

2.Объявление функции (задание формы обращения к функции)

3.Вызов функции.

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

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

//Примеры:

//Объявление функции:

int sum (int a, int b);

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

{

return (a+b);

}

5

Функция. Структурная декомпозиция программы

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

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

аргументы

int k = add_ints (2,3);

Значения аргументов заменяют соответствующие параметры в определении функции

Возвращается значение 5

int add_ints (int a, int b)

{

 

return a+b;

 

формальные

}

 

параметры

 

 

 

 

 

 

 

 

 

 

 

 

 

6

Функция. Структурная декомпозиция программы

void my_f1 (double v, int dim)

{

Выполнение

начинается с main return;

int main()

{

}

 

 

void my_f2 (double v1, double v2,

my_f1 (a,n);

 

 

double res, int dim)

 

 

{

 

 

 

 

my_f2 (a,b,c,n);

 

 

return;

 

 

}

 

output_vect (c,n);

 

 

 

 

 

void output_vect (double v, int dim)

 

 

 

 

{

 

 

return 0

 

 

 

 

 

}

 

 

 

return;

 

 

 

 

 

Возврат из main в ОС

 

}

 

 

 

 

 

 

 

 

 

 

7

Функция. Пример

Дублирование кода является признаком «низкого» или «ленивого» стиля программирования.

Хороший стиль программирования обычно основан на повторном использовании кода.

Проблемы, к которым приводит дублирование кода:

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

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

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

размер исходного текста: без применения какого-либо сжатия исходный текст занимает больше места.

//Вычисляет среднее значение массива целых чисел:

int array1[N]; int array2[N];

int sum1 = 0; int sum2 = 0; int average1; int average2; int i;

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

sum1 += array1[i];

average1 = sum1 / 4;

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

sum2 += array2[i];

average2 = sum2 / 4;

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

отыскать дубликаты кода, среди них:

алгоритм Бейкер;

алгоритм Рабина-Карпа;

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

В ряде случаев эффективно визуальное определение дубликатов

NB: Одна из основных причин проявления дублирования — программирование копированием-вставками, при котором участки кода копируются просто потому, что «это работает».

// Два цикла могут быть выделены в отдельную функцию:

int calcAverage (int* Array_of_4)

{

int sum = 0;

for (int i = 0; i < 4; ++i) sum += Array_of_4[i];

return sum / 4;

}

// Использование этой функции избавит код от дубликатов: int array1[N];

int array2[N];

int average1 = calcAverage(array1); int average2 = calcAverage(array2);

8

если чётное.
Это смешанная рекурсия – функция odd возвращает 1, если число нечётное и 0,

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

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

Пример:

Оператор return

int max ( int a, int b)

– вызывает немедленный выход из текущей

{ if (a>b)

функции и возврат в вызывающую функцию

return a;

– используется для возврата значения функции

else

– в теле функции может быть несколько

return b;

операторов return, но может не быть ни одного

 

}

 

В данном примере определена функция с именем max,

имеющая 2 параметра. Функция возвращает целое максимальное значение из а и b.

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

объявление (прототип) функции.

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

Для функции, определенной в последнем примере, прототип может иметь вид:

int max (int a, int b);

– если нет return, возврат в вызывающую программу происходит после выполнения последнего оператора тела функции

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

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

В Си можно объявить функцию до её определения.

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

Например:

#include <stdio.h>

//Прототипы функций. Имена аргументов можно не писать int odd(int);

int even(int); void main()

{

printf("if %d odd? %d\n", 11, odd(11)); printf("if %d odd? %d\n", 10, odd(10)); getch();

}

//Определение функций. Смешанная рекурсия

int even(int a)

 

 

 

 

 

 

{

 

 

 

 

 

 

if (a) odd(--a);

 

 

 

 

 

 

else return 1;

int odd(int a)

 

 

 

 

 

 

 

 

}

{

 

 

 

 

 

 

if (a) even(--a);

 

 

 

 

 

 

 

 

else

return 0;

 

 

 

 

 

 

 

 

 

}

 

9

 

 

 

 

 

 

 

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

Обычно объявление функции помещают отдельно, в .h файл,

Особенности каждого файла в каталоге (с библиотекой):

 

а определение функций в .c файл.

 

 

Наш файл, который содержит функцию main, подключает

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

необходимые ему библиотеки а также заголовочный файл

 

интерфейс библиотеки и показывает, как с ней работать, не

File1.h.

 

вдаваясь в содержимое кода.

 

 

Теперь компилятору известны прототипы функций, то есть он

 

Создание простой библиотеки

 

 

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

 

 

 

имена функций.

 

Для этого нужно будет создать два файла – один с

 

Заголовочный файл, как и оговаривалось ранее, содержит

расширением .h и поместить туда прототипы функций, а другой с

прототип функций.

расширением .c и поместить туда определения этих функций.

Также здесь могут быть подключены используемые

 

Если вы работаете с IDE, то .h файл необходимо создавать в

 

библиотеки.

каталоге «Заголовочные файлы», а файлы кода в каталоге

Макрозащита #define _FILE1_H_ и т.д. используется для

«Файлы исходного кода» (условные имена каталогов).

предотвращения повторного копирования кода библиотеки

 

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

 

при компиляции.

предыдущего слайда:

 

 

 

 

 

 

 

 

Эти строчки можно заменить одной #pragma once

 

Заголовочный файл

 

Содержимое файла

 

 

 

 

 

 

Файл File1.c исходного кода подключает свой заголовочный

 

File1.h:

 

исходного кода File1.c:

 

 

 

 

 

 

файл.

 

#ifndef _FILE1_H_

 

#include "File1.h"

 

 

 

#define _FILE1_H_

 

 

 

Всё как обычно логично и просто.

 

 

int even(int a)

 

 

 

int odd(int);

 

 

 

В заголовочные файлах принято кроме прототипов функций

 

 

{

if (a) odd(--a);

 

 

 

int even(int);

 

 

else return 1;

 

 

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

 

#endif

 

}

 

 

 

типы данных.

 

Макрозащита #define

 

int odd(int a) //*******

 

 

 

 

 

Именно в заголовочных файлах можно

_FILE1_H_ и т.д.

{

if (a) even(--a);

 

 

 

 

 

обширно комментировать код и писать

используется для

}

else return 0;

 

Функция main:

 

 

 

примеры его использования.

 

 

 

 

предотвращения

 

 

#include <stdio.h>

 

 

 

 

 

 

 

 

 

 

 

повторного копирования

 

 

 

 

 

 

 

 

 

 

#include "File1.h"

 

 

 

 

Или File1.h:

 

 

 

 

 

кода библиотеки при

 

 

 

 

 

 

void main()

 

 

 

 

компиляции.

#pragma once

 

{ printf("if %d odd? %d\n", 11, odd(11));

 

 

Эти строчки можно

#include "File1.c"

 

printf("if %d odd? %d\n", 10, odd(10));

 

заменить одной:

int odd(int);

 

getch();

 

 

 

 

#pragma once

int even(int);

 

}

 

 

 

10

 

 

 

 

 

 

 

 

 

 

 

Соседние файлы в папке Лекции