×

らずらいと姫の挑戦日記(第15回)~コントローラーのプログラムを作るぞ!③~

2016-07-12

コントローラーのプログラムを勉強している間に、何度が登場している『&』と『*』。今回はこの『&』と『*』について勉強したいと思います!

7

『*』とか『&』とか何気なく付いているこの記号が何だろう??と思っていたら、どうやらポインタとのこと。1号さんいわく、「hello world」や「Lチカ」が出来た人が最初にあたる壁だそうです。たしかに、ポインタの仕組みや役割をネットで調べたり、本を読んでみたりしましたが、さっぱりわかりませんでした・・・。

なので、できるだけ簡単に理解してもらえるよう、頑張って説明したいと思います。

いきなりポインタを説明するのも難しいので、身近なシチュエーションで考えてみたいと思います。

ソフトウエアは、関数を組み合わせて機能を実現していきます。関数というのは作業の単位で仕事をまとめたものになります。これを組み合わせるので、関数を呼び出す側は仕事の指示する人、呼び出される関数は作業する人という事になります。

代表的なお仕事のパターン:

24

 

パターン1:2

最初は文書校正の仕事で考えてみます。校正の指示をする人は、文章が書かれていて校正結果を直接書き込んでよい紙を渡して、作業する人は直接間違いを直したり、書き加えたりします。校正が終わったら、終わった旨を通知します。

この時、校正結果を直接書き込んでも良い紙を渡しているという事をできるようにしているのがポインタになります。

パターン2:2

次は計算機を例にしてみたいと思います。計算機を使う人が指示する人、計算機が作業者になります。指示する人は数字が書かれた紙をみて計算機にデータを入力します。その結果が計算機のディスプレイに表示されたので結果を自分で紙に書きます。

この時、計算機は指示する人が持っている紙に対して何も影響を及ぼしません。

仕事のやり取りを分解してみましょう

この二つのお仕事でどのようなことが行われたのか分解してみたいと思います。

「仕事を支持する側と受ける側のやり取り その1」 ポインタ渡し

パターン1では文章が書かれており、そのまま変更してよい紙を渡しました。これはC言語でいう「ポインタ渡し」というやり取りになります。。

原本そのものを渡したり、原本が保存されている場所(アドレス)を教えて、結果を返してほしい時に『&』を使っています。

仕事の指示をする人はプログラム作成者やORIZURUのコントローラで、作業をする人はSUBGHZだったりします。

プログラム例です↓↓↓

  1. SubGHz.getSendMode(&param)
  2. SubGHz.setSendMode(&param)
  3. msg = SubGHz.send(PANID, HOST_ADDRESS, (unsigned char*)&data_packet, sizeof(data_packet),NULL)

1は、初期値のデータをとってきてまっさらな紙の原本「param」という名前のメモリへ書き込みます。以前に勉強したように、「param」は無線の色んな項目の設定が入っていますね。その書き込まれた「param」を結果として返してほしいので『ポインタ渡し』にしています。

stamp19「仕事を受けた側がコピーを返せばいいじゃん?」

人間の仕事の中では、「原本を返す」ことも、「コピーを返す」ことも当たり前のように出来ますよね。でも、C言語の世界では呼び出された関数がメモリを確保して「コピーを返す」という事は基本的に出来ないそうです。仕事を受けた側は「戻り値」以外に自分で確保したメモリの中身を返す方法が無いのです。結果を返してほしい時は、仕事を指示する側が結果を書いてもらうメモリを確保し、それをポインタとして渡す必要があるそうです。色んな決まり事があるんですね!

20

「仕事を支持する側と受ける側のやり取り その2」 データ渡し

パターン2は、計算機に計算して欲しい数字を入力しました。仕事を受けた側の計算機は原本に対して何も影響を及ぼしません。これはデータ渡しになります。

最も簡単な例は、digitalWriteになります。ORANGE_LEDやLOWというパラメータはデータ渡しでdigitalWrite関数にデータを渡しています。

digitalWrite(ORANGE_LED,LOW)

「オレンジのLEDをLOWで光らせる」という、投げっぱなしのプログラムです。答えは必要ないですね。なので『データ渡し』です。

「仕事を指示する側と受ける側のやり取り その3」 戻り値

パターン1、2共に、終わったとか、簡単な報告は必要です。そのような結果を返すのは戻り値が便利です。計算機のような簡単な作業なら、戻り値で仕事の結果を返すことも可能です。

この最も簡単な例はdigitalReadになります。

digitalRead(i)

この時、iの値はデータ渡しですが、PIN番号=iの端子の状態 HIGHまたはLOWは戻り値としてdigitalRead関数が返してきます。ですから、次のように式に組み込んで使用すると、digitalRead(i)の部分が実際の結果に置き換わってswに結果が格納されていくわけです。

sw=(sw<<1)+((digitalRead(i) == LOW) ? 0 : 1)

ポインタの使い方

SubGHz.send()の部分が実際にどのようになっているのか説明をしていきたいと思います。

SubGHz.send(PANID, HOST_ADDRESS, (unsigned char*)data_packet, sizeof(data_packet),NULL)

ORIZURUの制御をするためのスライダーやジョイスティックの値は

unsigned short data_packet[4]に保存されています。一方、SubGHz.sendはデータを送るというお仕事をしてくれる関数で、その時は「unsigned char* data」で送信するデータの先頭ポインタを指定して、その次に送信するデータの長さを指定する必要があります。仕事を指示する側が便利なデータの形と、仕事を受ける側のインタフェースが異なってしまいました…

13

stamp2どうしよう…

たとえるなら、A3の紙を送りたいのにFAX機がA4にしか対応していないというところでしょうか…
やり方は簡単です。2枚に分ければよいのです。

14

上の例の場合、data_packetの先頭ポインタを指定して、送信するデータを8にすれば良いのですが、コンピューターは、「あれ?data_packetってunsigned shortじゃなかったっけ???」と混乱してしまいます。そこで、これは「unsigned char型のポインタとして扱ってください」と明記するわけです。

この書き方が、「(unsigned char*) &data_packet」という事になります。「&」はデータ名の名前の先頭につけることで、「そのポインタを使ってね!」というC言語のお約束になっています。

15

また、ここでは送信するデータの長さをsizeof(data_packet)にしていますが、これは自動的にdata_packetのサイズに置き換えてくれます。コンピュータの都合でサイズが変わることがあるので、sizeofを使用してデータの長さを指定したほうが安全です。

以上、ポインタの説明でしたが、まだまだ奥が深そうで・・・。でも、「数をこなして慣れれば大丈夫!」という一号さんの言葉を信じ、頑張りたいと思います!

次回は、7月19日(火)にお届けする予定です。

Laz-princess_footer