C의 함수에서 여러 값을 반환하려면 어떻게해야합니까?


87

결과 int와 결과를 생성하는 함수가있는 경우 함수 string에서 둘 다 반환하려면 어떻게해야합니까?

내가 알 수있는 한, 함수 이름 앞의 유형에 의해 결정된대로 한 가지만 반환 할 수 있습니다.


6
으로 string할 당신은 "나는 C를 사용하고 ++이이 의미 std::string클래스"또는 "나는 C를 사용하고 있는데 이것은이다 char *포인터 나 char[]배열입니다."
Chris Lutz

글쎄, 내 특별한 경우에는 두 개의 정수였습니다. 하나는 내가 비교 한 '점수'에 대한 것이고 다른 하나는 최대 점수가 발견 된 곳의 '인덱스'에 대한 것입니다. 난 그냥 더 일반적인 경우 여기를 문자열 예제를 사용하는 원
토니 스타크에게

참조로 문자열을 전달하고 int를 돌려줍니다. 가장 빠른 방법. 구조가 필요하지 않습니다.
Stefan Steiger

1
두 가지 결과를 반환하는 함수가 두 가지 이상을 수행하지 않습니까? 밥 삼촌이 뭐라고했을까요?
Duncan

답변:


123

나는 당신 string이 무엇인지 모르지만 그것이 자체 메모리를 관리한다고 가정 할 것입니다.

두 가지 솔루션이 있습니다.

1 : struct필요한 모든 유형을 포함 하는 a 를 반환 합니다.

struct Tuple {
    int a;
    string b;
};

struct Tuple getPair() {
    Tuple r = { 1, getString() };
    return r;
}

void foo() {
    struct Tuple t = getPair();
}

2 : 포인터를 사용하여 값을 전달합니다.

void getPair(int* a, string* b) {
    // Check that these are not pointing to NULL
    assert(a);
    assert(b);
    *a = 1;
    *b = getString();
}

void foo() {
    int a, b;
    getPair(&a, &b);
}

어떤 것을 사용하기로 선택하는지는 주로 당신이 더 좋아하는 의미에 대한 개인적인 선호도에 달려 있습니다.


7
반환 값이 얼마나 관련되어 있는지에 더 많이 달려 있다고 생각합니다. int가 오류 코드이고 문자열이 결과 인 경우 이들을 구조체에 함께 넣어서는 안됩니다. 그것은 단지 어리석은 일입니다. 이 경우 함수가 자체 문자열 및 / 또는 return을 할당하는 것이 절대적으로 중요하지 않는 한 int를 반환하고 문자열을 a char *및 a size_t로 길이 로 전달합니다 NULL.
Chris Lutz

@Chris 나는 당신과 완전히 동의하지만 그가 필요로하는 변수의 사용 의미를 알지 못합니다.
Travis Gockel

좋은 지적 크리스. 지적 할 가치가있는 또 다른 것은 가치 대 참조입니다. 예제에 표시된 것처럼 구조체를 잘못 반환하지 않으면 반환시 복사본이 만들어집니다. 맞습니까? (저는 C에서 약간 흔들립니다) 다른 방법은 참조에 의한 통과를 사용하므로 더 많은 메모리를 할당 할 필요가 없습니다. 물론 구조체는 포인터를 통해 반환되고 동일한 이점을 공유 할 수 있습니다. (제대로 메모리를 할당 확인하는 물론이 모든)
RTHarston

@BobVicktor : C에는 참조 의미 체계가 전혀 없으므로 (C ++ 전용) 모든 것이 값입니다. 두 포인터 용액 (# 2) 함수에 대한 포인터의 사본을 통과 getPair한 후 역 참조 . 수행중인 작업 (실제로 C 질문 인 경우 OP는 명확하지 않음)에 따라 할당이 문제가 될 수 있지만 일반적으로 C ++ 영역 (반환 값 최적화는이 모든 것을 저장함)과 C 영역에서 데이터가 아닙니다. 복사본은 일반적으로 명시 적으로 발생합니다 ( strncpy또는 무엇이든).
Travis Gockel 2019

@TravisGockel 수정 해 주셔서 감사합니다. 포인터가 사용된다는 사실을 언급하고 있으므로 값을 복사하지 않고 이미 할당 된 것을 공유합니다. 그러나 그것이 C에서 pass-by-reference라고 적절하게 부르지 않는다고 말하는 것이 옳습니다. 그리고 다른 훌륭한 지식에 대해서도 감사드립니다. 저는 언어에 대해이 작은 것들을 배우는 것을 좋아합니다. :)
RTHarston

10

Option 1: int 및 string을 사용하여 구조체를 선언하고 구조체 변수를 반환합니다.

struct foo {    
 int bar1;
 char bar2[MAX];
};

struct foo fun() {
 struct foo fooObj;
 ...
 return fooObj;
}

Option 2: 포인터를 통해 둘 중 하나를 전달하고 포인터를 통해 실제 매개 변수를 변경하고 다른 하나를 평소와 같이 반환 할 수 있습니다.

int fun(char **param) {
 int bar;
 ...
 strcpy(*param,"....");
 return bar;
}

또는

 char* fun(int *param) {
 char *str = /* malloc suitably.*/
 ...
 strcpy(str,"....");
 *param = /* some value */
 return str;
}

Option 3: 옵션 2와 유사합니다. 포인터를 통해 둘 다 전달하고 함수에서 아무것도 반환하지 않을 수 있습니다.

void fun(char **param1,int *param2) {
 strcpy(*param1,"....");
 *param2 = /* some calculated value */
}

옵션 2와 관련하여 문자열의 길이도 전달해야합니다. int fun(char *param, size_t len)
Chris Lutz

이제 기능이 수행하는 작업에 따라 다릅니다. 함수가 어떤 종류의 결과를 char 배열에 넣는 지 알면 충분한 공간을 할당하고 함수에 전달할 수 있습니다. 길이를 전달할 필요가 없습니다. strcpy예를 들어 우리가 사용하는 것과 비슷한 것 입니다.
codaddict

7

결과 유형 중 하나가 문자열 (그리고 C ++이 아닌 C를 사용하고 있음)이므로 포인터를 출력 매개 변수로 전달하는 것이 좋습니다. 사용하다:

void foo(int *a, char *s, int size);

다음과 같이 호출하십시오.

int a;
char *s = (char *)malloc(100); /* I never know how much to allocate :) */
foo(&a, s, 100);

일반적으로 함수 자체가 아닌 호출 함수 에서 할당을 수행하는 것을 선호 하므로 다른 할당 전략에 대해 최대한 개방 될 수 있습니다.


6

두 가지 접근 방식 :

  1. 반환 값을 포인터로 전달하고 함수 내에서 수정하십시오. 함수를 void로 선언하지만 포인터로 전달 된 값을 통해 반환됩니다.
  2. 반환 값을 집계하는 구조체를 정의합니다.

반환 값이 너무 많으면 지루할 수 있지만 # 1은 무슨 일이 일어나고 있는지에 대해 조금 더 분명하다고 생각합니다. 이 경우 옵션 # 2는이 목적을위한 특수 구조체를 만드는 데 정신적 오버 헤드가 있지만 꽤 잘 작동합니다.


1
포스터를 사용하기 때문에 비록 C는 ;-) 참조가되지 않습니다 string, 가정 안전 할 수있는 C ++ ...
트래비스 Gockel

완전히 잊었습니다! 포인터를 사용하도록 내 대답을 수정했지만 C ++ 땅에 너무 오랫동안 머물 렀습니다. :)
James Thompson

6

구조체를 만들고 내부에 두 개의 값을 설정하고 구조체 변수를 반환합니다.

struct result {
    int a;
    char *string;
}

char *프로그램에 공간을 할당 해야합니다.


3

함수 매개 변수로 포인터를 사용하십시오. 그런 다음이를 사용하여 여러 값을 반환합니다.


2

함수에 대한 참조로 매개 변수를 전달합니다.

예 :

 void incInt(int *y)
 {
     (*y)++;  // Increase the value of 'x', in main, by one.
 }

또한 전역 변수를 사용하지만 권장하지 않습니다.

예:

int a=0;

void main(void)
{
    //Anything you want to code.
}

4
void main(void)오, 어떻게 화상!
Chris Lutz

무슨 말이야? @ Chris Lutz
Badr

6
main상태 코드를 반환해야합니다. * nix에서는으로 선언하는 것이 더 일반적 int main(int argc, char *argv[])이며 Windows에는 유사한 규칙이 있다고 생각합니다.
Duncan

2

한 가지 방법은 매크로를 사용하는 것입니다. 이것을 헤더 파일에 넣으십시오.multitype.h

#include <stdlib.h>

/* ============================= HELPER MACROS ============================= */

/* __typeof__(V) abbreviation */

#define TOF(V) __typeof__(V)

/* Expand variables list to list of typeof and variable names */

#define TO3(_0,_1,_2,_3) TOF(_0) v0; TOF(_1) v1; TOF(_2) v2; TOF(_3) v3;
#define TO2(_0,_1,_2)    TOF(_0) v0; TOF(_1) v1; TOF(_2) v2;
#define TO1(_0,_1)       TOF(_0) v0; TOF(_1) v1;
#define TO0(_0)          TOF(_0) v0;

#define TO_(_0,_1,_2,_3,TO_MACRO,...) TO_MACRO

#define TO(...) TO_(__VA_ARGS__,TO3,TO2,TO1,TO0)(__VA_ARGS__)

/* Assign to multitype */

#define MTA3(_0,_1,_2,_3) _0 = mtr.v0; _1 = mtr.v1; _2 = mtr.v2; _3 = mtr.v3;
#define MTA2(_0,_1,_2)    _0 = mtr.v0; _1 = mtr.v1; _2 = mtr.v2;
#define MTA1(_0,_1)       _0 = mtr.v0; _1 = mtr.v1;
#define MTA0(_0)          _0 = mtr.v0;

#define MTA_(_0,_1,_2,_3,MTA_MACRO,...) MTA_MACRO

#define MTA(...) MTA_(__VA_ARGS__,MTA3,MTA2,MTA1,MTA0)(__VA_ARGS__)

/* Return multitype if multiple arguments, return normally if only one */

#define MTR1(...) {                                                           \
    typedef struct mtr_s {                                                    \
      TO(__VA_ARGS__)                                                         \
    } mtr_t;                                                                  \
    mtr_t *mtr = malloc(sizeof(mtr_t));                                       \
    *mtr = (mtr_t){__VA_ARGS__};                                              \
    return mtr;                                                               \
  }

#define MTR0(_0) return(_0)

#define MTR_(_0,_1,_2,_3,MTR_MACRO,...) MTR_MACRO

/* ============================== API MACROS =============================== */

/* Declare return type before function */

typedef void* multitype;

#define multitype(...) multitype

/* Assign return values to variables */

#define let(...)                                                              \
  for(int mti = 0; !mti;)                                                     \
    for(multitype mt; mti < 2; mti++)                                         \
      if(mti) {                                                               \
        typedef struct mtr_s {                                                \
          TO(__VA_ARGS__)                                                     \
        } mtr_t;                                                              \
        mtr_t mtr = *(mtr_t*)mt;                                              \
        MTA(__VA_ARGS__)                                                      \
        free(mt);                                                             \
      } else                                                                  \
        mt

/* Return */

#define RETURN(...) MTR_(__VA_ARGS__,MTR1,MTR1,MTR1,MTR0)(__VA_ARGS__)

이를 통해 함수에서 최대 4 개의 변수를 반환하고 최대 4 개의 변수에 할당 할 수 있습니다. 예를 들어 다음과 같이 사용할 수 있습니다.

multitype (int,float,double) fun() {
    int a = 55;
    float b = 3.9;
    double c = 24.15;

    RETURN (a,b,c);
}

int main(int argc, char *argv[]) {
    int x;
    float y;
    double z;

    let (x,y,z) = fun();

    printf("(%d, %f, %g\n)", x, y, z);

    return 0;
}

이것이 인쇄하는 것입니다.

(55, 3.9, 24.15)

가변 매크로 및 for-statement 변수 선언에 C99 이상이 필요하기 때문에 솔루션은 이식성이 떨어질 수 있습니다. 하지만 여기에 게시하는 것이 충분히 흥미 롭다고 생각합니다. 또 다른 문제는 잘못된 값을 할당하면 컴파일러가 경고하지 않으므로주의해야합니다.

추가 예제와 공용체를 사용하는 코드의 스택 기반 버전은 내 github 저장소 에서 사용할 수 있습니다 .


1
더 큰 이식성 문제는 비표준 기능입니다__typeof__
MM

typeof 대신 sizeof를 사용할 수 있습니다. 반환 값의 크기를 가져 와서 모두 저장할 수있을만큼 큰 배열을 할당합니다. 그런 다음 반환 할 수 있습니다.
Klas. S
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.