Принцип возвращения значений функциями в Си
Изучаю язык Си на интенсиве и столкнулся со следующим моментом. Сказано, что функция scanf в Си возвращает количество успешно считанных аргументов. Однако вместе с тем подставив её для записи в переменную, в переменную будет записано не количество успешно считанных аргументов, а непосредственно сами аргументы. Получается есть некий механизм отличный от return, за счёт которого осуществляется запись в переменную. Что это за механизм?
Например, в Python в переменную будет записано то, что возвращает функция через return.
Ответы (2 шт):
Функция может общаться с окружающим миром как минимум тремя способами:
- через return. При этом допустимо возвращать даже структуру.
- через глобальные переменные
- через изменение аргументов, переданных по указателю. scanf это и делает.
- (3.1?) через изменение аргументов, переданных по ссылке (почти то же самое, но с неявным указанием адреса). Надо иметь ввиду, что это уже не синтаксис стандартного Си, это C++.
Иногда функции требуется где-то сохранять собственные данные, чтобы использовать их при повторных вызовах. В этих случаях в функции объявляют переменные с ключевым словом static. Они под капотом также оказываются глобальными, как и в п.2, но видны только самой функции. Так что тут общение с окружающим миром ограничивается потребностями самой функции.
Есть и другие экзотические способы, например функция может специально упасть в ошибку-исключение, описав ошибку (throw Exception). А место, откуда она вызывалась специально обёрнуто в конструкцию, готовую к ошибкам и их отлавливающую (try/catch). Но это уже C++.
#include <stdio.h>
//глобальная переменная
int a=1;
//Функция использует глобальную переменную
void func1(){
a=a+100;
}
//Функция использует обычный аргумент и возвращает
int func2(int arg1){
return arg1+100;
}
//Функция использует аргументы-указатели
void func3(int* arg1){ //Ожидается не переменная, а её адрес
*arg1+=100; //изменяем то, что находится по этому адресу
}
// Функция использует аргументы-ссылки
void func4(int &arg1) { // Ожидается не переменная, а ссылка на неё
arg1 += 100; // косвенно изменяем то, что находится по этому адресу
}
int main() {
int b=2;
int c=3;
int d=4;
func1();
printf("%d\n",a);
b=func2(b);
printf("%d\n",b);
func3(&c);//передаём адрес (указатель) переменной
printf("%d\n",c);
func4(d); // неявно передаём адрес (ссылку) переменной
printf("%d\n", d);
}
/*
101
102
103
104
*/
Несколько общие соображения, но, возможно, это тоже будет полезно. На мой взгляд, все эти запутанные варианты возврата результатов из функций произошли от того, что когда-то давно ещё не придумали толком кортежи и их удобную распаковку. Как в Питоне, например. Когда мы можем просто написать:
a, b, c = f(x, y, z)
И получить удобным способом сразу несколько возвращаемых значений. В традиционном C возвращаемое значение могло быть только одно, поэтому пришлось результаты количеством более одного возвращать, меняя аргументы функции, переданные в виде ссылок. При этом логично, что единственное, возвращаемое функцией значение, отправляет некую диагностику - успешно ли выполнилась функция и сколько сущностей она обработала (приняла или передала), а сами эти сущности меняются с использованием ссылок.
Конечно, можно было бы возвращать некую структуру, но это нужно было бы её описывать и заполнять, а после получения из функции разбирать, что гораздо неудобнее, чем использовать просто переменные.
Ну и плюс к тому контроль памяти. Когда вы передаёте в функцию ссылки на готовые уже переменные, под которые выделена память - вы знаете, что эту память выделили вы и вы же за неё отвечаете и будете удалять её потом. А когда под переменные память выделяет функция, то ответственность размывается. И область видимости этих переменных. Переменные вроде бы локальные для функции, а она их передаёт наружу. В общем, для C такой подход с созданием переменных и отдачей их наружу был бы очень не естественным по многим причинам.
P.S. Я на C писал лет 30 назад, прошу простить за возможные ошибки в его понимании.