• ベストアンサー

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

はじめて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

  • 構造体の宣言

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

  • 複数ファイルによる共通の外部変数定義について

    初歩的な質問なのですが、今各関数をファイルごとに分けて記述しようとしています。 ここで詰まっているのでできればご教授お願いします ファイル構成は プログラムファイル main.cpp sub.cpp ヘッダーファイル  myheader.h(main.cppとsub.cpp両方でインクルード) ヘッダーファイルには両方のプログラムファイルで使う変数が定義してあります。 それでmain.cppとsub.cpp両方で同じ変数を使いたいのですがコンパイルエラーが発生してしまいます。 内容はsub.obj側で「i(共通のループカウンタ)はmain.objですでに定義されています」というものです。 とにかく私のやりたいことはヘッダーに外部変数を定義して両方のプログラムファイルで使いたいというものです。 もしかしたらファイルを分けるにも何か設定が必要なのでしょうか? 私はただ[プロジェクト]→[プロジェクトに追加]→[新規作成]でソースファイルを作っているだけなんですが・・・ 上記の状態を回避する方法はあるのでしょうか?

  • 構造体について

    linuxのソースコードを呼んでいる者ですが、構造体について、実際に使う方法が分かりません。 ソースコードを少し書き換えて改良したいのですが、構造体が定義されているので、それをそのまま引用しないといけないので分からなくなってしまいました。 例えば、それぞれの関数のなかで最初の変数宣言の部分にstructが付いているものもあれば、付いていないものもあります。 この差はなんなのでしょうか?

  • 構造体型の宣言を幅広く使うには・・・?

    すみません。 プログラム全体で使う定数、#defineの定数のように 1つの構造体型をプログラム全体で使いたいのですが、 2重インクルード防止コードの所為か所々未定義扱いになってしまいます。 そして2重インクルード防止コードの外では今度は再定義のエラーが。。 構造体型の宣言をプログラム内で幅広く共有するには どうすれば良いのでしょうか?

  • 構造体のアドレス渡し

    構造体を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関数に構造体を渡したい } どうかよろしくお願いします。

  • C++ visualstudio グローバル変数

    C++のプログラミングに関する質問です。 visual studio2008を使用しています。 手元に非常に複雑なC++ファイルなどから構成されている1つのprojectがあります。(ヘッダファイルなどもあります) そのため、このprojectをデバッグすると、A.cppファイルをデバッグ→B.cppファイルをデバッグ→A.cppファイルをデバッグ→C.cppファイルをデバッグのように、色々なcppファイルを跨ってデバッグします。 1つのcppファイルには大量の関数があり、cppファイルの先頭や,関数外の部分に例えばA.cppで int test などと宣言すれば、A.cppファイル内ではtestと名付けた変数をすべての関数で使えます。 しかし、あくまでA.cppファイル内だけで使用できるだけで、別のBやC.cpp内で使うことはできません。 このtest変数をB.cppやC.cppファイルでも使えるようにするにはどうすればいいでしょうか? 上で挙げた例でいうなら、A.cppからB.cppファイルに移動する際の関数の引数として渡す方法は考えられますが、実際には、 test変数を使いたいのはかなり後に登場するcppファイルで関数の引数として扱うのは非常に大変です。 (Z.cppでtest変数を使いたい。しかしZ.cppに到達するまでにはA~Y.cppを通り、その間に登場するすべての関数でtestを引数にしなければならない) 関数の引数でtest変数を使えるようにする以外の方法がありましたたら教えていただけると幸いです。 実際にやりたいことは A.cpp内にある関数が実行されたらその数をカウントし(A.cppのこの関数は何度も呼び出される関数) //count ++ このcount++の値によってZファイルのある関数での動作を変えたいと思っています。 if(count<100){ printf("aaa"); } しかし、現状ではZ.cppファイル内ではcountが定義されていないので上のようなif文を書くとエラーになってしまいます。

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

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

  • 左側がクラス、構造体、共用体、ジェネリック型への

    VS2008 でVC++のコンパイルをしたときに、 「error C2227: '->SetValueXyzwpr2' : 左側がクラス、構造体、共用体、ジェネリック型へのポインタではありません。」のエラーがでました。ネット上で、このエラーを探すと、GetValueXyzwprが定義していないとありますが、同じソース上の違う関数の中で使っているところではエラーがでません。今回新たに記述したところで発生しています。 【ソース】frrjiftestDlg.cpp void CFrrjiftestDlg::PrintFrameOfData(FILE *handle, sFrameOfData *FrameOfData)    pSysVarPos->SetValueXyzwpr2(X,Y,Z,W,P,R,E1,E2,E3,C1,C2,C3,C4,C5,C6,C7,UF,UT); ←ここでエラーがでます。 同じ、cpp 上で、 void CFrrjiftestDlg::OnButtonSetSysvar()  ←ここの中でも同じ使い方をしていますが、こちらではエラーはでません。 まったく、理由が分からず困り果てています。

  • 構造体のポインタに関する質問

    構造体の中にポインタ変数を置いたのですが、代入時に members.*pstack=*pushdeta; と記述すると、 エラー E2451 C:\WINDOWS\デスクトップ\作成\s\ds.cpp 85: 未定義のシンボル pstack(関数 stack::push(int *,char *) ) とエラーが出てしまいます ちなみに↓がその構造体です struct stack_member{ int *pstack; char *chpstack; int *pfornt; }members; このエラーの解決方法がわかる方が居たら教えてください (*を取れば、エラーは出ない) ちなみにこれを書いた言語はC++です

  • 参照送りした構造体のメンバ変数の扱い方が

    自作関数に構造体のポインタを送り、メンバ変数の値を扱いたいのですが、 &で送り、*で受け取り、*とアロー演算子でメンバを指定しても 「error C2100: 間接指定演算子 (*) の使い方が正しくありません。」 と出てしまいます。 変数と構造体とでは勝手が違うのでしょうか? どうすればメンバ変数の値を扱えるのでしょうか。