- ベストアンサー
HOSTENT構造体を宣言する必要はないのですか?
ネットワークプログラミングを勉強しているのですが,ソケットを用いた通信のサンプルで, HOSTENT *lphost として,HOSTENT構造体へのポインタを宣言して, lphost = gethostbyname(ホスト名の文字列); で,サーバーのアドレスをHOSTENT構造体にセットするとあります。 構造体へのポインタを宣言しても,構造体自体の領域は確保されないのではないかと思うのですが,gethostbyname関数が返すポインタは,いったい誰がどこに確保した領域を指しているのか,そしてその領域はいつまで保持されるのか,よく理解できません。構造体そのものを宣言せずに,それへのポインタを宣言し,それに関数の戻り値を代入するというのが,よく理解できないです。どなたか解説していただけると幸いです。
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
★アドバイス >おおもとのmain関数が終了するまで値を保持し続ける ↑ そうです。 ただし複数回gethostbyname関数を呼び出すと最後の情報だけが 構造体の値として残ります。よって複数回呼び出す場合は 構造体へコピーして使う必要があります。 ※1回しか呼び出さないならポインタで使いまわせば良い。 // 構造体を宣言 HOSTENT host; // 構造体をコピー host = *gethostbyname( ホスト名の文字列 ); このように戻り値のポインタを使って別々の構造体にコピーすれば マルチスレッドや複数回呼び出しにも対応できます。 ※C標準関数のlocaltime関数の仕組みと同じです。 ※内部にstaticな構造体を1つだけ持っていてそのポインタを返す。 ※http://www9.plala.or.jp/sgwr-t/lib/localtime.html
その他の回答 (2)
- chirubou
- ベストアンサー率37% (189/502)
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)
OS が分からないのですが、以下 Linux と仮定して。 参考 URL に Linux の man page を示しました。ここに書いてあるように、gethostbyname() は内部に static 宣言された struct hostent の構造体へのポインタを返します。あるいは、実装によってそういう場合があるので、例えば、帰って来た struct hostent を free() する、なんてことはしてはいけません。 さらに、この仕様は pthread 等で並行的に動作する場合、正しい動作が保証されません。このため Linux では gethostent_r() という関数があって、再帰呼び出しを可能にしています。ただし、この関数は、元の gethostent() をベースにしているので使い難いのが難点です。 Linux というか Unix には仕様自体が古くて、最近のマルチスレッドでは使う事ができない関数というのがいくつか存在します。たとえば strtok() もそのひとつです。これらの関数では関数内部の static 変数に状態を保存してしまっていますので、再帰呼び出しができない構造になっているのです。
お礼
ありがとうございます。 猫でもわかるシリーズをC言語編,Windows編,ネットワーク編と読み進めてきた初心者です。VC++を使うつもりです。 ということは,呼び出されたgethostbyname関数が構造体を静的に宣言して,その構造体へのポインタを返し,それはstaticなものなので,おおもとのmain関数が終了するまで値を保持し続けるという理解でよいのでしょうか。 マルチスレッドのことはよくわからないですが,gethostbyname関数は,一つの同一のHOSTENT構造体にデータをセットするので,複数のスレッドからgethostbyname関数を呼び出すと,それぞれが関数を呼び出すたびに異なる値が構造体にセットされ,とんでもないことになるということですか。 なんとなくわかったような気がします。ありがとうございました。
お礼
ご回答ありがとうございます。 呼び出し元で構造体を用意して,それへのポインタを関数に渡して,構造体にデータをセットしてもう場合と,関数が構造体を用意して,それにデータをセットして,その構造体へのポインタを返す場合があるのですね。必要に応じて,呼び出し元が構造体を用意して,関数の用意した構造体の値をコピーして使う。 どうして関数によって処理の仕方が違うのでしょうか。これらの違いには何か理由があるのですか。