計算機言語
II第
6回 構造体
http://www.math.u-ryukyu.ac.jp/~suga/gengo/2018-2/06.pdf
1 構造体
これまでに変数型として, char, int, float, doubleという基本変数型と, ポインタを値に持つことが できるポインタ型, およびこれらの配列を述べました.
純粋に数値だけを計算するプログラムを書くためには,これらの型があれば十分です. しかし,現実のプログ ラムでは,ネットワークサービスをしたり,表計算をしたり,ゲームを提供したりとかなり複雑な処理が要求さ れます.
「複雑な処理」を実装するには,これまでに述べた単純なデータ型では, 実装が大変になります. つまり,複 雑な処理には「複雑なデータ型」があったほうが良いのです. これは, プログラムを書きやすくかつ読みやす くするためには重要なことです. 書きやすく読みやすいソースでないと,複雑なプログラム開発は大変です.
プログラミングでは,処理対象(のデータ)に対して適切な構造を持たせて,さらにその構造に対して効率の 良いアルゴリズム(処理方法)を実装するのが基本です. 「データ構造(Data structure)」と「アルゴリズム
(Algorithm)」の二つの言葉が書名に入った本がいくつも出ていますが, プログラミングに興味のある人はど
れかを取って読んでみてください. プログラミングにおいてとても重要なことなのですが,この講義では,これ らを取り上げることがほとんどできません. コンピュータとプログラムというものが世に出てから,データ構 造とアルゴリズムに関しては,多くの研究や実装がなされています. 必要とあれば,過去の経験にアクセスする というのも常に心がけることですが,やはり,講義ではそのことを述べる時間も多く取れそうにありません.
複雑なデータ型を実現するために, Cでは構造体(structure)と共用体(union)という仕組みがあります. 構造体, 共用体は,いくつかの異なるデータ型をひとつの変数型にまとめるものです. ここでは,構造体につい て述べます. 共用体は,ネットワークサービスを提供するプログラムなどで利用されていますが,構造体のこと がわかっていれば使い方は同じなので省略します.
Cのような手続き型プログ言語(Pascalなど)では,必ず構造体と似た仕組みのデータ構造が用意されてい ます. 最近流行のオブジェクト指向プログラミング(最近開発されているプログラミング言語のほとんどは, 基本的にオブジェクト指向プログラミングができる)では,「Cの構造体に対応するデータ構造に付け加えて, それらのデータを処理するアルゴリズムまで込めて,ひとつの対象とする.」という形で,「オブジェクト」(こ れは, この講義で述べたオブジェクトとは, 少し違う概念)を組み立てることによりプログラムを書いていき ます.
1
1.1 構造体の宣言と使い方
構造体の宣言の基本は次です. 下は一行にしてますが,教科書にあるように複数行に記述しても構いません. struct タグ名 { 変数型A メンバ1, メンバ2...; 変数型B メンバa,...; ...; 変数型Z ...; };
上では,タグ名を持つ構造体型の変数の「型宣言」がされているだけで,変数の宣言ではありません. 構造体の 中にある変数は,「構造体のメンバ」と呼ばれます. 構造体のメンバとなれる変数型は,その構造体が宣言され る以前に定義されている全ての変数型です. すなわち, 基本変数型,ポインタ型,配列以外に,それまでに宣言 されている構造体型をメンバの変数型にすることができます.
例えば, hogehogeというタグ名を持つ構造体を,次のように定義したとします.
struct hogehoge { int hoge; char *p hogehogehoge;};
この時, hogehogeを型に持つ変数の宣言は,
struct hogehoge a;
ちなみに,「線形リスト」と呼ばれるデータ構造を表す構造体の型宣言は, 次のようになります(自己参照構造 体と言われる).
struct list { char str[80]; struct list *next;};
教科書, p. 309のList 12–1 は教科書にあるようにダメな実装です. このようなプログラムは,バグの原因
になりやすく, また修正も大変になりますので, 書いてはいけませんし,この講義でも,このプログラムに対す る時間は取りません.
教科書, p. 310 – 311 に構造体の概念が図とともに説明されています. 構造体のオブジェクト内でのメンバ
の並びが記述されていますが,以前に述べたことと同じで,これは, Cの処理系がプログラマに見せる仮想的な 配置で, 現実の実行時とはずれているかもしれません. ただし, そこの整合性は, 言語処理系やOS が吸収し ます.
1.2 構造体型変数の性質
構造体型変数の性質は,基本変数型,ポインタ型と同じです. すなわち,
• 同じ型の構造体型変数は,代入によりコピーを作ることができる.
• 構造体型変数は,関数の返り値にできる.
• 構造体型変数を関数の引数にすると, 値渡し(Call by Value),すなわち値のコピーが渡される.
構造体型変数のメンバの値の参照は, 変数名.メンバ名
と間にピリオドを利用します.
2
また, 構造体変数の宣言時の初期化は, 教科書p. 313 にあるように, 配列変数の初期化と同様, {, }で囲っ て,コンマで区切ります.
教科書, p. 312, List 12–2, p. 313, List 12–3をコンパイル実行してください. List 12–2で文字列の代入に
は, strcpyというライブラリ関数を利用しています. これは,以前に述べた「配列は代入できない」ことと,配
列名は,配列の先頭要素へのポインタであることから,このようにプログラムするのが最も合理的です.
参照渡しと矢印演算子
上で述べたように,関数の引数として配列は値渡しです. 参照渡しを実現しようとすると,基本変数型と同様 にポインタを利用した関節参照を利用します. 構造体へのポインタにに対しては, その構造体のメンバへのア クセスは,矢印演算子->も利用できます. つまり,この場合, 次の二つのメンバへのアクセス方法が使えます.
(*構造体へのポインタ名).メンバ名 構造体へのポインタ名 -> メンバ名
2 typedef
Cにはtypedefというキーワードがあり,変数型の別名を定義するのに利用できます. 主な用途は, 次のふ たつです.
• 処理系依存のデータ型の範囲をはっきりさせる.
• 構造体変数の宣言などで, いちいちstructキーワードをつけるのを省略する
例えば, C では, short int 型の値の範囲は処理系依存です. 例えば, この講義で使っている処理系では, 16
bitです. そこで,そのことを明らかにするために, typedef short int int16;
とプログラムの先頭部分に書いて置くと, int16 a;
は,変数aがこのソースではshort int型の変数であるという宣言であると同時に, 16 bit整数であること が明示できます. 他の処理系でのコンパイルまで込めたソースを書く際には,cppの条件分岐を利用して(こ のことを講義する予定はありません),どのような処理系であろうと,int16が16 bit整数になるように,ソー スを書くことができます.
ふたつ目の利用方法が教科書に載っているものです.
typedef struct hogehoge{ int hoge; char *hogehogehoge} Hoge;
としておくと, Hoge a;
で,Hogeという構造体型変数 aの宣言になります.
3
教科書, p. 314, List 12–4, p. 316, List 12–5は, ポインタ経由での間接参照,矢印演算子,typedefの例で す. プログラムとして無理矢理な例で感心しませんが, 他に適切なサンプルも無いので, とりあえずコンパイ ル,実行して下さい.
レポート問題: 締め切り 11月15日(木)
送り先は, [email protected]
1. 教科書,演習12-1. 実行結果もレポートすること. 件名Enshu 12-1.
2. 教科書,演習12-2. 件名Enshu 12-2.
テキストの置き場所: ftp://ftp.math.u-ryukyu.ac.jp/pub/gengo/2018-2/
4