• ベストアンサー

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

知り合いの方から関数の戻り値をえるときに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

  • systemの戻り値を取得する方法

    system関数で int main(){ int i; i = system("./test.exe"); printf("i=%08x", i); } として、test.exeを呼び出しています。 test.exeでは、 int test(){ clock_t a, b; int i, j; j = 0; a = clock(); for (i=0;i<0xFFFFFF;i++) j++; b = clock(); return (int) (b-a); } int main(){ int i; i = test(); return i; } として、iの値を戻しています。 試したところでは、i = 63が戻り値となるはずですが、 0x3f00という値がsystem関数から渡されています。 systemだと、シェルも動作できるのでこの戻り値はOS(or cygwin)からの戻り値になると考えています。この理解で正しいでしょうか? また、この場合のi = 63を戻す方法はあるのでしょうか? 環境はXP Home + cygwin + gcc4.3.0です。

  • 関数の戻り値に関数のアドレスを返すできませんか?

    戻り値に関数のアドレスを与える方法が良く分かりません. ひとまず,以下のように動くプログラムを作りたいと思っています.  f2(1,2)(1); //このように引数の()を二種類に分けたいのですが 無理でしょうか?? プログラムは下のように作って実験しているのですが良く分かりません・・ どなたか分かる方居たら教えてください. int f1(int x , int y){ return 0; } ????? int ff(int x){ return f; };

  • 配列を戻り値にして逆行列を求める関数

    #include(stdio.h) int main(){ double a[4][4]={{1,2,0,-1},{-1,1,2,0},{2,0,1,1},{1,-2,-1,1}}; //入力用の配列 double inv_a[4][4]; //ここに逆行列が入る double buf; //一時的なデータを蓄える int i,j,k; //カウンタ int n=4; //配列の次数 //単位行列を作る for(i=0;i<n;i++){ for(j=0;j<n;j++){ inv_a[i][j]=(i==j)?1.0:0.0; } } //掃き出し法 for(i=0;i<n;i++){ buf=1/a[i][i]; for(j=0;j<n;j++){ a[i][j]*=buf; inv_a[i][j]*=buf; } for(j=0;j<n;j++){ if(i!=j){ buf=a[j][i]; for(k=0;k<n;k++){ a[j][k]-=a[i][k]*buf; inv_a[j][k]-=inv_a[i][k]*buf; } } } } //逆行列を出力 for(i=0;i<n;i++){ for(j=0;j<n;j++){ printf(" %f",inv_a[i][j]); } printf("\n"); } } という、4次元正方行列の逆行列を求めるプログラムがあるのですが これを複数の行列の逆行列が求められる関数にする場合を教えてください・・行列は配列で作ってるのでよくわかりません、お願いします

  • 関数呼び出しについて

    今cygwin 上でC++の勉強をしているのですが 以下の2つのプログラムの違いがよくわかりません どなたかよろしくお願いします <プログラム1> #include<iostream> using namespace std; int a(); int main(){ cout << abs();  return 0;} int a(){ cout << "test\n";  return 1;} <プログラム2> #include<iostream> using namespace std; int a(int i); int main(){ cout << a(1);  return 0;} int a(int i){ cout << "test\n";  return i;} プログラム1では関数a()内の"test"が出力されるのですが プログラム2ではa(int i)内の"test"は出力されません。 この違いはどこにあるのでしょうか? 同じプログラムでint a() と int a(int i)を double a() と double a(double d)にすると この違いは生じません。なぜaの戻り値をint に設定したときだけ この違いが生じるのでしょうか?

  • main関数の戻り値

    C言語のmain関数の戻り値はint型ですよね。 私もそういう決まりだと思って守ってきました。 しかし、「mainが戻り値を返すって、どこに返すの?」ということが、私は理解できていません。 私が調べたところでは、「ホスト実行環境」という言葉がこの問題に関係あるようですが、この言葉の意味はよくわからないですし、似た言葉で「ホスト環境」ということばがあるのですが意味も関係もわかりません。 これらは、OSとは違うと思うんですが、自信はありません。 それでも、ない知識を振り絞っていろいろ考えてみると、次のようなことらしいのですが、正しいでしょうか。 ・OSはプログラムの実行に先立ちホスト実行環境を作る。 ・静的記憶域のオブジェクトを初期化するのはホスト実行環境である。 ・関数が、main関数を呼ぶことは可能である。(以下では、main関数が関数から呼ばれる場合は除く。) ・main関数を呼ぶのは、ホスト実行環境が行なう。 ・main関数の中のreturnによってプログラムが終了するのと、exit関数でプログラムが終了することに違いはない。 ・main関数の戻り値は、ホスト実行環境に返される。 ・returnによってホスト実行環境に返される値は、int型である限りなんでもよい。 ・exitによってホスト実行環境に返される値は、int型である限りなんでもよい。 ・必ずexitで値が返されるならば、main関数の中にreturnはなくてもよい。 main関数からの戻り値をどうしようと構わないんだと思うんですが、皆さんの経験の中で、実例としてこういうふうに使われる、というのは何かないのでしょうか。 (ホスト実行環境に値が返される、といっても無視するのでは意味はないと思うのです。 その値の使用例としては、 0が返ってくると「プログラムは正常終了しました。」と表示するとか、0以外の値が返されると別のプログラムが走るとか、 そういうことだと思うんですが。)

  • 戻り値を返す関数の前に(void)を付ける

    今日会社で 変数 = (void)戻り値のある関数 #具体的な例としては下記一例を参照 と、ソースで書かれているものがあったのですが、 コメント等では、 「戻り値を明示しないときに(void)を付けて使用する」 と、書いてあります。 戻り値のある関数の前で(void)を付けると、戻り値が明示されなくなるのでしょうか?? または、このような動作にはならないのでしょうか。 自分自身、ソースの内容をうる覚えになっておりますので、 確認をしながらアップしていきたいと思いますので、 宜しくお願い致します。 /*一例*/ ・num1とnum2を足した値を返す int PlusPoint(int num1 , num2); ・PlusPointの値を格納する int Sum ◇ソース◆ #include <stdio.h> #include "Plus.h" int main(void) { int Sum; Sum = (void)PlusPoint; if(Sum == 2) { printf("false"); exit(0); } printf("true"); return 0; } *PlusPoint関数は外部で定義されている *プロトタイプ宣言はPlus.hに定義されいるものとする

  • 関数の戻り値がこうなる理由が分かりません。

    かなり簡単なプログラムだと思いますが、関数内で出力した場合と、呼び出し側で出力した場合で値が異なります。理由が分からないので教えていただけますか? #include <stdio.h> #include <stdlib.h> Table(int num, double ltable) { double temp[] = { 0.99852, // 1 0.99410, // 2 0.98677 // 3 }; ltable=temp[num]; printf("%f\n",ltable); } main (int argc, char *argv[]) { int num ; double ltable ; num=1; Table(num, ltable); printf("%f\n",ltable); } 実行結果 0.994100 0.000000

  • 二次元配列の引数渡し

    二次元配列を関数の引数として渡し、 その配列を戻り値として呼び出し元の変数に返したいです。 具体的にはルンゲクッタ法で微分する関数の引数を行列で扱いたいです。 for (i=0;i<N;i++){ for (j=0;j<N;j++){ y[i][j]=rand(); } }   K1=h*ff(t,y); K2=h*ff(t+h/2,y+K1/2); K3=h*ff(t+h/2,y+K2/2); K4=h*ff(t+h,y+K3); Ka=(K1+2*K2+2*K3+K4)/6; double ff(int t,double y[][]){ ここで return y[][]; のような形で二次元配列を戻り値として変数に返したいのです。 } C言語初心者なのでいまいちよくわかりません、 宜しくお願いいたします。

  • 関数の戻り値が複数ある場合

    C言語初心者です。 関数の戻り値を返す場合、return 変数名;と記述しますよね。 配列を返す場合、ポインタを使用しないで返すにはどうしたらいいですか? 例えば合計と平均を計算する関数があり その結果を配列に入れてmainに返すなど。 以前他の方の質問のコメントに 配列の要素が固定であれば、構造体にして返す方法もあります。 struct array { int x[10]; }; struct array func() {  struct array a;  ...  return a; } -- とあったのですが、構造体の要素が全てint型ならば 配列ではダメなのですか?

  • 2次元配列を戻り値とする関数?

    いつもお世話になっています。 角度を入力すると、 2×2の2次元配列を戻す 関数を作りたいのですが、 コンパイルすると、 戻り値の型のところで、 不正な変換だというエラーが出て うまく行きません。 参考書を何度も読み直して 戻り値の型をポインタのポインタにするなど、 いろいろトライしてみたのですが、うまく行きません。 typedef を使う方法も考えましたが、 他にもっとすっきりする方法はないでしょうか? どなたか参考URLをお教えくださるか、 解決策を教えてください。 よろしくお願いします。 ちなみに、この関数は大凡下記の通りです。 double** Matrix(double sita) { double mat[2][2]; mat[0][0]= cos(sita); mat[0][1]= sin(sita); mat[1][0]=-sin(sita); mat[1][1]= cos(sita); return mat; }

専門家に質問してみよう