• ベストアンサー

構造体と構造体型の変数宣言箇所

はじめてborlandC++builder6でプログラムを作っているのですが、構造体と構造体型の変数宣言箇所についてわからないことがあるので質問させてください。 あるフォームのソースファイル(○○○.cpp)内の関数で、自分で定義した構造体を使う場合、ヘッダファイル(○○○.h)に構造体を定義すると、関数内で「変数が未定義」エラーになってしまいます。一方、ソースファイル(○○○.cpp)の先頭に書けばエラーになりません。  ヘッダファイルに変数定義した場合とソース内の先頭に構造体と構造体型変数を定義するのでは何が違ってくるのでしょうか?

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

  • ベストアンサー
  • 1839cc
  • ベストアンサー率54% (12/22)
回答No.10

> この書き方は,適切な記述方法ではないのでしょうか? これでは、適切か不適切か以前に、コードの意味が全然変わってしまっていますよ! 当初の記述が誤りであれば今の方がマシですが、当初の記述が正しいのであれば今の記述は下手をすればバグです。 プロパティ(メンバ変数)というのはオブジェクトの状態を管理する変数です。 当初のコードではtmpはフォームのプロパティでしたので、フォームの状態を表現する変数のひとつだったのです。 フォームの幅や高さ、位置や色といった状態と同列だったわけです。 これはフォームが複数いればそれぞれが持っている値です。 だから、どこにも属さないグローバル関数からは変更できなかったわけですね。 しかし、変更後のコードを見ると、tmpはグローバル変数になってしまっています。 これはプログラム全体の状態を表すための変数に変わったことを意味します。 フォームの存在に関係なく、この変数はこのプログラムを起動している数と同数存在していることになります。 どちらのあり方が、tmpとして正しいあり方なのか考えてみてください。 とは言え、気になるのは、tmpがポインタ変数であることです。 実体は一体どこにいるのでしょうか。 なんにしても、グローバル変数の取り扱いには注意してくださいね。 細かいことを言えば、グローバル変数を不用意に使っている時点で不適切な記述です。

BIGMON
質問者

お礼

何度も回答いただきまして大変ありがとうございました。 tmp実体はフォーム作成時のイベント処理関数内で生成してあります。ということは、やはり、tmpはグローバル変数じゃまずいですね。グローバル関数も構造体もすべてform1のヘッダで定義して、form1のクラスに含めることにしようと思います。できるだけグローバル変数は使いたくないですし。。。

その他の回答 (9)

  • 1839cc
  • ベストアンサー率54% (12/22)
回答No.9

> データ格納用構造体と同じように,form1.cppの先頭に移動させてしまいました それは、NO5さんへの補足にあったコードを、以下のように変更したということですか? ////form1.h//// class TForm1 : public TForm{  __published: // IDE 管理のコンポーネント    void __fastcall XXX();  public: // ユーザー宣言  __fastcall TForm1(TComponent* Owner); }; ////form1.cpp//// struct aaa{  int iVal;  char cVal; }; struct aaa *tmp; int func() ; void __fastcall TForm1::XXX()  func(); } void func(){  tmp->iVal = 1; }

BIGMON
質問者

補足

はい.そうです. この書き方は,適切な記述方法ではないのでしょうか?

  • 1839cc
  • ベストアンサー率54% (12/22)
回答No.8

> funcがどのクラスに属するのか、よくわかりません。 funcはどこにも属していないのでしょう。 では、なぜエラーが出るのかを手抜き気味ですが、説明してみます。 BIGMONさんは、TForm1というクラスを設計しました。 では、そのTForm1で設計されたフォームが同時に二つ表示されていることを想像してみてください。 その状態で、以下のコードの動作を考えてみてください。 void func(){   tmp->iVal = 1; } どこにも属さないはずの func から tmp->iVal をいじろうとしているわけですが、これではコンパイラは困ってしまいます。 「誰の tmp->iVal をいじればよいのか」がわからないのです。 それはそうですよね。フォームは二ついるわけですから。 どちらのフォームの tmp->iVal を変更すればよいのかわかりません。 しかも、スクロールバーやボタンなども同じく tmp という名前のメンバ変数をもっているかも知れないのです。 または、フォームが一個も表示されておらず、だれも tmp を持っているものは居ない、ということもありえます。 一体 func はどこを変更すればよいのやら。 つまり、上記のコードに足りないのは、「誰の tmp をいじるのか」という情報です。 そのためには、「(1)func が TForm1 に属している」か、「(2)tmp を変更する相手を特定してあげる」必要があるわけです。

BIGMON
質問者

補足

 もともとのソースでは,funcは,データをデータ格納用構造体(この構造体はform1.cppの先頭で定義されている.)に詰める処理に特化しており,func内で,tmpをいじらないために,tmpの方はヘッダファイルに定義されておりました.  しかし,機能強化のための修正に際し,func内でtmpをいじる必要がでてきたので,データ格納用構造体と同じように,form1.cppの先頭に移動させてしまいました.これは,「(2)tmp を変更する相手を特定してあげる」ことになるのでしょうか???  たびたびの質問でお手数おかけしますが,よろしくお願いいたします.

  • 1839cc
  • ベストアンサー率54% (12/22)
回答No.7

なるほど、そう言うことですか。 NO5さんへの補足欄のコードで合っているのであれば、確かにエラーです。 これからクラスとオブジェクトの概念をしっかり理解していかないといけないようですね。 対策はいろいろあるのですが、funcが誰から呼ばれ、何をする関数なのかで設計が変わってきます。 普通に考えれば、対策案は以下のどちらかでしょうか。 対策1:funcをTForm1のメソッドにしてしまう 対策2:TForm1のポインタをfuncが受けるようにして、TForm1にアクセス用メソッドを用意する。 正直言うと、TFormの設計方針や、func、aaa を利用する目的によって設計方針が変わってきてしまうので、対策を断定することは難しいです。

  • 1839cc
  • ベストアンサー率54% (12/22)
回答No.6

う~ん・・・ 気になる点を挙げてみますね。 1.tmp と XXX は、実は同一のクラスのメンバではないですか?   それに対して、func はグローバル関数か、tmp と異なるクラスのメンバではないですか? 2.form1.h のインクルードがありませんが、どの位置でしていますか? 3.「変数が未定義」エラーよりも前に、他のコンパイルエラーはでていませんか? 特に3が気になります。 例えば以下はコンパイルエラーのはずです。 struct aaa{ int iVal; char cVal; }AAA tmp; これだと、確かに tmp は作成されないので、未定義のエラーが発生してもおかしくありません。 ただ、その場合は XXX の中でもエラーが出ているはずですが。 ちなみに、AAA は型名でしょうか。 だとしたら struct の前に typedef が必要です。 そして、以下のようにインスタンスの作成は別のステップに分けてください。 typedef struct aaa{ int iVal; char cVal; }AAA; AAA tmp; これで直らなければ、気になる点1~3の補足をお願いします。 あとは、今回とは関係なさそうですが、良くない点です。 ・XXXの戻り値の型がない。 ・プロトタイプ宣言を行う前に func をコールしている。 ・複数のソースからインクルードされた際に、tmp が複数作成されてしまう(リンクエラー)。その対策がない。

BIGMON
質問者

補足

すみません。構造体の定義はおっしゃるとおりの書き方でした。 NO5の方の補足にソースを付けさせていただきました。 以下、インラインで返信させていただきます。 1.tmp と XXX は、実は同一のクラスのメンバではないですか?   それに対して、func はグローバル関数か、tmp と異なるクラスのメンバではないですか?   →→→tmp と XXX は同一のクラスのメンバのようです。funcがどのクラスに属するのか、よくわかりません。 2.form1.h のインクルードがありませんが、どの位置でしていますか?   →→→ソースファイルの先頭でしております。 3.「変数が未定義」エラーよりも前に、他のコンパイルエラーはでていませんか?   →→→出ていません。先日書いたソースが間違っていました。大変申し訳ないです。

  • yukimican
  • ベストアンサー率70% (112/159)
回答No.5

その簡略化ソースの書き方だとコンパイルエラーになると思うのですが・・・。 意図していたのは、  (1) typedefでstruct aaaをAAAという別名にする。その後、AAA型で変数tmpを宣言  typedef struct aaa{   (略)  } AAA;  AAA tmp;  (2) 「struct aaa」型で変数tmpを宣言  struct aaa{   (略)  } tmp; のどちらでしょうか? このソースを見る限りだと、ヘッダファイルに型だけじゃなくて変数も書いていたのですね。 エラーの原因はまだわかりませんが、ヘッダで変数を宣言するのはあまり良い方法ではありません。 (そのヘッダをインクルードしたすべてのソースファイルで個別に変数宣言することになるため) こういう場合、XXXハンドラとfunc関数で共通の変数を使いたいというなら、 form1.cppの中でstatic変数として宣言するか、 funcの引数として構造体を渡すようにするべきです。

BIGMON
質問者

補足

出張先で思い出しながら書いていたのでまちがえていました。大変申し訳ございません。 (1) のほうでした。 再度ソースを簡略化して示します。 ////form1.h//// class TForm1 : public TForm{  __published: // IDE 管理のコンポーネント    void __fastcall XXX();  public: // ユーザー宣言  __fastcall TForm1(TComponent* Owner);  struct aaa{    int iVal;    char cVal;  };  struct aaa *tmp; }; ////form1.cpp//// int func() ; void __fastcall TForm1::XXX() tmp->iVal = 0; (←ここで使う分には問題なし。) func(); } void func(){ tmp->iVal = 1; (←ここで使うと変数未定義エラー) }

  • yukimican
  • ベストアンサー率70% (112/159)
回答No.4

「イベント処理関数」と「イベント処理関数から呼び出される自作の関数」 は同じソースファイルに書いてあるのですか? また、「変数が未定義」になっているのは構造体自身ですか? それとも構造体のメンバ(基本型以外)ですか? もし後者の場合、構造体の定義よりも前に、メンバ変数の型定義が必要です。 例えば、以下のような場合、(1)か(2)のどちらかにfoo.hのインクルードが無いとエラーになります。 --- foo.h --- typedef struct Foo{  ... } Foo; --- bar.h --- #include "foo.h" // --- (1) typedef struct Bar{  ...  Foo m_Foo;  ... } Bar; --- bar.cpp --- #include "foo.h" // --- (2) #include "bar.h" void func() {  Bar bar;  ... } ちなみに、#includeは、指定したファイルの中身をその位置に挿入するという意味です。 #includeの代わりにヘッダファイルの内容をそのまま書いても、 コンパイラから見れば同じソースコードになります。

BIGMON
質問者

補足

以下、インラインで返答させていただきます。 >「イベント処理関数」と「イベント処理関数から呼び出される自作の関数」 >は同じソースファイルに書いてあるのですか? はい。同じソースファイルです。 >また、「変数が未定義」になっているのは構造体自身ですか? >それとも構造体のメンバ(基本型以外)ですか? 構造体自身です。

  • 1839cc
  • ベストアンサー率54% (12/22)
回答No.3

基本的には、ヘッダファイルに書いても、ソースファイルに書いても、何も変わりませんよ。 正直、これだけではわかりませんね。 どのようなコードを書いたのかが見えてきませんので。 簡略化したコードを示していただけないでしょうか。

BIGMON
質問者

補足

わかりにくくてすみません。 ソースを簡略化して示します。 ////form1.h//// struct aaa{ int iVal; char cVal; }AAA tmp; ////form1.cpp//// _fastcall XXX(){ tmp.iVal = 0; (←ここで使う分には問題なし。) func(); } void func(){ tmp.iVal = 1; (←ここで使うと変数未定義エラー) }

回答No.2

No1です。ちょっとプログラムを書き間違えました。 /*a.h*/ void func_a( void ); typedef struct {   int num; } kouzoutai_t; に直して置いてください。

BIGMON
質問者

補足

 説明が足りなくて申し訳ございません。  ヘッダのインクルードはできており、イベント処理関数( __fastcallがついてる関数)の中では変数を認識してくれるのですが、イベント処理関数から呼び出される自作の関数だと「定義されていない」となってしまいます。

回答No.1

あまり例が良くないかもしれませんがこんな感じでどうですか? 「変数が未定義」とでるのは単に.hを.cppの先頭でインクルードし忘れて いるのではないでしょうか?(私も良く間違えます) /*main.c*/ #include "a.h" void main( void ){   func_a(); } /*a.c*/ #include <stdio.h> #include "a.h" void func_a( void ){   kouzoutai_t test = {0};   printf("%d", kouzoutai_t.num ); } /*a.h*/ typedef struct {   int num; } kouzoutai_t;

関連するQ&A