人に聞けない痔の悩み、これでスッキリ >>

こんにちは。

授業で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);
などとりあえず試してはみたのですがどれも上手くいきません。

そもそも関数名の前に*(間接演算子?)があること自体がよくわかっていないです……

なので、返り値をどうすれば正しく機能してくれるのか教えていただけるとありがたいです。


※課題で指定されているので、関数頭部については変更しないとします。

このQ&Aに関連する最新のQ&A

A 回答 (12件中1~10件)

ポインタはややこしいですが、&や*が付いたらどんな型になるかを落ち着いて考えましょう。




> 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は、そのローカル変数のアドレスを示します。
そのローカル変数が解放されたら、その領域がどうなるかはわかりません。すぐに別の変数がその領域を使ってしまうかもしれません。
なので、「受け取ったアドレスが無意味になってるかもしれませんよ。大丈夫ですか?それを意図したプログラムですか?」という警告です。
    • good
    • 0
この回答へのお礼

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

お礼日時:2012/05/25 16:01

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修飾ありの変数でも結果を受けることができるようになります。
    • good
    • 0
この回答へのお礼

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

お礼日時:2012/05/25 16:01

もちろん、そんな関数に意味があるとは私も思ってないです>#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に結果を代入するものです。

補足日時:2012/05/25 14:56
    • good
    • 0

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



ふつうは「検索して見付けたもの」を返すのでは?
    • good
    • 0

詳細な説明はほかの方がしてくれているので、別の意見を。



とりあえず、プロトタイプは
Person *bin_search( const Person *key, Person *data, size_t num)
でないとまずいんじゃないかな。

それとプロトタイプからconstを外すというのが一番よいと思いますが
プロトタイプでconstをつけるのは関数の中で値を書き換えないことの
意思表示でもあるので検索系の関数だし、
constをつけたままにして return するときに(Person *)で
キャストしてしまうとか、戻り値そのものを int にして、配列の番号を返す仕様に変更する案もありかと思います。
    • good
    • 0
この回答へのお礼

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

お礼日時:2012/05/25 16:03

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

    • good
    • 0

関数名の前に付いている * は、戻り値のデータ型の一部です。

つまり、この関数の戻り値のデータ型は 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)
などの書籍が参考になります。
    • good
    • 0
この回答へのお礼

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

お礼日時:2012/05/25 16:04

>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
という警告文が表示されました。

補足日時:2012/05/24 16:40
    • good
    • 0

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

    • good
    • 0

*bin_search( const Person *key)



関数の前に*がついていると、関数ポインタになります。
    • good
    • 0
この回答へのお礼

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

お礼日時:2012/05/25 16:04

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

Q戻り値で構造体を返すことは可能でしょうか?

perlでは以下のように2つの戻り値が可能ですが、C言語では
それができるのでしょうか?
my (ret1, ret2) = test1();

よくやるのは、引数にポインタを渡して、内容を書き換える手を使っていますが、戻り値を複数返せたら、直感的にわかりやすいかなと思いまして・・・

Aベストアンサー

C言語から遠く離れた者ですが、

>>> よくやるのは、引数にポインタを渡して、内容を書き換える手を使っています

これが常識でしょう。これが直感的に理解できるようにC言語を身に付ける必要があるのではないでしょうか。

Qポインタを使って構造体の配列を戻り値にするには

関数の戻り値を構造体の配列(アドレスを受け渡しを利用して)にしたいのですがうまくゆきません。
以下のプログラムではコンパイルはできるのですが、
a0 = 2
a1 = 4198512
a2 = 4329332
と表示されてしまいa1,a2がうまくゆきません。

*********************************************

#include<stdio.h>
struct test{
int a;
};
struct test *func(void);

void main(void)
{
struct test *data;//構造体ポインタ
int i;

data = func(); //ポインタにtest関数の戻り値(アドレス)を代入
for(i=0;i<=2;i++){
  printf("a%d = %d\n",i,(data+i)->a); //構造体要素を表示
}
}

struct test *func(void)
{
struct test data[3]={1,2,3}; //構造体配列を定義

return (&data[0]); //構造体配列の先頭アドレスを返す
}

*************************************************

test関数から受ける取ったアドレス(&data[0])をポインタ(data)に代入して1づつずらして表示させれば

a0=1,a1=2,a=3

となると思ったのですがどこが間違っているのでしょうか?
よろしくお願いします。

関数の戻り値を構造体の配列(アドレスを受け渡しを利用して)にしたいのですがうまくゆきません。
以下のプログラムではコンパイルはできるのですが、
a0 = 2
a1 = 4198512
a2 = 4329332
と表示されてしまいa1,a2がうまくゆきません。

*********************************************

#include<stdio.h>
struct test{
int a;
};
struct test *func(void);

void main(void)
{
struct test *data;//構造体ポインタ
int i;

data = func(); //ポインタにtest関数の戻り値...続きを読む

Aベストアンサー

struct test data[3]={1,2,3};

auto変数は関数からの脱出時になくなってしまいます。

static struct test data[3]={1,2,3};

静的変数として宣言しましょう。


return (&data[0]); //構造体配列の先頭アドレスを返す
ここは
return (data); //構造体配列の先頭アドレスを返す
こうじゃないと警告が出ませんか?

QC言語のポインターに関する警告

line[100]で
「1」が格納されていたら「a」
「2」が格納されていたら「b」
「3」が格納されていたら「c」
とout[100]に代入する関数を作りたいのですが
コンパイルすると関数の部分で
warning: assignment makes integer from pointer without a cast
という警告がでます。
ポインターは使っていないのですが、ポインターに関する警告が出ているようで困っています。
どこが悪いのかまったくわからなくて作業が完全に止まってしまいました。
解決法をおしえてください。お願いします。

/*宣言*/
int=i; /*main関数内のfor文で使用*/
char line[100], out[100];
void change(int);

/*関数*/
void change(int i)
  {
   if(line[i]=='1'){
    out[10]="a\0"
   }if(line[i]=='2'){
    out[10]="b\0";
   }if(line[i]=='3'){
    out[10]="c\0"
}
}

line[100]で
「1」が格納されていたら「a」
「2」が格納されていたら「b」
「3」が格納されていたら「c」
とout[100]に代入する関数を作りたいのですが
コンパイルすると関数の部分で
warning: assignment makes integer from pointer without a cast
という警告がでます。
ポインターは使っていないのですが、ポインターに関する警告が出ているようで困っています。
どこが悪いのかまったくわからなくて作業が完全に止まってしまいました。
解決法をおしえてください。お願いします。

/*宣言*/
int...続きを読む

Aベストアンサー

>    out[10]="a\0"
>    out[10]="b\0";
>    out[10]="c\0"

"a\0"や"b\0"や"c\0"は「charへのポインタ」ですよ。

out[10]は「char」ですから「記憶域が小さい整数(つまり、charに)に、ポインタを代入すると、値が失われるぞ」と警告が出ます。

void change(int i)
  {
   if(line[i]=='1'){
    out[10]='a';
   }if(line[i]=='2'){
    out[10]='b';
   }if(line[i]=='3'){
    out[10]='c';
}
}
または
void change(int i)
  {
   if(line[i]=='1'){
    out[10]=0x61; /* aのASCIIコード */
   }if(line[i]=='2'){
    out[10]=0x62; /* bのASCIIコード */
   }if(line[i]=='3'){
    out[10]=0x63; /* cのASCIIコード */
}
}
と書きましょう。

>    out[10]="a\0"
>    out[10]="b\0";
>    out[10]="c\0"

"a\0"や"b\0"や"c\0"は「charへのポインタ」ですよ。

out[10]は「char」ですから「記憶域が小さい整数(つまり、charに)に、ポインタを代入すると、値が失われるぞ」と警告が出ます。

void change(int i)
  {
   if(line[i]=='1'){
    out[10]='a';
   }if(line[i]=='2'){
    out[10]='b';
   }if(line[i]=='3'){
    out[10]='c';
}
}
または
void change(int i)
  {
   if(l...続きを読む

QC言語で文字列をかえす正しい書き方が知りたいです?

C言語で次の警告が表示されます。
文字列を返したいのですが、正しい書き方はどのようにすれば良いのでしょうか?


jci.h(20) : warning C4172; ローカル変数またはテンポラリのアドレスを返します。


char *test(char *a, int b)
{
char str[BUFSIZ];
return str; <------

}

Aベストアンサー

再入可能にするかどうかで、回答は変わります。

A.2度呼び出した場合に前のデータを破壊してもよいケース(再入不可能)

char *test(...)
{
static char str[BUFSIZ]; // static指定でメモリは静的に確保されます。

...

return str;
}

B.2度呼び出した場合に前のデータを破壊しないケース(再入可能)
B-1.mallocを使ってもいいケース
char *test(...)
{
char *str;
str = malloc(BUFSIZ);
if(str == NULL) return NULL; // エラー

...

return str;
}
この場合は、呼び出し元でちゃんとfreeしましょう。

B-2.呼び出し元でメモリを確保するケース
(注意:同じアドレスを指定して複数回呼び出すと、メモリ内容は当然破壊されます)
char *test(char *str, ...)
{

...

return str;
}
これは#1の方の回答と同じです。

B-3.B-1/B-2の複合
(注意:NULL以外の同じアドレスを指定して複数回呼び出すと、メモリ内容は当然破壊されます)
char *test(char *str, ...)
{
if(str == NULL)
{
str = malloc(BUFSIZ);
if(str == NULL) return NULL; //エラー
}

...

return str;
}

こんなところですかね。

再入可能にするかどうかで、回答は変わります。

A.2度呼び出した場合に前のデータを破壊してもよいケース(再入不可能)

char *test(...)
{
static char str[BUFSIZ]; // static指定でメモリは静的に確保されます。

...

return str;
}

B.2度呼び出した場合に前のデータを破壊しないケース(再入可能)
B-1.mallocを使ってもいいケース
char *test(...)
{
char *str;
str = malloc(BUFSIZ);
if(str == NULL) return NULL; // エラー

...

return str;
}
この場合は、呼び...続きを読む

Qchar*を初期化したいのですが

Cの標準関数だけで
char*を初期化したいのですが
どの様にすればいいのでしょうか?

char* a = "aaaa";
char* b = "bbbb";
strcat( a, b );//"aaaabbbb"?
とし使いまた後で
aにまた値を入れ直したいので
初期化して再利用したいのですが

どのようにすればいいのでしょうか?

ポインタとかもうっすら(ほとんどわかりません)
よろしくお願いいたします

Aベストアンサー

> char* a = "aaaa";
> char* b = "bbbb";
> strcat( a, b );//"aaaabbbb"?
この上のコードは間違っていますよ。
変数a、bに入っているのはあくまでも文字列の"ポインタ"です。
従いまして、strcat( a, b );とすると、aがさしているメモリは5バイトしかないのでメモリを破壊してしまいます。
static char sza[] = "aaaa";
static char szb[] = "bbbb";
char szBuffer[256];
char* a = szBuffer;
strcpy (a, sza);
strcat (a, szb);
としないとだめです。

またポインタ変数はNULLで初期化可能です。

Q構造体の初期化のmemsetの第三引数

memset(&lvitem, 0, sizeof(LVITEM));
memset(&lvitem, 0, sizeof(lvitem));
LVITEMに特化した質問ではなく構造体の初期化について、この2つの書き方によるソース管理やコンパイルでのメリットとデメリットを教えてください。

Aベストアンサー

(1)memset(&lvitem, 0, sizeof(LVITEM));
この書き方は、構造体LVITEMを初期化しているんだな、と分かりやすい。
(2)memset(&lvitem, 0, sizeof(lvitem));
この書き方はとにかく変数lvitemを初期化するんだな、という感じだが、型が分からないので宣言しているところを参照しなければならない。
それともう一つ、ただ単に何回も初期化するんでなければ、こういう書き方もできる。
(3)LVITEM lvitem = {0};

結局どれがいいのかといえば、個人的にはケースバイケースですねえ。初期化を1回すればいいような感じなら(3)、構造体名を明示した方が調べる手間がなくなるようなら(1)、そうでなければ(2)を使います。
コンパイルでのメリット、デメリットは特にないんじゃないかなあ。アセンブラがまだ全盛だった頃ならともかく、いまじゃどのコンパイラだって最適化オプションで同じようなコードはくでしょう。
気にするほどでもないと思うけど…

Qint型からchar型への変換

タイトル通り、int型からchar型への変換の仕方がわかりません!><
どうしたらいいのでしょうか?

Aベストアンサー

#include <stdio.h>


char buf[5];
int no;

no = 10;
sprintf(buf, "%d", no);

Q関数ポインタの利点

こんにちは。
C言語初心者ですが今勉強中です。

その中でポインタについては理解できたのですが、
関数ポインタの利点、使うべき所などが理解できません。

ポインタの基本は理解しています。
値渡し、アドレス渡しも理解しています。

関数ポインタを使うと何がいい、またはどんなとき使わなければならないのか
教本を読んでいてもさっぱりわかりません。
サンプルプログラムを打っても何のために使ってるのかわからないです。

どなたか教えていただけませんでしょうか?
よろしくお願いします。

Aベストアンサー

実際に関数へのポインタを使用している事例を調べるのが一番手っ取り早いと思います。
標準関数だけでも、atexit、bsearch、qsort、signalが関数へのポインタを使用しています。一度、触ってみるとよいでしょう。

ところで、余談かもしれませんが、関数へのポインタがなければ関数を呼び出すことができません。
関数呼出し演算子(括弧演算子)がオペランドとして要求するのは、関数型ではなく、関数へのポインタ型だからです。すなわち、

voif func(void);
func();

のようにfuncを呼び出す場合でも、funcは関数へのポインタ型(ここではvoid(*)(void)型)に暗黙的に型変換され、関数呼出し演算子のオペランドになっているわけです。

Q構造体のアドレス渡し

構造体をmain()からアドレス渡しで別関数(test.cpp)に渡し、その関数の中だけでの別関数test1()にその構造体を、値渡しでもアドレス渡しでも、渡せるのでしょうか?
ちなみにmain.cppと、test.cppと、myhead.hとして分割コンパイルでやりました。

/*---------main.cpp--------*/
//ヘッダファイルで構造体宣言、test()のプロトタイプ宣言済み
void main()
{
struct data dt[10];
・・・・・・
test(dt); //test.cppのtest関数に構造体を渡す。
}

/*---------test.cpp---------*/
void test1(??????); //test1()のプロトタイプ宣言

void test(struct data *p) //構造体をアドレス渡しで受け取った
{
・・・・・
test1(?????); //test.cppで宣言したtest1関数に構造体を渡したい
}

どうかよろしくお願いします。

構造体をmain()からアドレス渡しで別関数(test.cpp)に渡し、その関数の中だけでの別関数test1()にその構造体を、値渡しでもアドレス渡しでも、渡せるのでしょうか?
ちなみにmain.cppと、test.cppと、myhead.hとして分割コンパイルでやりました。

/*---------main.cpp--------*/
//ヘッダファイルで構造体宣言、test()のプロトタイプ宣言済み
void main()
{
struct data dt[10];
・・・・・・
test(dt); //test.cppのtest関数に構造体を渡す。
}

/*---------test.cpp---------*/
void ...続きを読む

Aベストアンサー

構造体だと考えるからややこしく思えるのです。

構造体でなく、単なるintで考えてみて下さい。Cにとっては「引数に渡せる物」は構造体もintも一緒です。

void main()
{
int dt[10];
・・・・・・
test(dt); //test.cppのtest関数にintを渡す。
}

/*---------test.cpp---------*/
void test1(int para); //値渡しtest1()のプロトタイプ宣言
void test2(int *para); //アドレス渡しtest2()のプロトタイプ宣言

void test(int *p) //intをアドレス渡しで受け取った
{
・・・・・
test1(*p); //test.cppで宣言したtest1関数にintを値で渡したい
test1(p[0]); //こう書いても同じです
test2(p); //test.cppで宣言したtest1関数にintをアドレスで渡したい
test2(&p[0]); //こう書いても同じです
}
上記の「int」を「struct data」に置き換えても、Cは同じ事をします。

test1には「値が1つだけ」渡ります。渡るのは配列の1つの要素だけです。0~9番目をまとめて全部渡す事は出来ません。

test2には「アドレス」が渡ります。渡るのは配列の先頭アドレスなので、アドレスを元に0~9番目にアクセス出来ます。

「配列の10個の要素をまとめて値渡ししたい」と言う場合は、その配列をメンバにもつ構造体を新たに定義し、配列をラップした構造体で値渡ししましょう。

構造体だと考えるからややこしく思えるのです。

構造体でなく、単なるintで考えてみて下さい。Cにとっては「引数に渡せる物」は構造体もintも一緒です。

void main()
{
int dt[10];
・・・・・・
test(dt); //test.cppのtest関数にintを渡す。
}

/*---------test.cpp---------*/
void test1(int para); //値渡しtest1()のプロトタイプ宣言
void test2(int *para); //アドレス渡しtest2()のプロトタイプ宣言

void test(int *p) //intをアドレス渡しで受け取った
{
・・・・・
test1(*p); //...続きを読む

Qi++と++i ってどう違うのですか?

i++と++i ってどう違うのですか?

C#です。

Aベストアンサー

++や--を変数の前に置く演算を「前置演算」、++や--を変数の後に置く演算を「後置演算」といい、その違いは、次のようになります。

前置演算:先に処理(++や--)をしてから代入
(例)i=2;
   a=++i;
    ~~~
    先に処理 : iは3
   ~~~~~
   後から代入 : aは3

後置演算:先に代入してから処理(++や--)
(例)i=2;
   a=i++;
   ~~~
   先に処理   : aは2
    ~~~~~
    後から代入 : iは3


人気Q&Aランキング

おすすめ情報