Принцип возвращения значений функциями в Си

Изучаю язык Си на интенсиве и столкнулся со следующим моментом. Сказано, что функция scanf в Си возвращает количество успешно считанных аргументов. Однако вместе с тем подставив её для записи в переменную, в переменную будет записано не количество успешно считанных аргументов, а непосредственно сами аргументы. Получается есть некий механизм отличный от return, за счёт которого осуществляется запись в переменную. Что это за механизм? Например, в Python в переменную будет записано то, что возвращает функция через return.


Ответы (2 шт):

Автор решения: Solt

Функция может общаться с окружающим миром как минимум тремя способами:

  1. через return. При этом допустимо возвращать даже структуру.
  2. через глобальные переменные
  3. через изменение аргументов, переданных по указателю. scanf это и делает.
  4. (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
*/

https://godbolt.org/z/b4vcr7an9

→ Ссылка
Автор решения: CrazyElf

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

a, b, c = f(x, y, z)

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

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

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

P.S. Я на C писал лет 30 назад, прошу простить за возможные ошибки в его понимании.

→ Ссылка