• ベストアンサー

HOSTENT構造体を宣言する必要はないのですか?

ネットワークプログラミングを勉強しているのですが,ソケットを用いた通信のサンプルで, HOSTENT *lphost として,HOSTENT構造体へのポインタを宣言して, lphost = gethostbyname(ホスト名の文字列); で,サーバーのアドレスをHOSTENT構造体にセットするとあります。 構造体へのポインタを宣言しても,構造体自体の領域は確保されないのではないかと思うのですが,gethostbyname関数が返すポインタは,いったい誰がどこに確保した領域を指しているのか,そしてその領域はいつまで保持されるのか,よく理解できません。構造体そのものを宣言せずに,それへのポインタを宣言し,それに関数の戻り値を代入するというのが,よく理解できないです。どなたか解説していただけると幸いです。

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

  • ベストアンサー
  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.2

★アドバイス >おおもとのmain関数が終了するまで値を保持し続ける  ↑  そうです。  ただし複数回gethostbyname関数を呼び出すと最後の情報だけが  構造体の値として残ります。よって複数回呼び出す場合は  構造体へコピーして使う必要があります。  ※1回しか呼び出さないならポインタで使いまわせば良い。 // 構造体を宣言 HOSTENT host; // 構造体をコピー host = *gethostbyname( ホスト名の文字列 ); このように戻り値のポインタを使って別々の構造体にコピーすれば マルチスレッドや複数回呼び出しにも対応できます。 ※C標準関数のlocaltime関数の仕組みと同じです。 ※内部にstaticな構造体を1つだけ持っていてそのポインタを返す。 ※http://www9.plala.or.jp/sgwr-t/lib/localtime.html

fukugoo07
質問者

お礼

ご回答ありがとうございます。 呼び出し元で構造体を用意して,それへのポインタを関数に渡して,構造体にデータをセットしてもう場合と,関数が構造体を用意して,それにデータをセットして,その構造体へのポインタを返す場合があるのですね。必要に応じて,呼び出し元が構造体を用意して,関数の用意した構造体の値をコピーして使う。 どうして関数によって処理の仕方が違うのでしょうか。これらの違いには何か理由があるのですか。

その他の回答 (2)

  • chirubou
  • ベストアンサー率37% (189/502)
回答No.3

No.1 です。 「どうして関数によって処理の仕方が違うのでしょうか。これらの違いには何か理由があるのですか。」 特に理由なんてないのではないでしょうか。最初に作った人が適当に(今となっては良くない)仕様を決めてしまっただけだと思いますよ。10 年以上前に作られたんではないでしょうか。 Linux や Unix ではこのような例がいくつも見られて、例えば errno というグローバル変数は、今の時代当たり前のマルチスレッドでは実は困り者なんですが、なんとかコンパイラで誤摩化してしまっています。 私の最初の回答では、マルチスレッドで問題になると書きましたが、マルチスレッドでなくても、2回以上、gethostbyname() を呼ぶと、最初に帰って来た値が次の呼び出しの値に書き換えられてしまうため、最初に帰って来た値が突然変わってビックリすることもあります。 いまひとつ自信がないんですが、No.3 さんのご回答にあるような方法で、構造体をコピーしてもダメだと思います。つまり HOSTENT 構造体のメンバーの文字列もコピーしなくてはなりませんから。このように構造体の全てのメンバー(もしメンバーが構造体だったらそのメンバーも)全てコピーする事を deep copy といったりします。で、HOSTENT をコピーするなら deep copy でないとダメですね。 実際、gethostbymae_r() 関数は deep copy をしてくれるんですが、HOSTENT の全てのメンバーの文字列領域を gethostbyname_r() 関数に別途与える必要があって、面倒なのです(全ての文字列を格納するに必要なだけの長さが事前に分からないので)。

  • chirubou
  • ベストアンサー率37% (189/502)
回答No.1

OS が分からないのですが、以下 Linux と仮定して。 参考 URL に Linux の man page を示しました。ここに書いてあるように、gethostbyname() は内部に static 宣言された struct hostent の構造体へのポインタを返します。あるいは、実装によってそういう場合があるので、例えば、帰って来た struct hostent を free() する、なんてことはしてはいけません。 さらに、この仕様は pthread 等で並行的に動作する場合、正しい動作が保証されません。このため Linux では gethostent_r() という関数があって、再帰呼び出しを可能にしています。ただし、この関数は、元の gethostent() をベースにしているので使い難いのが難点です。 Linux というか Unix には仕様自体が古くて、最近のマルチスレッドでは使う事ができない関数というのがいくつか存在します。たとえば strtok() もそのひとつです。これらの関数では関数内部の static 変数に状態を保存してしまっていますので、再帰呼び出しができない構造になっているのです。

参考URL:
http://www.linux.or.jp/JM/html/LDP_man-pages/man3/gethostbyname.3.html
fukugoo07
質問者

お礼

ありがとうございます。 猫でもわかるシリーズをC言語編,Windows編,ネットワーク編と読み進めてきた初心者です。VC++を使うつもりです。 ということは,呼び出されたgethostbyname関数が構造体を静的に宣言して,その構造体へのポインタを返し,それはstaticなものなので,おおもとのmain関数が終了するまで値を保持し続けるという理解でよいのでしょうか。 マルチスレッドのことはよくわからないですが,gethostbyname関数は,一つの同一のHOSTENT構造体にデータをセットするので,複数のスレッドからgethostbyname関数を呼び出すと,それぞれが関数を呼び出すたびに異なる値が構造体にセットされ,とんでもないことになるということですか。 なんとなくわかったような気がします。ありがとうございました。

関連するQ&A

  • ファイルのデータを構造体に代入する方法

    学習し始めてから時間が経ちましたが、まだ初心者のまま・・・少しずつ頑張っている状態の者です。 今回はタイトルのことで御質問させてください。 関数Read(自己作成)でcsvファイルの内容を読み取り、読み取ったデータを用意しておいた構造体の変数ポインタを利用して代入しようと試みているのですが・・・要領がつかめません。 それで気になったのですが、読み込んだファイルを用意しておいた構造体に入れるにはどんな方法があるかよろしければご教授下さい。 私としてはforを使ったやり方があるのなら是非見本を見せてほしいのですが・・・ ちなみに、私が考えていたプログラム案は以下の通りです。 構造体にはcsvファイル・1レコード分の情報が入る構成。 構造体と読み取るファイルの構造は酷似しているが構造体全てに入る情報でない場合がある。 構造体には必要な分、配列領域を確保する ファイルの先頭アドレスはファイルポインタに代入。 構造体の先頭アドレスはポインタ変数に代入。   main関数とは別の関数の引数に2つのアドレスをコピー。 ★ そこでファイルデータを構造体へ代入しmainに戻って出力する。    その関数での戻り値は特になし(成功したら0・・・と考えていたのですが使い道が今のところはreturnだけです) ★で特に悩んでいるので、よろしくお願い致します。

  • 構造体の宣言

    下記のように構造体の宣言をした所、 struct B_PARAM test; 「`test' の領域サイズがわかりません」というエラーになってしまいました。この構造体を宣言し、値を入れていこうとしています。 ヘッダファイルに構造体の形は定義してあるのですが、 構造体の中に構造体があるからでしょうか? またこの構造体を正しく宣言するにはどうすればいいのでしょうか?

  • 不正な構造体の演算

    ある構造体を戻り値として関数からかえして、変数に代入すると不正な構造体の演算というエラーになります。 これは、どういったことが原因と考えられますか?

  • 関数 構造体 ポインタ

    番号、名前、年齢を一組とした構造体の変数をmain関数で宣言し、そのアドレスを副関数に引き渡す。副関数で従業員番号、氏名、年齢を入力し、main関数に戻る。main関数で構造体の内容を表示する。 困ってます、ポインタがどうにも理解できません。

  • ポインタ 構造体 関数

    番号、名前、年齢を一組とした構造体の変数をmain関数で宣言し、そのアドレスを副関数に引き渡す。副関数で従業員番号、氏名、年齢を入力し、main関数に戻る。main関数で構造体の内容を表示する。 困ってます、ポインタがどうにも理解できません。

  • プロトタイプ宣言について

    C言語で関数を作成しプロトタイプ宣言するときの質問です。 関数実体の引数に構造体のポインタを宣言します。 プロトタイプ宣言には,構造体のポインタを宣言したのと同じ位置にvoid *を宣言します。 関数実体とプロトタイプ宣言は,異なるファイルです。 このように作成した関数は,VC++2008では,コンパイルできるのですが, なぜ関数実体の宣言とプロトタイプ宣言の型が異なるのにコンパイルできるのでしょうか? また,この関数を別ファイルの関数から呼び出した場合,正しく引数を理解し,正しく処理されます。 これは,言語仕様として正しい書き方なのでしょうか? それとも環境依存の書き方なのでしょうか? ご存知の方がいましたらお答えお待ちしています。

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

    関数の戻り値を構造体の配列(アドレスを受け渡しを利用して)にしたいのですがうまくゆきません。 以下のプログラムではコンパイルはできるのですが、 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 となると思ったのですがどこが間違っているのでしょうか? よろしくお願いします。

  • 構造体の宣言方法について

    構造体の宣言で ヘッダーファイルに struct RAM rom_AAA[20] を宣言 Cソースファイル(上記のヘッダーファイルをインクルードする)に struct RAM { struct BBB *CCC } を宣言 とした時 rom_AAA[20]と*CCC(BBBアドレス)はリンクしてる状態になるのでしょうか? 構造体の一部の定数テーブルを参照するために 間単にポインタ使ってグルグル回したいのですが、同じものをアクセスしてる事にならないでしょうか?

  • 構造体の代入

    構造体の比較はパディング領域が存在しうるので一つ一つの要素を比較する必要がありますが、 構造体を代入するのは、そのまま代入して問題ないでしょうか? それとも1要素ずつ代入すべきでしょうか。

  • Cで関数とポインタと構造体の表現法

    C言語の関数で 構造体・共用体の入った配列を関数にポインタで送りたいと思うのですが上手くいきません プロトタイプ宣言とメイン文での表現・関数そのものでの表現 が詳しく知りたいのですがサイトまたは記述方を教えてください。 上記の条件で ポインタは*をつけるつけない 関数で引数はどうやって選ぶのか 構造体名などはどのように表現すればいいのか などの見分けがいまいちつかないので悩んでいます。

専門家に質問してみよう