Cの関数から複数の値を返すにはどうすればよいですか?


87

結果intと結果を生成する関数がある場合、関数stringから両方を返すにはどうすればよいですか?

私が知る限り、関数名の前の型によって決定されるように、私が返すことができるのは1つだけです。


6
stringあなたは「私はC ++を使用していますし、これはある意味ですかstd::stringクラス」または「私はCを使用していますし、これはchar *ポインタまたはchar[]配列。」
クリス・ルッツ

ええと、私の特定のケースでは、それらは2つのintでした。1つは私が比較していたものの「スコア」用で、もう1つはその最大スコアが見つかった場所の「インデックス」用です。より一般的なケースのために、ここで文字列の例を使用したかった
Tony Stark

参照によって文字列を渡し、intを返します。最速の方法。構造体は必要ありません。
Stefan Steiger

1
2つの結果を返す関数は、複数のことを実行していませんか?ボブおじさんは何と言いますか?
ダンカン

回答:


123

あなたstringが何であるかはわかりませんが、それが独自のメモリを管理していると仮定します。

2つの解決策があります。

1:struct必要なすべてのタイプを含むを返します。

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がエラーコードであり、stringが結果である場合、これらを構造体にまとめるべきではありません。それはばかげています。その場合、関数が独自の文字列を割り当てたり、を返したりすることが絶対に不可欠でない限り、intを返し、長さの文字列をachar *およびaとして渡します。size_tNULL
クリス・ルッツ

@Chris私はあなたに完全に同意しますが、彼が必要とする変数の使用セマンティクスについてはわかりません。
Travis Gockel 2010

良い点クリス。私が指摘する価値があると思うもう一つのことは、価値対参照です。例に示されているように構造体を返すのを間違えない場合は、返却時にコピーが作成されることを意味しますが、それは正しいですか?(私はCで少し不安定です)他の方法は参照渡しを使用するため、より多くのメモリを割り当てる必要はありません。もちろん、構造体はポインタを介して返され、同じ利点を共有することができますか?(適切にメモリを割り当てることを確認すること、そしてもちろんそのすべて)
RTHarston

@BobVicktor:Cには参照セマンティクスがまったくないため(C ++専用)、すべてが値です。二つのポインタの溶液(#2)は、関数へのポインタのコピーを渡してgetPair、次いで逆参照します。あなたがしていることに応じて(これが実際にCの質問であるかどうかOPは決して明確にされません)、割り当ては懸念されるかもしれませんが、通常はC ++ランド(戻り値の最適化はこれをすべて保存します)とCランドのデータにはありませんコピーは通常、明示的に(経由strncpyなどで)発生します。
TravisGockel19年

@TravisGockel訂正していただきありがとうございます。ポインタが使用されているという事実に言及していたので、値をコピーするのではなく、すでに割り当てられているものを共有するだけです。しかし、あなたはそれがCでは適切に参照渡しと呼ばれていないと言っているのは正しいです。そして他の素晴らしい知識の塊にも感謝します。私は言語についてこれらのささいなことを学ぶのが大好きです。:)
RTHarston

10

Option 1:intとstringを使用して構造体を宣言し、構造体変数を返します。

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

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

Option 2:ポインタを介して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)
クリス・ルッツ

これは、関数が何をしているかによって異なります。関数がchar配列にどのような結果を詰め込むかがわかっている場合は、十分なスペースを割り当てて関数に渡すことができます。長さを渡す必要はありません。strcpyたとえば、私たちが使用する方法に似たもの。
codaddict 2010

7

結果タイプの1つは文字列であるため(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

2つの異なるアプローチ:

  1. 戻り値をポインターで渡し、関数内で変更します。関数をvoidとして宣言しますが、ポインターとして渡された値を介して返されます。
  2. 戻り値を集約する構造体を定義します。

戻り値が多すぎると面倒になる可能性がありますが、#1は何が起こっているかについてもう少し明白だと思います。その場合、オプション#2はかなりうまく機能しますが、この目的のために特殊な構造体を作成することには精神的なオーバーヘッドが伴います。


1
ポスターは使わ以来もののCは、;-)参照を持っていないstring、仮定しても安全であるかもしれないC ++ ...
トラヴィス・ゴッケル

それを完全に忘れました!ポインターを使用するように回答を変更しましたが、明らかにC ++の土地に長く滞在していました。:)
ジェームストンプソン

6

構造体を作成し、内部に2つの値を設定して、構造体変数を返します。

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)やけど!
クリス・ルッツ

どういう意味ですか?@クリス・ルッツ
Badr 2010

6
mainステータスコードを返すことになっています。* nixの下では、次のように宣言するのが一般int main(int argc, char *argv[])的です。Windowsにも同様の規則があると思います。
ダンカン

2

1つのアプローチは、マクロを使用することです。これをヘッダーファイルに配置します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ステートメント変数宣言にC99以降を必要とするため、移植性が低い場合があります。でも、ここに投稿するのは面白かったと思います。もう1つの問題は、間違った値を割り当ててもコンパイラが警告を表示しないため、注意が必要なことです。

追加の例、およびユニオンを使用したスタックベースのコードは、私のgithubリポジトリで入手できます


1
より大きな移植性の問題は、非標準機能です__typeof__
MM

typeofの代わりに、おそらくsizeofを使用できます。戻り値のサイズを取得し、それらすべてを格納するのに十分な大きさの配列を割り当てます。その後、あなたはそれを返すことができます。
クラス。S
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.