- ベストアンサー
マイコンのC言語プログラムの構造体の宣言とvolatileの必要性について
- マイコンのC言語プログラムで、構造体の宣言が不要な場合とは?
- マイコンのC言語プログラムで、volatileが必要なのはなぜ?
- マイコンのC言語プログラムにおける構造体の宣言とvolatileの必要性について解説します。
- みんなの回答 (6)
- 専門家の回答
質問者が選んだベストアンサー
>ためしに >struct st_hudi *p = HUDI; >と記述してみても、型が違うとエラーを吐かれてしまいました。 HUDIは「実体」なので、アドレスが欲しい場合は & 演算子を使う。 struct st_hudi *p = &HUDI; 因みに、構造体のポインタをインクリメントすると、アドレスが+1されるとは限らない。 アドレスが幾つ足されるかは「処理系に依存する」ので、やってはいけない。 もし「構造体や変数は、常に4バイト境界に配置する」と言う処理系の場合、p++すると、pは「構造体のサイズより大きい、4の倍数ごと」にアドレス加算される。 例えば、構造体サイズが1~4バイトなら4、構造体サイズが5~8バイトなら8がアドレス加算される事になる。 今回の場合も、p++した場合、0xFFFE2000が0xFFFE2002になるか0xFFFE2004になるか0xFFFE2008になるか、やってみないと判らない。 もし、やってみて「期待通りになった」としても、コンパイラを別の物に変えたら、期待通りにはならなくなる可能性がある(移植性が無く、処理系依存になってしまう)
その他の回答 (5)
- jacta
- ベストアンサー率26% (845/3158)
処理系不明なので正確なことは何ともいえません。 > 1.上記のような場合は、構造体の宣言はいらないのでしょうか? > 現在は、#defineの部分でアドレスを指定してあるため、構造体の宣言はいらない、という認識を持っています。 確かにそうなのですが、整数値 0xFFFE2000 をポインタにキャストした場合の結果は処理系定義です。 ハードウェア的なアドレスと一致するかどうかもわかりません。 > 2.volatileがなくても動きそうなのですが、これは必要なのでしょうか? > アドレスを明記しているため、volatileがなくても正常に動きそうな気がします。 volatileがなければ、最適化によって、ソースコード上では記述されている読み書きが、実際には省略されてしまったり、順序が入れ替わったりすることがあります。 ただ、制御レジスタなどはワード単位でアクセスしなければならない場合が多いのですが、volatileを付けていたとしても、バイト単位でアクセスしてしまう可能性があり、正しく動作するかどうかわかりません。 実際には、特定のハードウェアや特定のコンパイラに特化したコードだと思いますので、そうした状況下では正しく動く可能性はあります。
- chie65536(@chie65535)
- ベストアンサー率44% (8740/19838)
>1.(*(volatile struct st_hudi *)0xFFFE2000)この部分の考え方は >(volatile struct st_hudi *)が明示的なキャストを示しているのでしょうか? そう。 「(volatile struct st_hudi *)0xFFFE2000」は、0xFFFE2000を構造体のポインタにキャストしている。 >そして、*がついているので、0xFFFE2000の中身を表すということで合っていますか? ちがう。 HUDIを「 (*『何かのポインタ』)」と定義している。 「何かのポインタの中身」として定義しているから HUDI->SDIR.WORD = 0; ではなく HUDI.SDIR.WORD = 0; と書く事が出来る。 >2.volatileがなくても動きそうなのですが、これは必要なのでしょうか? 必要。 これの意味は「書き込む際、読み出す際に、前の値がどうなっているか忘れて、毎回、指定のメモリに読み書きしなさい」って意味。 じゃないと a=HUDI.SDIR.WORD; /*最初の状態を読み出す*/ (一秒待つ処理) b=HUDI.SDIR.WORD; /*1秒後の状態を読み出す*/ って言うコーディングをした時に、最適化されてしまって a=HUDI.SDIR.WORD; /*最初の状態を読み出す*/ (一秒待つ処理) b=a; /*1秒後の状態を読み出す筈なのに読み出さない*/ というプログラムになってしまいます。 余計な最適化されちゃうと、もし「aに読み出した後、1秒後にbに読み出し、aとbが違う値になっている時に、何か行う」って言う書き方をしたら、正常に動作しません。 なので「読み書きする時は、面倒でも、毎回ちゃんと読み書きしてね」って言う指示が「volatile」なのです。
補足
1. 認識を正していただきありがとうございます。 言われるまで「->」か「.」かは気にしていませんでした。 2. >読み書きする時は、面倒でも、毎回ちゃんと読み書きしてね このように柔らかく表現していただけると、理解しやすいです。 ありがとうございます。
- unacyo
- ベストアンサー率51% (35/68)
1については、その通りですね。 私はポインタでしか使いませんでしたが、キャストの前に*を着けると、"."でのアクセスになります。 2については、無いと動作が変わります。 ステータスレジスタの類いは、書き込むのはハードウェアで、ソフトウェアは読むばかりなので、最適化が働くと読み込みが省略されて、まともに動作してくれません。 複数回連続で読み込む場合とか、読み込まなかったり、一回しか読まないコードになっていたりと、困った結果になります。 新人の頃これを知らず、デバッグで苦労した覚えがあります。
補足
回答ありがとうございます。 volatileについての知識が深まりました。 私は現在新人で、同様に苦労していました。
- KEIS050162
- ベストアンサー率47% (890/1879)
(ファーム系のプログラミングから離れて、相当年月が経っているので、かなりウロ覚えではありますが…) 1.の認識は概ねその通りです。 define分で、既に共用体の宣言がされている実アドレス(恐らく、メモリマップされたI/O空間なのでしょう)が定義されているので、参照するときは、共用体の中のどの型で参照しているか(構造体を利用するときは、そのメンバー)を明記するだけで参照できます。 例では、WORD (unsigned short)で参照されています。(ビット参照、ワード参照が可能な様に共用体宣言されているのでしょう) レジスタ制御する為の共用体宣言の例 http://monoist.atmarkit.co.jp/mn/articles/0701/23/news094.html 2.必要です。 コンパイラの最適化の処理によりますが、意図しない最適化を防ぐために、明記しておくのが無難でしょう。 具体的な例は、下記URLを参照してください。 http://proger.blog10.fc2.com/blog-entry-20.html 要は、先ほど述べた メモリマップされたI/Oなどの場合、初期設定以降は単に変化を待ち続ける場合多々ありますが、それを明示してあげないと、コンパイラが”恒久的に変化しない変数”という風に勘違いしてしまうことがあって、プログラマが意図しない方向に最適化してしまう場合がある、ということですね。 ご参考に。
補足
参考URLも記載していただきありがとうございます。 URL先を読ませていただきました。 その結果、以下のような新しい疑問が出てきました。 もしも、このような手法(任意のアドレスに宣言したい)で構造体の配列を用いたい時はどのようにすれば良いのでしょうか。 構造体のポインタを作成し、インクリメントしていけば大丈夫なのでしょうか? ためしに struct st_hudi *p = HUDI; と記述してみても、型が違うとエラーを吐かれてしまいました。
- hashioogi
- ベストアンサー率25% (102/404)
Q.通常は、構造体の宣言(struct st_hudi aなど)を行わないと使えないと思うのですが… A.プリプロセサが HUDI.SDIR.WORD = 0; を (*(volatile struct st_hudi *)0xFFFE2000).SDIR.WORD = 0; にしますので翻訳のエラーは出ません。 Q.アドレスを明記しているため、volatileがなくても正常に動きそうな気がします A.volatileは、マルチタスクで動作していたり、割り込みが動作するような場合は誰がいつアドレス0xFFFE2000の内容を書き換えるかわからないのでこのアドレスをアクセスする場合は最適化をしないで下さいという指定です。ですからvolatileを外したら正常に動かなくなる場合があります。
補足
1.(*(volatile struct st_hudi *)0xFFFE2000)この部分の考え方は (volatile struct st_hudi *)が明示的なキャストを示しているのでしょうか? そして、*がついているので、0xFFFE2000の中身を表すということで合っていますか? 2.納得できました。
補足
移植性については全く考えていませんでした。 構造体を配列にするのではなく、構造体のメンバを配列にすることで対応しようと思います。