• ベストアンサー

関数から戻り値を得る方法について

知り合いの方から関数の戻り値をえるときにreturn以外に例えば File(&i,&k);←関数呼び出し int File(int **j, double *k)←関数 {      ~      ~ } とやると(double *k)は関数からの出力として、呼び出しているFile(&i,&k)の(&k)に返されると聞きました。しかも、戻り値はint File()のintに依存しないと言われて試してみたのですが、どうもうまくいきません。 正しいやり方知っている方いましたら、教えて下さい。あと複数戻り値を返したいときはどうすればいいんでしょうか?

質問者が選んだベストアンサー

  • ベストアンサー
  • ency
  • ベストアンサー率39% (93/238)
回答No.6

# すみません。 # 説明が長くなったので2回に分けます。。。 # まず前半部分です。 まず、ポインタについてですが、私は「型」であるというふうに理解しています。 int型やdouble型があるように「ポインタ型」があるというわけです。 ただ、このポインタ型は単独で存在するわけではなく、「○○型へのポインタ型」というようにベースとなる型があって、それをポイントする型として定義されています。 「int型へのポインタ型」「double型へのポインタ型」といった感じですね。 「『int型へのポインタ型』へのポインタ型」なんてのもありです。 そして、「int型へのポインタ型」と「double型へのポインタ型」は別の型になります。 「int型へのポインタ型」と「『int型へのポインタ型』へのポインタ型」も別の型です。 要するにベースとなる型が違えば、同じポインタ型という名であっても別物というわけです。 で、ポインタは「型」ですから、変数や値が存在するんです。 通常、「ポインタ」といった場合、「ポインタ型」「ポインタ型の変数」「ポインタ型の値」をごちゃまぜにして使っているように思います。 分かっている人には、型なのか変数なのか値なのか、話の流れからパッと判断できてしまいますからね。 で、ポインタがアドレスというのは、「ポインタ型の値としてアドレスを使用している (実装が多い)」ということです。 # ANSI C の仕様上はポインタがアドレスであるとはどこにも書いてありません。 # K&R には怪しい記述がありますけど。。。 ですので、「int型のポインタ型の値」を「double型のポインタ型の変数」に代入することができないのはそういうことです。

その他の回答 (6)

  • ency
  • ベストアンサー率39% (93/238)
回答No.7

# ency です。 # No6 のつづきです。 さて、前置きが長くなってしまいました。 以下の2つの関数はいずれも double型の値「3.14」を返します。 ---------------------------------------- double getDouble1( void ) { return 3.14; } ---------------------------------------- void getDouble2( double *pValue ) { *pValue = 3.14; } ---------------------------------------- 以下の2つの関数はいずれも int型の値「10」が設定されたポインタ (値) を返します。 ---------------------------------------- int *getIntPtr1( void ) { static int value = 10 return &value; } ---------------------------------------- void getIntPtr2( int **ppValue ) { static int value = 10 *ppValue = &value } ---------------------------------------- このように、「○○型」の値を返すのと、「○○型へのポインタ型」の変数を引数に設定するとで、同じ効果を期待できるわけです。 ただ、配列の場合に限って注意が必要です。 「char型の配列」の場合、戻り値で返すことを考えれば「char型へのポインタ型」の値を返すことになります。 ---------------------------------------- char *getCharArray1( void ) { static char array[6] = "Hello"; return array; } ---------------------------------------- しかし、引数で返す場合には「char型の配列へのポインタ」にしなければいけません。 ---------------------------------------- void getCharArray2( char (*pArray)[6] ) { static char array[6] = "Hello"; *pArray = array; } ---------------------------------------- なぜ配列の場合だけこのような違いがでるのかというと、C の配列に関する妙な規則のためです。 つまり、 「配列は (式の中では) 先頭要素へのポインタに読み替えられる」 というあまりに有名な規則ですね。 でも、コンパイラが勝手に読み替えるだけで、配列とポインタはやっぱり違うものなんです。 上記の例で見ると、配列がその先頭要素のポインタに読み替えられた結果、getArray1() は「char型の配列を返す」→「その先頭要素へのポインタを返す」⇒「char型へのポインタを返す」ことになります。 一方で、getArray2() では、「char型の配列を返す」→「char型の配列へのポインタを設定させる」ことになります。 これは配列→ポインタの読替えが再帰的には行われないためです。 ちなみに、面倒なことに以下の二つは別ものなんですね。 # ベース部分 (配列の要素数) が違うため、ポインタ型も別の型に # なってしまうんです。 char (*array1)[10]; /* 要素数10の配列へのポインタ */ char (*array2)[20]; /* 要素数20の配列へのポインタ */ ちょっと話が込み入ってきてしまいましたね。 とりあえず、配列の手前くらいまではご理解いただけたでしょうか。 配列の話は簡単に理解できないかもしれません。 # 私がそうだっただけですけど。。。 # というか、簡単に理解されたら、私の立場が。。。 で、複数の値を返したい場合についてですが、こちらについてはすでに何名かのかたが回答されているとおりで、 1. 引数を使って値を返す。 2. 構造体を使って、戻り値で返す。 といった方法が考えられます。 ちなみに私は、引数を使うことが多いですね。 構造体を使う場合でも、構造体のポインタを引数に渡して値を設定してもらう形をとることが多いと思います。 # 構造体を戻り値で返すことは、あまりしません。。。 # なぜかと言われても…これといった理由もなく、なんとなくかなぁ。。。 それでは、長々と失礼しました。

fooejiio
質問者

お礼

とても丁寧に詳しく回答していただきありがとうございました。かなり参考になりました(^ ^)なんとなく感じはつかめたので、あとはモノにできるようがんばります!!

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.5

> あと複数戻り値を返したいときはどうすればいいんでしょうか? 複数の値を返すのであれば、一番簡単なのは構造体にまとめて、その構造体を値返しする方法です。 たとえば、 struct result {  int a;  double b; }; struct result func(void) {  struct result t;  t.a = 1;  t.b = 2.0;  return t; } といった感じです。

fooejiio
質問者

お礼

たしかに構造体のほうが、扱いたい値がたくさんあるときは楽ですね。回答ありがとうございました。

  • rentahero
  • ベストアンサー率53% (182/342)
回答No.4

アドレス渡しそのものの説明は#3で行ったとおりですが。 あなたが混乱している元凶は、質問にあげられているFile関数の定義がややこしいためです。 int File(int **j, double *k) という定義ですが、 この定義で、引数 j, k の両方がアドレス渡しになっているのがややこしいのです。 **jは、ここでは二つの捉え方ができます。 int *j[100]のように定義されている、アドレスの配列である可能性と、もうひとつは、int j[100][100]のように定義されている二次元配列の可能性です。 入力として利用するということから、多分後者の二次元配列だと思います。 もうひとつ、戻り値のintに依存しない件ですが、 *kに値を入れると呼び出し元の変数を直接書き換えることになるため、「値が返される」ように感じますが、それは先ほど説明したとおり、違います。あくまでも、元の変数を書き換えているだけです。 厳密にいうと、戻り値というのは、return文で戻すもの(ここでの関数の値であるint)です。 あと、関数から戻ってきてほしい値が複数ある場合ですが、それは必要な個数の変数のアドレスを渡してやるか、返してほしい値を構造体にまとめて構造体のアドレスを渡してやるかのどちらかになりますね。 #2の方のおっしゃるとおり、あまり薦められませんが、グローバル変数を利用する方法もあります。

fooejiio
質問者

お礼

丁寧に回答してくださりありがとうございました。グローバル変数はできるだけ使わずにやってみます。

  • rentahero
  • ベストアンサー率53% (182/342)
回答No.3

ここしばらくポインタについての質問が多いような気がしますね。 C言語での関数では、他の言語(例えばpascalやFORTRANなど)にあるような参照渡しというのはなくて、すべてが値渡しなのです。 ここで出てくるポインタというのは、値渡しの上で参照渡しのような機能を実現するための「アドレス渡し」という方法です。 わざわざポインタというからわかんなくなるのです。ここでは、C言語の定義に正しく従って変数の「アドレス」を渡すというべきですね。 アドレス渡しでの値を書き換える例は#2にあげられているとおりとして、アドレスそのものに着目した例を挙げて説明します。 本来の値渡しの例をひとつ。 void a(int val) { printf("val=%d\n", val); /*↑valをそのまま値としてアクセス*/ printf("&val=%p\n", &val); /*↑valをアドレス演算子(&)を介してアドレスとしてアクセス(このアドレスは呼び出し元のアドレスとは違う)*/ return; } このとき、valの値が関数aに渡されます。違うメモリに値がコピーされているので、関数内部で値をどれだけいじっても、呼び出し元には全く関係ありません。 次にアドレス渡しの例をひとつ。 void b(int *addr) { printf("*addr=%d\n", *addr); /*↑addrを参照演算子(*)を介して内容にアクセス*/ printf("addr=%p\n", addr); /*↑addrをそのままアドレスとしてアクセス(このアドレスは呼び出し元のアドレスと同じ)*/ printf("&addr=%p\n", addr); /*↑addrがコピーされたアドレスを表示してみる(あまり意味はないが上の関数aとの比較のため) return; } このとき、addrの値(アドレス)が関数bに渡されます。同じメモリを指しているので(厳密にはメモリのアドレス番号そのものがコピーされたのですが)関数内部で、メモリの内容を書き換えると呼び出し元に影響します。 さて、ここで注意しておかないといけないのは、関数bでの値渡しは、メモリのアドレスだということです。つまり、実際には*addr(メモリの内容)ではなく、addrが引数なのです。 古い書き方で両方を書いてみるとよりよくわかります。 void a(val) int val; { printf("val=%d\n", val); printf("&val=%p\n", &val); return; } 関数aにおいて引数はvalです。 valの型はintです。 void b(addr) int *addr; { printf("*addr=%d\n", *addr); printf("addr=%p\n", addr); printf("&addr=%p\n", &addr); return; } 関数bにおいて引数はaddrです。 addrの型はint *(int型の変数としてアクセス可能なアドレス)です。 C++との互換性の問題であまり薦められない書き方なのですが、この書き方の方が、概念は理解しやすいと思います。上記の説明と関数の定義が直接一致しているのがわかると思います。 さて、アドレスが関数に渡されるとなぜよいのか。 渡される値がアドレスである以上、そのアドレスは、呼び出し元と呼び出された関数の間で同じメモリを共有するために利用できるのです。同じメモリを共有するということはすなわち、呼び出された関数でアドレスの指し示す変数の内容を書き換えることができるのです。 こうして、他の言語で実現できている「参照渡し」のような機能をC言語でも実現できるようになりました。 先の関数a,bを次のmainから読んでみた実行例は以下のとおり #include <stdio.h> void a(int); void b(int *); int main() { static int x=50; printf("x=%d\n", x); printf("&x=%p\n", &x); a(x); b(&x); return 0; } 実行結果 x=50 &x=0040A128 val=50 &val=0012FF88 *addr=50 addr=0040A128 &addr=0012FF88 注意:&valと&addrは関数に対してコピーされた変数のアドレスなのでたまたま同じアドレスになっています。このアドレスは「スタック」という特殊なメモリ領域です。 main関数で、xをstatic宣言して確保していますが、こうすれば明らかに違うアドレスになるためです。

  • nopo3
  • ベストアンサー率40% (8/20)
回答No.2

値渡しと参照渡しの理解は初心者には難しいですよね。 例を少し挙げますね。 /*********************************************/ int FuncA(int a){ a=10; return 0; } int FuncB(int *a){ *a=10; return 0; } void main(void){ int a=5; FuncA(a); print("%d\n",a); ・・・aは5です。値渡し FuncB(&a); print("%d\n",a); ・・・aは10です。参照渡し } /*********************************************/ 複数戻り値を返したい場合は、構造体を使用するか、関数の引数に複数設定、あとは奨めませんがグローバル変数を使用する方法ではないでしょうか。

参考URL:
http://wisdom.sakura.ne.jp/programming/c/c31.html
fooejiio
質問者

お礼

URLの説明も一緒に見て、値と参照の違いがわかりました。ありがとうございました。

  • cma3atgoo
  • ベストアンサー率35% (32/90)
回答No.1

int main() { int a,b; double c; a = 0; b = 0; c = 2.3; a = func(&b,&c); return 0; } int func(int *arg1 , double *arg2) { *arg1 = 3; *arg2 = 1.5; return 1; } この結果は(試してないので、実行できれば) a = 1 b = 3 c = 1.5 になるはず。 ポインタとか参照渡しとかいうものです。 詳しくは検索してみてください。 (int **j の **jを*j にするとうまくいくかも知れません。

fooejiio
質問者

お礼

わかりました。回答ありがとうございました。

関連するQ&A

専門家に質問してみよう