第五回C言語講習

執筆者:mznh

今回は構造体とbit演算を行います。

ポインタを乗り越えた皆さんからすれば今回の内容はおまけみたいなものです。

ですが構造体は複雑なプログラムを記述するには必要不可欠ですしbit演算はマイコンプログラミングには無くてはならない存在です。

構造体

これまでに配列という同じ型のデータを番号をつけてまとめたものを学びました。

配列を使えば出席番号を添え字にしてその人の成績を管理するといった処理が簡単に書けます。

ではこういった場合はどうでしょうか。

ここでgrades[生徒数][教科数] のように宣言して教科数の添字を国語は0、算数は1というように予め決めておけば良いと考えた方、

またはjapanese[生徒数] math[生徒数]というように教科ごとに配列を宣言しておくことを考えた方、素晴らしい、どちらも正解です。

でもさらにこういう場合はどうでしょう、

生徒の名前はchar型の配列もしくはポインタ、身長や体重は実数なのでfloatやdouble型です。

型が違うので多次元配列では表現できません。

まぁそれぞれの型でname[生徒数] height[生徒数] のように宣言すればこれは対応できるのですが、これはこれで困ったことがあります。

例えば生徒のデータとは別に教師のデータも保持しておきたいとします。

教師にも名前や身長があります。

そこでそれぞれ配列を宣言してみると

//仮に生徒数を100、教員数を10としておきます。
int student_japanese[100];  //
int student_math[100];      //生徒のデータ
char* student_name[100];    //
double student_height[100]; //

char* teacher_name[10];     //
double teacher_height[10];  //教師のデータ

ちょっと見づらいですね。 さらに体重や視力、住所などが増えたらどんどん見づらくなってしまいます。

生徒のデータは生徒のデータだけでまとめて管理したいですね。

そこで今回学ぶ構造体というものが必要になってきます。

構造体というのは複数の変数をひとまとめにした変数です。

上の例では生徒に対して名前、身長、各教科の成績をひとまとめにしたいですね。

つまりそれらをまとめた生徒(student)という構造体を作ってあげて、

そのstudent構造体を配列で宣言してあげれば良いわけです。

以下にそのイメージを示しましょう。

student構造体は以下のような構造体です。

|                                      |
| |     |      |          |          | |
| |     |      |          |          | |
| |_____|______|__________|__________| |
|___name_height__japanese_____math_____|    
               student

studentsの中にはnameやheightといった変数が内包されています。

あとはこのstudent構造体という大きな箱を配列で宣言してあげれば良いです。

構造体のイメージ、つかむことができたでしょうか?

では実際に構造体の宣言や使い方を学んでいきましょう。

構造体の宣言

構造体の宣言は今までの普通の変数やポインタとはちょっと違います。

というのもいきなり使える構造体を宣言、というわけにはいかないのです。

まず最初にほしい構造体の設計図を宣言してあげる必要があります。

それにはstruct という単語(この単語の名前が調べても出てこない)を使います。

では先ほどの例のとおりにstudentという名前の構造体の型を宣言してみましょう。

struct student {
  char name[20];
  double height;
  int japanese;
  int math;
};

これでstruct student型 が定義されました。

あとは普通の変数のように宣言できます。

struct student yamada_tarou;

上記のようにするとstruct student型のyamada_tarouという変数が宣言されます。

一般化すると以下のようになります。

struct 構造体名 {
  型 変数名;
    :
    :
};

メンバへのアクセス(ドット演算子)

さて、構造体の変数を宣言したあとはその各要素に値を代入してみましょう。

構造体の各要素のことをメンバと呼びます。

メンバにアクセスするにはドット演算子を用います。先ほどの例によりjapaneseに値を入れるには以下のようにします。

yamada_tarou.japanese = 50 ;

これを一般化すると以下のようになります。

構造体変数名.メンバ名 

アロー演算子

普通の変数と同様に構造体にもポインタがあります。宣言の仕方などは普通の変数と同じです。

struct student* student_p;

以上のようにするとstrucut student型のポインタが宣言されます。

こうしてできた構造体のポインタを通してメンバにアクセスする場合「->」アロー演算子を使います。

student_p = &yamada_tarou;

student_p->japanese = 60;

typedefマクロと構造体

今まで構造体の宣言をするときはstruct 構造体名 変数名;としてきました。

typedefマクロを用いるとこれを構造体名 変数名; にすることができます。

typedefマクロ

typedefマクロとは変数の型に新たな名前をつけるマクロです。例えばint型にbignumという名前を付けたいときは以下のようにします。

typedef int bignum;

後は普通の型と同じように宣言できます。

bignum hoge;
hoge = 100;

これを構造体の宣言に応用すると、

typedef struct{
  メンバ宣言;
     :
} 構造体名;

と宣言して、変数の宣言を以下に簡略化できます。

構造体名 変数名;

bit演算

C言語講習最後の題目はbit演算です。マイコンプログラミングにおいては一番大事な題目かもしれませんのでしっかりと取得してください。

bit演算子は変数を値ではなくてbit単位で計算します。ですので2進数の知識が必要になりますがこの講習資料の中では説明しません。各自調べておいてください。

bit演算子

bit演算子にはAND,OR,NOT,XORの4種類があります。

それぞれ記号 & | ~ ^で表し、以下のような演算をビットごとに行います。

引用画像

ITパスポート試験対策 BINARY「離散数学の解説」(http://it-passport.sophia-it.com/content/テクノロジ系/基礎論理/基礎論理/離散数学)より引用

シフト演算子

シフト演算子はbit単位で値をずらします。わかりにくいので具体例を示しましょう。

10という値があったとします

これは8bitの2進数で

0b00001010

ですのでこれを左に1ずらすと

0b00010100

になります。 またこのずらされたものを右に2ずらすと

0b00000101

となります。これを行うのが「>>」「<<」シフト演算子です。

それぞれ右、左にシフトする演算子です。

例えば右に3シフトさせたい場合は

int hoge = 10;
hoget = hoge<<3;

とします。(hoge>>3がシフトされた値を返しています。)

演習

演習1 :名前、年齢、身長(単位はm)体重(単位はkg)のデータを持つ構造体を宣言せよ(型は各自で考えよ)

演習2 :1で宣言した構造体の配列を宣言して中に適宜値を代入せよ。

演習3 :1で宣言した構造体を引数にとり、そのデータのBMIを計算して返す関数を宣言せよ

演習4 :1で宣言した構造体のアドレスを引数にとり年齢の値を1加算する関数を宣言せよ

演習5 :int型の変数を宣言し10を代入した後にその変数を左に1シフトさせたものの値を出力せよ(いくつになるか考えてから出力するとよい)

演習5 :int型の変数a,bにそれぞれ10と20を代入した後にaとbでANDを取ったものの値を出力せよ(いくつになるか考えてから出力するとよい)

おまけ

もう少し複雑なプログラムが書けるようになりたい人は以下のAOJの問題をやってみてはどうでしょうか。

各回へのリンク


クリエイティブ・コモンズ・ライセンス
この 作品 は クリエイティブ・コモンズ 表示 - 非営利 - 改変禁止 2.1 日本 ライセンスの下に提供されています。免責事項