Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Тема7_Функции.doc
Скачиваний:
1
Добавлен:
14.07.2019
Размер:
113.66 Кб
Скачать

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

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

bool isDiagonal(float matrix[N][N]) {

for (int i=0; i<N; i++) {

for (int j=0; j<N; j++) {

if (i!=j && matrix[i][j] != 0) {

return false;

}

}

}

return true;

}

Реализация функций в компактном виде

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

Рассмотрим функцию, которая проверяет, является ли число четным:

bool isEven(int n) {

if (n%2==0) {

return true;

} else {

return false;

}

}

Обратите внимание на то, что, если убрать ветку else, то работа функции не изменится, поскольку оператор return останавливает работу функции:

bool isEven(int n) {

if (n%2==0) {

return true;

}

return false;

}

Еще сократить код можно при помощи оператора ?::

bool isEven(int n) {

return n%2==0 ? true : false;

}

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

bool isEven(int n) {

return n%2 == 0;

}

Посмотрите, какой длинной программа была в начале, и какой компактной она стала в конце!

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

int max(int m, int n) {

if (m>n) {

return m;

} else {

return n;

}

}

Сократить код можно так:

int max(int m,int n) {

return m>n ? m : n;

}

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

int indexOf(int element) {

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

if (numbers[i]==element) {

return i;

}

}

return -1;

}

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

Правильное проектирование функций

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

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

for (int i=0; i<size; i++) {

numbers[i] = rand()%100;

}

for (int i=0; i<size; i++) {

printf("%d ", numbers[i]);

}

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

for (int j=size-1; j>0; j--) {

if (numbers[j] < numbers[j-1]) {

int temp = numbers[j];

numbers[j] = numbers[j-1];

numbers[j-1] = temp;

}

}

}

for (int i=0; i<size; i++) {

printf("%d ", numbers[i]);

}

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

#include <stdio.h>

#include <stdlib.h>

void initNumbers(int numbers[], int size);

void printNumbers(int numbers[], int size);

void sortNumbers(int numbers[], int size);

int main() {

const int mySize = 10;

int myNumbers[mySize];

initNumbers(myNumbers, mySize);

printNumbers(myNumbers, mySize);

sortNumbers(myNumbers, mySize);

printNumbers(myNumbers, mySize);

}

void initNumbers(int numbers[], int size) {

for (int i=0; i<size; i++) {

numbers[i] = rand()%100;

}

}

void printNumbers(int numbers[], int size) {

for (int i=0; i<size; i++) {

printf("%d ", numbers[i]);

}

}

void sortNumbers(int numbers[], int size) {

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

for (int j=size-1; j>0; j--) {

if (numbers[j] < numbers[j-1]) {

int temp = numbers[j];

numbers[j] = numbers[j-1];

numbers[j-1] = temp;

}

}

}

}

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

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

КОНТРОЛЬНЫЕ ВОПРОСЫ

  1. Зачем нужны функции?

  2. Что такое аргументы функции?

  3. Чем отличаются глобальные переменные от локальных?

  4. Объясните работу оператора return.

  5. Чем концептуально отличаются функции от процедур?

  6. Что такое рекурсивная функция? Приведите примеры.

  7. Что такое прототип функции? Приведите примеры.

  8. Что такое заголовочный файл и как с ним работать?

  9. Как передавать одномерные и двумерные массивы в качестве аргументов функций? Приведите пример.

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