【C言語】戻り値が構造体の関数

このQ&Aのポイント
  • C言語で構造体の戻り値を扱う関数について質問があります。
  • 特定の関数で構造体を返す際にエラーが発生し、正しい返り値の扱い方を知りたいです。
  • 関数名に存在する間接演算子についても疑問を持っています。
回答を見る
  • ベストアンサー

【C言語】戻り値が構造体の関数

こんにちは。 授業でC原語を習っており、その課題で詰まった所についての質問です。 ※以下の関数については可能な限り簡略化しています。 typedef struct {   char name[10];   int height; } Person; Person *bin_search( const Person *key) {   return (&key); } 以上のような構造体と関数があった場合、bin_searchでkeyを返したいのですが、 return (&key); では、コンパイルするとその行について return from incompatible pointer type warning: function returns address of local variable とエラーが出てしまいます。 return (key); return (*key); などとりあえず試してはみたのですがどれも上手くいきません。 そもそも関数名の前に*(間接演算子?)があること自体がよくわかっていないです…… なので、返り値をどうすれば正しく機能してくれるのか教えていただけるとありがたいです。 ※課題で指定されているので、関数頭部については変更しないとします。

  • stiyl
  • お礼率50% (11/22)

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

  • ベストアンサー
  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.6

ポインタはややこしいですが、&や*が付いたらどんな型になるかを落ち着いて考えましょう。 > Person *bin_search( const Person *key) *がbin_search, keyに付いてますが、考え方としては 「Person *」を返す関数「bin_search」 「const Person *」型の引数「key」です。 とすると、 「&key」は「『const Person *』型へのポインタ(const Person **)」になるので、戻り値の「Person *」とは一致しません。それが > return from incompatible pointer type (互換性の無いポインタを返した)というエラーです。 「*key」は「『const Person *』型(= const Personへのポインタ)から参照される実体(const Person)」になるので、戻り値の「Person *」とは一致しません。そもそもポインタでは無いので、エラーメッセージも違っているはずです。 では、「key」はなにか、と言えば、「const Person(=変更できないPerson)へのポインタ」です。 戻り値は「Person(=変更もできるPerson)へのポインタ」なので「一致しません」 Person * → const Person *は暗黙の型変換が行われます。これは、参照先を読み書きできるように扱うか、読み込みのみで扱うかの違いなので、問題はありません。 しかし、逆はできません。 対処法は次のようなものがあります。それぞれに一長一短です。他の部分との組合せ等から、適切なものを選んでください。 ・戻り値をconst Person *にする。 ・引数をPerson *にする。 ・return時に Person *へキャストする。 > warning: function returns address of local variable これは別の話しです。 keyは、この関数が呼ばれたときに確保され、関数から戻ったら解放される「(関数内で自動の)ローカル変数」です。 &keyは、そのローカル変数のアドレスを示します。 そのローカル変数が解放されたら、その領域がどうなるかはわかりません。すぐに別の変数がその領域を使ってしまうかもしれません。 なので、「受け取ったアドレスが無意味になってるかもしれませんよ。大丈夫ですか?それを意図したプログラムですか?」という警告です。

stiyl
質問者

お礼

丁寧で細かい説明ありがとうございました。 おかげでプログロムは無事実行することが出来ました。

その他の回答 (11)

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

bsearchそのものを実装する場合でも、 void *bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void*, const void*)); なので、最後に結果をreturnするときには同じ問題に直面します。 今回の問題に関して言えば、 return (Person*)key; とするのが一番妥当です。 仮引数にはconstが付いているけれども、返却値にはconstがないのは、Cでは普通の仕様です。 bsearchだけでなく、strchr, strrchr, strstr, memchrなども同様です。 引数で指定した配列の内容を破壊しないことを保障する意味で仮引数にconstを付けているわけですが、実引数がconst修飾されているかどうかは関数側からはわかりません。 const修飾無しの返却値を返すことで、const修飾無しの変数でもconst修飾ありの変数でも結果を受けることができるようになります。

stiyl
質問者

お礼

丁寧で細かい説明ありがとうございました。 おかげでプログロムは無事実行することが出来ました。

  • wormhole
  • ベストアンサー率28% (1622/5659)
回答No.11

もちろん、そんな関数に意味があるとは私も思ってないです>#10 でも#2の補足を読む限りは質問者さんはそういう仕様を考えていらっしゃるようなので。 検索キーはnameだったりするんじゃないかなと。

stiyl
質問者

補足

質問内容には関係ないと思い省略したのですが、細かく表記するべきだったでしょうか。 実際に作る関数は Person *bin_search( const Person *key, const Person *base,         size_t nmemb,       int (*compar)(const Person *, const Person *) ) で宣言され、引数keyはキー値へのポインタ、baseは比較対象へのポインタ、nmembは探索対象となる配列要素数、comparは比較用関数へのポインタとし、 Person *p; で宣言したpに結果を代入するものです。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.10

よしんば「実際に検索をする」関数であっても, 「検索キーをそのまま返す」検索関数に意味があると思えないのです>#8. ふつうは「検索して見付けたもの」を返すのでは?

回答No.9

詳細な説明はほかの方がしてくれているので、別の意見を。 とりあえず、プロトタイプは Person *bin_search( const Person *key, Person *data, size_t num) でないとまずいんじゃないかな。 それとプロトタイプからconstを外すというのが一番よいと思いますが プロトタイプでconstをつけるのは関数の中で値を書き換えないことの 意思表示でもあるので検索系の関数だし、 constをつけたままにして return するときに(Person *)で キャストしてしまうとか、戻り値そのものを int にして、配列の番号を返す仕様に変更する案もありかと思います。

stiyl
質問者

お礼

課題として関数宣言は指定されているので、constは外せなかったんですよ…… 説明ありがとうございました。

  • wormhole
  • ベストアンサー率28% (1622/5659)
回答No.8

検索せずにキーをそのまま返す検索関数って意味あるの?

noname#208507
noname#208507
回答No.7

関数名の前に付いている * は、戻り値のデータ型の一部です。つまり、この関数の戻り値のデータ型は Person * です。 引数は const Person * なので、警告文  return discards qualifiers from pointer target type はデータ型に付いている const を、戻り値で捨てている点を警告しています。 (たぶん、使われているのは gcc でしょうか) 下のように探索結果を返せば警告は出なくなります。key の内容は変えない方が分かりやすいプログラムになるでしょう。戻り値を free() することを忘れないよう注意してください。 Person *bin_search( const Person *key) {   Person *value = malloc(sizeof(Person));   if (value != NULL) {     ... 二分探索で見つけた値を value にセット ...   }   return value; } 話はそれますが、「二分探索」が課題なのですね。がんばってください。二分探索はシンプルなのに正しく作ることが難しいことで知られています。(最初にアルゴリズムが発表されてから、バグのないプログラムが出版されるまでに16年もかかっている!) 「珠玉のプログラミング」(ジョン・ベントリー, ピアソンエデュケーション, 2000) 「C言語による最新アルゴリズム事典」(奥村晴彦, 技術評論社, 1991) などの書籍が参考になります。

stiyl
質問者

お礼

丁寧で細かい説明とエール、ありがとうございました。 おかげでプログロムは無事実行することが出来ました。

  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.5

>Person *bin_search( const Person *key) bin_search関数の戻り値はPerson型へのポインターですね。よって、 >return (&key); // 戻り値の型はPerson **型 >return (*key); // 戻り値の型はPerson 型 これらが正しくないのはわかるのですが、 >return (key); これもダメでしたか? そのときのエラーメッセージは、どういう内容でしたか? # 関数ポインターという用語があることは間違いないが、このシチュエーションで使うものかどうかは微妙。

stiyl
質問者

補足

「return (key);」の場合、 return discards qualifiers from pointer target type という警告文が表示されました。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.4

「関数ポインタ」ってなんでしょうか>#3.

  • DarkMoon
  • ベストアンサー率21% (225/1046)
回答No.3

*bin_search( const Person *key) 関数の前に*がついていると、関数ポインタになります。

stiyl
質問者

お礼

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

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

その「bin_search」とやらは何をする関数なんでしょうか? ことと次第によっては「bin_searchでkeyを返したい」が既にはずれである可能性もあります.

stiyl
質問者

補足

元は、ライブラリ関数のbsearchを自作しようというものでした。 仮引数もいくつかありましたが、最終的にはkey(と同じ構造体の配列)を返せればよいので、このような形にしました。

関連するQ&A

  • C言語の関数について。

    学校の課題がわからないので教えてください。 1. 次の関数recfunc()は関数内で自分自身を呼び出す再起関数である。 int recfunc(int x){ if(x<=0)return -1; else if(x==1) return 3; else return 3*recfunc(x-1)-2; } 引数に1,2,3,4,5,...を与えたときの返り値を求めよ。その結果から、一般に 整数値nが与えられたとき、どのような値が計算されるか推測せよ。 一体何を返す関数だろうか?またどのようにその計算が実現されているか を簡潔に述べよ。 2.どのような再帰関数も、再帰を用いない関数に書き換えることができる。 問題1の関数recfunc()と同じ引数、同じ返り値をもつ再帰を用いない関数 func()を作成せよ。 2問も質問してすみません。 このプログラミングのレポートを提出しなくてはならないので困ってます。 何卒よろしくお願いします。

  • 構造体の返し方

    こういう構造体があるとして、 typedef struct Day{  int day1, day2, day3; } Day; このような関数で値を入れて、 int DayTest (char s) {  Day day;  day1 = 2003;  day2 = 12;  dat3 = 31;  return < ここの返し方がわからない >; } これを main() 関数で参照できるようにするには どうすればいいのでしょうか。 printf で出力させるとかで結構です。 ポインタだとは思うのですが、うまくいきません。

  • 関数への構造体の配列の渡し方<c言語初心者>

    こんにちは、関数への構造体の配列の渡し方で理解できない点があるため、質問させていただきます。 以下がスクリプトになります。3人の名前と年齢をinput関数で入力し、それらのデータをoutput関数で出力するのが目的です。 #include <stdio.h> typedef struct{ char name[64]; int age; }property; void input(property *data[]); void output(property *data[]); int main(void){ property data[3]; printf("Input data of three people.\n"); input(&data); output(&data); return 0; } void input(property *data[]){ int i; for(i=0;i<3;i++){ printf("%d banme\n",i+1); printf("name:"); scanf("%s",&data[i]->name); printf(" age:"); scanf("%3d",&data[i]->age); } return; } void output(property *data[]){ int i; for(i=0;i<3;i++){ printf("%d banme\n",i+1); printf("name:%s\n",data[i]->name); printf("age :%3d\n",data[i]->age); } return; } コンパイル時のエラーメッセージは以下のようになりました。(ファイル名はstructure5.c) structure5.c: In function ‘main’: structure5.c:14:2: warning: passing argument 1 of ‘input’ from incompatible pointer type [enabled by default] input(&data); ^ structure5.c:8:6: note: expected ‘struct property **’ but argument is of type ‘struct property (*)[3]’ void input(property *data[]); ^ structure5.c:15:2: warning: passing argument 1 of ‘output’ from incompatible pointer type [enabled by default] output(&data); ^ structure5.c:9:6: note: expected ‘struct property **’ but argument is of type ‘struct property (*)[3]’ void output(property *data[]); ^ structure5.c: In function ‘input’: structure5.c:24:3: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[64]’ [-Wformat=] scanf("%s",&data[i]->name); ^ 構造体の配列をinput関数やoutput関数に渡すときにエラーが発生しているようなのですが、自分で調べても解決できなかったため、質問させて頂きます。 皆様のお知恵を貸してください。なおプログラミング言語自体初心者のため、できる限りわかりやすいお言葉でご教授願います。よろしくお願い致します。

  • C言語のプログラムで...

    下のプログラムは参考書にあったサンプルプログラムなのですが /* 関数の宣言 */ int print_struct(struct person dat); のところはなぜint型なのですか? #include <stdio.h> #include <string.h> /* 構造体の定義 */ struct person { char name[20]; /* 名前 */ double height; /* 身長 */ double weight; /* 体重 */ int bpl; /* 最低血圧 */ int bph; /* 最高血圧 */ }; /* 関数の宣言 */ int print_struct(struct person dat); /* main関数 */ int main(void) { /* 変数の宣言 */ struct person dat; /* 構造体のメンバーに値を代入 */ strcpy(dat.name,"山田太郎"); dat.height = 173.5; dat.weight = 63.0; dat.bpl = 98; dat.bph = 113; /* struct person関数を実行 */ print_struct(dat); return 0; } /* print_struct関数 */ int print_struct(struct person dat) { /* 画面に出力 */ printf("%s\n",dat.name); printf("%f\n",dat.height); printf("%f\n",dat.weight); printf("%d\n",dat.bpl); printf("%d\n",dat.bph); return 0; }

  • 構造体について

    5件のデータをRECORDに追加したいのですが、 RECORD inputdata(void)の宣言文エラーなどのコンパイルエラー。それと、inputdataを用いてどうやって5件のデータを入れたらいいかがわかりません。教えてください。 #define SIZE 5 #include <stdio.h> typedef struct{ int yy; int mm; int dd; }YMD; typedef struct{ char name[20]; YMD birthday; int age; }RECORD; RECORD inputdata(void); void main(void) { int i; for(i = 0;i < SIZE;i++){ inputdata(); } RECORD inputdata(void) { RECORD person; printf("名前>"); scanf("%s", person.name); printf("誕生日入力\n"); printf("年>"); scanf("%d", person.birthday.yy); printf("月>"); scanf("%d", person.birthday.mm); printf("日>"); scanf("%d", person.birthday.dd); return person; }

  • 構造体についての問題なんですが

    文字列入力関数と(StrInput)いうものをつくっているんですが、作成にあたっては以下の定数・構造体・関数を用います。 #define BUF_LEN (79) struct InputData { char Data; struct InputData *pPrev; struct InputData *pNext; }; struct InputBuffer { struct InputData *pHead; struct Inputdata *pTail; struct Inputdata idList[BUF_LEN]; }; char *StrInput(char *pdefStr, int nlen) { static char cStr[BUF_LEN + 1]; struct InputBuffer ibBuffer; : : : return (cStr); } というふうになるんですが、そこで使う関数を9つ作らなければいけないので作りました。 DataInit関数・・・構造体の初期化 charInp関数・・・1文字入力関数 ListSearch関数・・・リストの空き要素を検索 DispBuffer関数・・・InputBuffer再表示 Delete関数・・・DELキーによる削除 Insert関数・・・INSキーによる挿入 BackSpace関数・・・BSキーによる後退 LeftKey関数・・・カーソルの位置移動 RightKey関数・・・カーソルの位置移動 これを使って作りたいんですが、引数とかが与えられていないので、全く作り方がわかりません。どうか上から4つくらいの関数がどうなるのか、教えて頂けないでしょうか!お願いします。

  • 自己参照構造体のtypedef宣言とスコープ

    MinGWとgccでプログラムを組んでいます(OSはWin7です)。 main.cpp、variable.h、function.hの3つのファイルからなり、 variable.hで自己参照構造体とそれのtypedef宣言をしています。 以下のような感じです。 ************************* typedef struct hoge HOGE; struct hoge{ int a,b; double x,y; HOGE *p1, *p2; } ************************* そのあとfunction.hでこのHOGE型のポインタを受ける関数を宣言しています。 void hogehoge(HOGE *p1, HOGE *p2); これをmain.cpp内で、variable.h、function.hの順に読み込んでいます。 そして、コンパイルエラーがでます。 error: unknown type name 'HOGE'(これがずらっと) typedefのスコープの関係なのか、それ以外の問題なのか。 煮詰まっています。アドバイスお願いいたします。m(_ _)m

  • map::find()の戻り値が変数に入ってくれません。

    こんにちは、boundaryといいます。 ユーザー型のキーとユーザー型の値をmapに挿入したのです が、map::find()の戻り値が変数に入ってくれません。 operator=を定義しないといけないのかなと思いやってみた のですがうまくいきません。 std::map<ユーザー型,ユーザー型>::iteratorを定義しない といけないのでしょうか? どなたかお知恵をお貸しください。 よろしくおねがいします。 windows2000 vc6.0sp5 ps.ソースコードを載せようとしたのですが、質問文字数が 最長文字数を超えるようで質問する事ができません。 ですのでかなり端折っています。(スミマセン) /* mapのキーです。 */ typedef struct _StateAndReturn { int State; int Return; } StateAndReturn; /* mapの値です。 */ struct NextStateAndFunc { NextStateAndFunc& operator=(const NextStateAndFunc& X){ NextState = X.NextState; MemFuncPointer = X.MemFuncPointer; } typedef int (Funcs::*pFunc)(); int NextState; pFunc MemFuncPointer; }; class CEventMap { private: typedef int (Funcs::*pFunc)(); typedef map<StateAndReturn, NextStateAndFunc, my_less> _EventFuncMap; _EventFuncMap EventFuncMap; public: void Set(int StateNow, int ServerReturnCode, int StateNext, pFunc pF) { /*登録します。*/ } const NextStateAndFunc Answer(int StateNow, int ServerReturnCode) { StateAndReturn tempStateAndReturn; tempStateAndReturn.State = StateNow; tempStateAndReturn.Return = ServerReturnCode; map<StateAndReturn, NextStateAndFunc>::const_iterator ite; ite = EventFuncMap.find(tempStateAndReturn); /* ←ここでエラーになります。 */ return ite->second; } };

  • C言語に関して質問です。構造体について

    構造体についての課題を出されたのですが、よくわかりません。教えてください。 問.複素数の構造体を作成し、その構造体変数2つを引数にもつ四則演算の関数を作成せよ。ただし、それぞれの四則演算の構造体を作成し、main関数内で、(a + bi) + (c + di)の形にし、a=2, b=3,c=4,d=5とすること。 この問題で以下までしか作れませんでした。解説を付けてソースをお願いします。 戻り値の型あってますか? #include<stdio.h> typedef struct{ double x,y; }Complex; void add(Complex z1 , Complex z2); void sub(Complex z1 , Complex z2); void multi(Complex z1 , Complex z2); void divide(Complex z1 , Complex z2);

  • 関数から構造体を返す一般的な手法は

    たとえば下のような平面上の点を表す構造体があって、 typedef struct { const int x; const int y; } Point; 2点間の中心を計算する関数を書くとします。 この場合、計算結果をどのように返せばよいでしょうか。 「ただ動けばよい」コードを書くことはできるのですが、 私が知りたいのは、どのような書き方が一般的かということです。 構造体の要素が const なので、 下のコードはコンパイルできません。 void center(Point* result, Point* p1, Point* p2) {  result->x = (p1->x + p2->x) / 2;  result->y = (p1->y + p2->y) / 2; } 新しいオブジェクトを作ってコピーする? void center(Point* result, Point* p1, Point* p2) {  Point p = { (p1->x + p2->x) / 2, (p1->y + p2->y) / 2 };  memcpy(result, &p, sizeof(Point)); } この例のような小さい構造体であれば、 オブジェクトをまるごとリターンしてもよい? Point center(Point* p1, Point* p2) {  Point p = { (p1->x + p2->x) / 2, (p1->y + p2->y) / 2 };  return p; } あるいは、そもそも構造体の要素を const にする手法は C言語では一般的でないのでしょうか? どうかご教授願います。