
こんにちは。
授業で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);
などとりあえず試してはみたのですがどれも上手くいきません。
そもそも関数名の前に*(間接演算子?)があること自体がよくわかっていないです……
なので、返り値をどうすれば正しく機能してくれるのか教えていただけるとありがたいです。
※課題で指定されているので、関数頭部については変更しないとします。
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は、そのローカル変数のアドレスを示します。
そのローカル変数が解放されたら、その領域がどうなるかはわかりません。すぐに別の変数がその領域を使ってしまうかもしれません。
なので、「受け取ったアドレスが無意味になってるかもしれませんよ。大丈夫ですか?それを意図したプログラムですか?」という警告です。
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修飾ありの変数でも結果を受けることができるようになります。
No.11
- 回答日時:
もちろん、そんな関数に意味があるとは私も思ってないです>#10
でも#2の補足を読む限りは質問者さんはそういう仕様を考えていらっしゃるようなので。
検索キーはnameだったりするんじゃないかなと。
この回答への補足
質問内容には関係ないと思い省略したのですが、細かく表記するべきだったでしょうか。
実際に作る関数は
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に結果を代入するものです。
No.10
- 回答日時:
よしんば「実際に検索をする」関数であっても, 「検索キーをそのまま返す」検索関数に意味があると思えないのです>#8.
ふつうは「検索して見付けたもの」を返すのでは?
No.9
- 回答日時:
詳細な説明はほかの方がしてくれているので、別の意見を。
とりあえず、プロトタイプは
Person *bin_search( const Person *key, Person *data, size_t num)
でないとまずいんじゃないかな。
それとプロトタイプからconstを外すというのが一番よいと思いますが
プロトタイプでconstをつけるのは関数の中で値を書き換えないことの
意思表示でもあるので検索系の関数だし、
constをつけたままにして return するときに(Person *)で
キャストしてしまうとか、戻り値そのものを int にして、配列の番号を返す仕様に変更する案もありかと思います。
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)
などの書籍が参考になります。
No.5
- 回答日時:
>Person *bin_search( const Person *key)
bin_search関数の戻り値はPerson型へのポインターですね。よって、
>return (&key); // 戻り値の型はPerson **型
>return (*key); // 戻り値の型はPerson 型
これらが正しくないのはわかるのですが、
>return (key);
これもダメでしたか?
そのときのエラーメッセージは、どういう内容でしたか?
# 関数ポインターという用語があることは間違いないが、このシチュエーションで使うものかどうかは微妙。
この回答への補足
「return (key);」の場合、
return discards qualifiers from pointer target type
という警告文が表示されました。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- C言語・C++・C# C言語初心者 構造体 課題について 1 2023/03/10 19:30
- C言語・C++・C# スタックフレームの消滅 6 2023/05/20 12:33
- Java JavaのSingletonパターンのprivateの持つ意味が分かりません。 5 2022/06/12 10:38
- C言語・C++・C# C言語初心者 構造体 課題について 2 2023/03/10 19:48
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
- 大学・短大 C言語線形リストの問題です 3 2022/12/22 00:45
- C言語・C++・C# C言語の課題が出たのですが自力でやっても分かりませんでした。 要素数がnであるint型の配列v2の並 3 2022/11/19 17:41
- その他(プログラミング・Web制作) pythonにおける単方向リストの実装について 4 2022/07/13 12:34
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
このQ&Aを見た人はこんなQ&Aも見ています
関連するカテゴリからQ&Aを探す
おすすめ情報
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
[C#] DeviceIoControlの変数の型
-
fopne で失敗する原因
-
構造体の初期化のmemsetの第三引数
-
C言語のポインタに直接アドレス...
-
【VC++2005(CLR)】マルチスレッ...
-
セグメントエラー
-
プーさんのマウスポインタを教...
-
変換
-
C言語、配列とポインタとアスタ...
-
パスからファイル名を抽出
-
bsearch関数の呼び出しで
-
init関数の意味
-
newとdeleteでのアサート
-
Cで作成したDLL関数をVBから呼...
-
c言語で任意のファイルから読み...
-
C++ Builderでのnewコマンドに...
-
Run-Time Check Failure #3とい...
-
【C言語】戻り値が構造体の関数
-
LPSTR型の初期化について
-
VBはCを混乱させる?
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
セグメントエラー
-
C言語のポインタに直接アドレス...
-
init関数の意味
-
Run-Time Check Failure #3とい...
-
戻り値で構造体を返すことは可...
-
ExcelVBAでのkernel32(64bit)
-
アプリを32bitから64bit移行
-
参照型で受け取った引数をポイ...
-
fopne で失敗する原因
-
PASCALとFARの意味
-
LPSTR型の初期化について
-
CWnd::EnableWindow()の扱い方
-
ポインタについて
-
プーさんのマウスポインタを教...
-
連結リスト 要素の入れ替え
-
ハンドルはポインタか
-
C++で関数ポインタから関数名を...
-
自作DLLの引数について、ポイン...
-
NULLポインタが0でない処理系と...
-
TCHAR文字列内の検索について
おすすめ情報