Чистая функция, это функция, которая:
int f(int x) {
int y = 0;
int z = 0;
y = 2 * x;
z = y + 1;
return z / 3;
}
Что делать, если функции нужно вернуть сразу два или три значения, причем, возможно, разных типов?
Увы, просто так этого сделать нельзя.
Такие ситуации можно разделить на два характерных случая:
Итак, функции getPixelColor(x, y) передаются координаты точки на текстуре, надо вернуть ее цвет.
Цвет в модели RGB TrueColor описывается тремя целыми беззнаковыми числами в диапазоне от 0 до 255.
Здесь цвет точки однозначно представляет собой объект, который должен быть выделен в структуру данных.
typedef struct {
unsigned char r, g, b; //компоненты цвета red, green, blue
} tColor;
Как только создана такая Си-структура, вопрос передачи цвета из функции решен:
Функция getStudentMark() должна получить с клавиатуры строчку с фамилией студента и его оценку по информатике. При этом для студентов уже существует структура данных:
typedef struct {
int studentID; //идентификатор студента
char lastName[30]; //фамилия
char name[30]; //имя
unsigned int groupNumber; //номер группы
} tStudent;
Заметим, что эта структура нам для возврата значения не подходит. Мы возвращаем лишь фамилию и оценку.
Фамилия -- лишь часть этой структуры данных, остальное нам в этой функции заполнить неоткуда. Заполнить пустыми значениями? А вдруг мы в месте вызова забудем, что в возвращенном экземпляре структуры правдива лишь фамилия?
Оценку по информатике можно было бы внести в эту структуру, но, согласитесь, будет странно, если оценка будет атрибутом самого студента, а не его строчки в журнале. Это не логично, особенно если учесть, что другая функция, работающая со списком студентов, будет вынуждена "выдумывать" теперь уже оценку.
Выход может состоять в том, чтобы создать "одноразовую" структуру данных, осознавая ограниченность ее применения: только для возврата из данной конкретной функции.
typedef struct {
char lastName[30]; //фамилия
unsigned int mark; //оценка
} tStudentMark;
Будет хорошо, если эта структура выработана логично, и она нам еще понадобится в других функциях, тогда она "перерастет" свою одноразовость.
Функция будет выглядеть так:
tStudentMark getStudentMark()
{
tStudentMark studentMark; //локальная переменная, куда будем доставать фамилию и оценку.
//... достать фамилию и оценку (без подробностей)
return studentMark;
}
В месте вызова:
char lastName[20];
int mark;
tStudentMark studentMark; //переменная, которая нужна только для разбора значений из функции
studentMark = getStudentMark();
lastName = studentMark.lastName;
mark = studentMark.mark;
Текста получилось много, зато мы сохранили "чистоту функции".
Есть еще два способа вернуть из функции значение:
Итак, функция getStudentMark() получает в качестве параметров адрес строки и адрес оценки, считывает с клавиатуры строчку с фамилией студента и его оценку по информатике, и сохраняет в параметры.
Попробуем написать функцию с таким заголовком:
void getStudentMark(char lastName[], int *mark)
{
scanf("%s%d", lastName, mark);
}
В месте вызова:
char lastName[20];
int mark;
getStudentMark(lastName, &mark);
Плюсы: отказались от "одноразовой" структуры данных, уменьшили длину кода.
Минусы: корректность памяти определяется местом вызова. Внутри функции хорошо бы проверять корректность данных адресов памяти.