Как вернуть два значения из функции c
Перейти к содержимому

Как вернуть два значения из функции c

  • автор:

Как вернуть два значения из функции c

Функция в C может возвращать только одно значение. Тем не менее, что если нам надо получить из функции сразу несколько значений? В этом случае мы можем использовать разные подходы. Основные из них — возвращение комплексного объекта, который инкапсулирует отдельные значения, либо использование выходных параметров.

Объединение возвращаемых значений

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

#include #include typedef struct < int min; int max; >MinMax; MinMax getMinMax(int* array, size_t length) < assert(length >1 && "Array length is invalid"); MinMax result; result.min = array[0]; result.max = array[0]; for(size_t i = 0; i < length; i++) < if(array[i] < result.min) result.min = array[i]; if(array[i] >result.max) result.max = array[i]; > return result; > int main(void) < int array[] = ; size_t length = sizeof(array) / sizeof(int); MinMax data = getMinMax(array, length); printf("min=%d\n", data.min); // min=2 printf("max=%d\n", data.max); // max=9 >

Здесь для возвращения из функции минимального и максимального значения массива определена структура MinMax, которую возвращает функция getMinMax. Это наиболее простой и очевидный способ возвращения нескольких значений. Однако он имеет свои минусы. Прежде всего нам надо специально определять структуру под возвращаемые значения. Во-вторых, при возвращении структуры происходит копирование ее значений в стеке, что увеличивает объем потребляемой памяти.

Выходные параметры

Другой способ возвратить из функции несколько значений представляют выходные параметры (out-параметры). Язык Си как таковой не имеет концепции выходных параметров функции (как например, C#), однако мы можем симулировать выходные параметры с помощью указателей:

#include #include void getMinMax(int* array, size_t length, int* min, int* max) < assert(length >1 && "Array length is invalid"); *min = array[0]; *max = array[0]; for(size_t i = 0; i < length; i++) < if(array[i] < *min) *min = array[i]; if(array[i] >*max) *max = array[i]; > > int main(void) < int array[] = ; size_t length = sizeof(array) / sizeof(int); int minVal = 0; int maxVal = 0; getMinMax(array, length, &minVal, &maxVal); printf("min=%d\n", minVal); printf("max=%d\n", maxVal); >

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

void getMinMax(int* array, size_t length, int* min, int* max)

Здесь параметры-указатели min и max как представляют выходные параметры. Внутри функции нам не важны их начальные значения. Наоборот, функция устанавливает их значения.

При вызове функции определяются переменные, и их адреса передаются выходным параметрам:

int minVal = 0; int maxVal = 0; getMinMax(array, length, &minVal, &maxVal);

В данном случае выходным параметрам min и max передаются соответственно адреса переменных minVal и maxVal.

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

Оператор return (C)

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

Синтаксис

jump-statement :
return expression необ. ;

Значение expression , если оно имеется, возвращается вызывающей функции. Если expression параметр опущен, возвращаемое значение функции не определено. Параметр «выражение», если он присутствует, вычисляется и преобразуется к типу, возвращаемому функцией. Если оператор return содержит выражение в функциях, имеющих тип возвращаемого значения void , то компилятор выдает предупреждение, а выражение не вычисляется.

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

В качестве лучшей методики разработки рекомендуется всегда указывать тип возвращаемого значения для ваших функций. Если возвращаемое значение не требуется, объявите функцию как имеющую тип возвращаемого значения void . Если тип возвращаемого значения не указан, компилятор C предполагает, что по умолчанию используется тип возвращаемого значения int .

Многие программисты используют круглые скобки для заключения expression аргумента return оператора . Однако использовать эти скобки в языке C необязательно.

Если компилятор обнаруживает операторы, размещенные после return , он может вывести предупреждающее диагностическое сообщение о недоступном для выполнения коде.

В функции main оператор return и выражение являются необязательными. То, что происходит с указанным возвращаемым значением, зависит от реализации. Только для Майкрософт:реализация C от Майкрософт возвращает значение выражения процессу, вызвавшему программу, например cmd.exe . Если выражение return не указано, среда выполнения C от Майкрософт возвращает значение, соответствующее успешному (0) или неудачному (ненулевое значение) выполнению.

Пример

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

// C_return_statement.c // Compile using: cl /W4 C_return_statement.c #include // for INT_MAX #include // for printf long long square( int value ) < // Cast one operand to long long to force the // expression to be evaluated as type long long. // Note that parentheses around the return expression // are allowed, but not required here. return ( value * (long long) value ); >

Функция square возвращает квадрат своего аргумента, используя более широкий тип для избежания арифметической ошибки. Только для систем Майкрософт: в реализации C от Майкрософт тип long long достаточно велик, чтобы вмещать произведение двух значений int без переполнения.

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

double ratio( int numerator, int denominator ) < // Cast one operand to double to force floating-point // division. Otherwise, integer division is used, // then the result is converted to the return type. return numerator / (double) denominator; >

Функция ratio возвращает частное двух int аргументов в виде значения double с плавающей запятой. Выражение return принудительно использует операцию с плавающей запятой путем приведения одного из операндов к типу double . В противном случае будет использоваться оператор целочисленного деления, а дробная часть будет потеряна.

void report_square( void ) < int value = INT_MAX; long long squared = 0LL; squared = square( value ); printf( "value = %d, squared = %lld\n", value, squared ); return; // Use an empty expression to return void. >

Функция report_square вызывает square со значением параметра INT_MAX — самым большим целым числом со знаком, которое помещается в int . Результат типа long long сохраняется в squared , а затем выдается в выводе. Функция report_square имеет тип возвращаемого значения void , поэтому она не содержит выражения в операторе return .

void report_ratio( int top, int bottom ) < double fraction = ratio( top, bottom ); printf( "%d / %d = %.16f\n", top, bottom, fraction ); // It's okay to have no return statement for functions // that have void return types. >

Функция report_ratio вызывает ratio со значениями параметров 1 и INT_MAX . Результат типа double сохраняется в fraction , а затем выдается в выводе. Функция report_ratio имеет тип возвращаемого значения void , поэтому явно возвращать значение не требуется. Выполнение report_ratio не дает результата и не возвращает вызывающей функции никакого значения.

int main()

Функция main вызывает две функции: report_square и report_ratio . Поскольку report_square не принимает параметров и возвращает void , результат не присваивается переменной. Аналогичным образом функция report_ratio возвращает void , поэтому ее возвращаемое значение тоже не сохраняется. После вызова каждой из этих функций выполнение продолжается в следующем операторе. Затем main возвращает значение 0 (обычно свидетельствующее об успешном выполнении), чтобы завершить программу.

Чтобы скомпилировать пример, создайте файл исходного кода с именем C_return_statement.c . Затем скопируйте весь пример кода в показанном здесь порядке. Сохраните файл и скомпилируйте его в окне Командной строки разработчика с помощью следующей команды:

cl /W4 C_return_statement.c

После этого, чтобы запустить пример кода, введите C_return_statement.exe в командной строке. Выходные данных в этом примере выглядят следующим образом:

value = 2147483647, squared = 4611686014132420609 1 / 2147483647 = 0.0000000004656613 

вернуть Несколько значений из функции в C

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

vdm ★★
28.12.06 05:20:13 MSK

Не нравится мне твоё «сделать массив», сдаётся мне что ты собрался ссылку на локальный массив возвращать. 🙂 Очень не рекомендую. 🙂

Вообще обычно в таких случаях функции передают указатель на массив или структуру, в которую она пишет результаты. А возвращает она в таком случае просто код завершения (типа успешно/неуспешно). См., например, man 2 fstat.

Teak ★★★★★
( 28.12.06 05:25:30 MSK )
Ответ на: комментарий от Teak 28.12.06 05:25:30 MSK

typedef struct < int one, int two >retval2; retval2 * f(. ) < retval2 * retval = NULL; . if (!error) < retval = malloc(sizeof(retval2)); retval->one = one; retval->two = two; > return retval; > int main(. ) < retval2 * retval; if (retval = f(. )) < printf("one = %i, two = %i\n", retval->one, retval->two); free(retval); > > --- что нить типа такого в голову не приходило. )))

Ex ★★
( 28.12.06 09:01:16 MSK )

> Нужно возвратить из функции значения 2х переменных. На ум приходит > только идея сделать массив с этими значениями и через return выдать > указатель на него. Но может есть другой способ? Так можно делать только если массив статический: int *foo() < static int a[] = ; return a; > Второй способ вернуть структуру: struct S foo(char c, double d) < struct S s; s.ch = c; s.dl = d; return s; >Третий способ, который обычно применяется во всех библиотеках, объявить массив или структуру во внешнем блоке и передавать в функцию их адреса.

shumer ★
( 28.12.06 09:40:31 MSK )

Можно возвращать структуру, но считается, что это не оч. хорошо, т.к. вся структура укладывается в стек: typedef struct < int one; int two; >retval2; retval2 f() < retval2 x; x.one=1; x.two=2; return x; >int main(int argc, char **argv) < retval2 y; y=f(); >Выделять память внутри функции считается не оч. правильным путём, насколько я понимаю. Так что наиболее правильным является передача функции указателя на структуру (или массив): typedef struct < int one; int two; >retval2; int f(reval2 *x) < x->one=1; x->two=2; return 1; //код возврата=всё ок > int main(int argc, char **argv) < retval2 y; if (!f(&y)) //обработка ошибки >

Davidov ★★★★
( 28.12.06 10:17:07 MSK )
Ответ на: комментарий от Davidov 28.12.06 10:17:07 MSK

>> Выделять память внутри функции считается не оч. правильным путём, насколько я понимаю. Так что наиболее правильным является передача функции указателя на структуру (или массив):

не спорю, попросили привести пример, мну привел то что первое в голову пришло )

Ex ★★
( 28.12.06 10:49:02 MSK )
Ответ на: комментарий от Ex 28.12.06 10:49:02 MSK

>не спорю, попросили привести пример, мну привел то что первое в голову пришло )

Да не, никаких претензий. Сам так иногда делаю. Более того, по-моему, в каких-то стандартных вызовах было нечто подобное.

Я так понимаю, что логика состоит в том, что выделением памяти и её освобождением должны быть в логически эквивалентных местах.

То есть, например, в случае наличия функций retval2 *alloc_s1() и int free_s2(retval2 *r), всё уже хорошо. Вот и пример, где это может быть оправдано.

Кстати, всё это уже начинает смахивать на ООП.

Davidov ★★★★
( 28.12.06 10:57:57 MSK )
Ответ на: комментарий от Davidov 28.12.06 10:57:57 MSK

таки ООП это парадигма программирования и не зависит от конкретной реализации в ЯВУ

Ex ★★
( 28.12.06 11:05:00 MSK )
Ответ на: комментарий от shumer 28.12.06 09:40:31 MSK

>Так можно делать только если массив статический: >int *foo() > < >static int a[] = ; > return a; >> Только надо быть очень аккуратным: при повторном вызове будет возвращён тот же указатель.

Davidov ★★★★
( 28.12.06 11:10:25 MSK )
Ответ на: комментарий от Davidov 28.12.06 11:10:25 MSK

>>Так можно делать только если массив статический:

>>int *foo() >>< >> static int a[] = ; >> return a; >>>

>Только надо быть очень аккуратным: при повторном вызове будет возвращён >тот же указатель.

Еще это несколько не thread-safe

anonymous
( 28.12.06 15:08:24 MSK )

void test_retval (int a,int b, int c, int *ret1,int *ret2) < . *ret1=. ; *ret2=. ; >. int r1,r2 test_reatval(1,2,3,&r1,&r2); .

xnix ★★
( 28.12.06 16:25:14 MSK )
Ответ на: комментарий от Ex 28.12.06 09:01:16 MSK

Ну и нахрена звать malloc без необходимости? Если только чтоб выпендриться.

Teak ★★★★★
( 28.12.06 17:38:44 MSK )
Ответ на: комментарий от shumer 28.12.06 09:40:31 MSK

> Так можно делать только если массив статический:

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

Teak ★★★★★
( 28.12.06 17:40:11 MSK )
Ответ на: комментарий от Teak 28.12.06 17:40:11 MSK

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

Конечно так делать не следует, хотя и возможно. Вобщем я сказал «can», а ты имел ввиду «may» 🙂

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

shumer ★
( 28.12.06 17:52:21 MSK )
Ответ на: комментарий от shumer 28.12.06 17:52:21 MSK

Согласен, все посты в этой теме, кроме первого, моего, — лишние. 🙂

Teak ★★★★★
( 28.12.06 18:02:01 MSK )
Ответ на: комментарий от Teak 28.12.06 17:38:44 MSK

ув. тов. Teak прочтите пост выше.

Ex ★★
( 28.12.06 18:07:45 MSK )
Ответ на: комментарий от Ex 28.12.06 18:07:45 MSK

> ув. тов. Teak прочтите пост выше.

Не, Ex, извини, но ты там фигню написал. Заморачиваться со структурой в функции стоит только если ты ее всю будешь возращать (структурка небольшая). А создавать ее динамически, а потом возвращать указатель, имхо хреновая идея. Кто-то использующий твою функцию должен знать и помнить о том, что ему где-то free надо втыкать, лишний, тяжелый malloc, и ради чего все?

shumer ★
( 28.12.06 18:51:10 MSK )
Ответ на: комментарий от Teak 28.12.06 18:02:01 MSK

> Согласен, все посты в этой теме, кроме первого, моего, — лишние. 🙂

ты не указал решение с возвратом структуры. Оно непопулярно, потому что этого не было в ранних компиляторах и про это не написано в К&Р Но для 2-х полей оно оптимально

dilmah ★★★★★
( 28.12.06 18:59:37 MSK )
Ответ на: комментарий от dilmah 28.12.06 18:59:37 MSK

> Оно непопулярно, потому что этого не было в ранних компиляторах и про это не написано в К&Р

Вот хохма на эту тему, совершенно случайно наткнулся:

2.2: I heard that structures could be assigned to variables and passed to and from functions, but K&R I says not.

K&R I was wrong; they hadn’t actually learned C very well before writing the book. Later, Ritchie got a job at Bell Labs, and worked closely with the authors of C, allowing the 2nd edition of the book to be much more accurate. (Kernighan already worked at Bell Labs, as a video game developer.)

Как вернуть два значения из функции c

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

package main 
import "fmt" 

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

func vals() (int, int)  return 3, 7 > 
func main()  

Здесь функция возвращает два разных значения и присваивает их переменным a,b . Это называется множественное присваивание.

a, b := vals() fmt.Println(a) fmt.Println(b) 

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

_, c := vals() fmt.Println(c) > 
$ go run multiple-return-values.go 3 7 7 

Принятие переменного количества аргументов — еще одна приятная особенность функций Go; Рассмотрим это дальше.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *