計算機言語
I
第10
回教科書
12
章: typedef
と構造体この資料: http://www.math.u-ryukyu.ac.jp/~suga/gengo/2020-1/12.pdf
レポートへのツッコミ
今回遠隔授業になったせいで, C.言語の処理系が様々になりました. long double のサイズは, 処理系に よって実装が様々になりやすいものの一つです.
最も新しい実装では, long double は IEEE–754 規格の 128ビット(16バイト) 浮動小数点数に準拠 するようですが, 少し古い処理系では 80 ビット(10 バイト)のものもあるようです. この 80ビットの long double では, 配列の次のデータのアドレスの差は12(96ビット)となる実装もあるようです. 96ビッ トは32ビットの3倍で, 32ビットを基本とするシステムでは扱いやすいからです.
今では, PCのほとんどのOSは 64ビット OSとなっており, long double も128 ビットが多いようで す. ただし,家電製品の中にあるようなコンピュータ(いわゆる「組み込み系」と呼ばれるもの)だと, 32ビッ トのシステムもまだ多く存在していると思います.
教科書 第 12章: 構造体
今回は,教科書12章の構造体の話をします. C言語の文法では,処理系に依存しない重要な内容だからです.
12.1: typedef
typedefはプログラマが新たな変数の型を定義することができるための仕組みです. 使い方は, 教科書にあ る通りです. 次に述べる構造体の記述を簡略化するのに多く用いられますが,これ以外にも,コンパイラの仕様 の違いを吸収するのにも用いられます.
よく用いられるのが, 基本データ型の範囲の違いです. Cでは, 基本データ型(例えば int)に対して,その 値の範囲を仕様で規定していません. これは,「Cはハードウェアに密着して,より効率の良い実行ファイルを 作成する」というのが開発目的であったためで,その特徴を残すためにこのような仕様になっています.
ところが,プログラマ的には,例えばlong int型が32bitだったり 64 bitだったりの変化があると,処理 系に合わせてプログラムを書き換えなければならない事態が発生します.
1
最近のCでは,ヘッダファイルでこの処理系の差を吸収する仕組みが用意されています. 例えば, Linuxで は,次のファイルにその処理系のサイズに合わせた整数型の定義がなされています.
/usr/inlclude/stdint.h
このファイルの在処は, 処理系によって異なりますが, 現在では, ほとんどの処理系で置かれています
(Explorer やFinder でファイル検索をしてみて下さい). 各自どこにあるか探してみて下さい. ソースコー
ドで,
#include <stdint.h>
としておくと, uint32_t
という型を使うことができて,その処理系に合わせた符号無し32bit整数型であることが保証されます.
構造体
構造体は,複数の変数型をまとめて一つの変数にしたものです. 配列が,同じ変数型を複数個まとめたもので あることと対比できます.
教科書の補足
構造体型の変数をただひとつ使う場合だと,構造体タグも不要です. 例えば教科書のseisekiと同じ構造体 型の変数personを宣言するには,次のようにすればOK です.
struct { int num;
char name[20];
char sub[20];
char score;
} person;
しかし, これだと,同じ構造体を持つ変数を宣言する度に同じことを繰り返す必要があります. また, プログラ ムの可読性の問題もあります. そこで,教科書のように構造体タグseisekiを書いておくと(p. 192中程),教
科書p. 193, 4行目のような変数宣言が可能になり,さらにtypedefを用いて, 新しい型の定義を同時にする
ことで,より簡単な変数宣言が可能になるのです. (教科書p. 193,下から4行目) 構造体メンバへの参照方法は教科書の通りです.
PascalやModula–2というプログラム言語を開発したN. Wirth (ビルト)の有名な著書に, アルゴリズム+データ構造=プログラム(原題Algorithms + Data Structres = Programs) というものがあります(日本語訳は,残念ながら現在絶版中).
2
この講義では, Cの文法を中心に述べていますが,本格的なプログラミングでは,「処理対象に対して適切な データ構造を定義して,さらに処理を行う適切なアルゴリズムを記述する.」が求められます. 構造体は「適切 なデータ構造の定義」のための仕組みです.
いくつかの注意
教科書では,構造体の配列が書かれていて,教科書通りの内容以外に述べることはないのですが,実践的なプ ログラムでは,「構造体へのポインタの配列」を用いられることが多いです.
理由は,前回述べた「配列のコピーができない」というのと同じ部分です.
前回少し述べましたが,構造体は,文法上「代入によるコピー」ができます. すなわち,変数a, bが構造体 型だとすると,
b = a;
は,変数bのメンバの全てに,aのメンバの値が代入されます. しかし,このコピーの動作は, CPU的には「ひ とつひとつコピーする」以外に方法がなく, 手間がかかるわけです.
実践的なプログラムでは,ポインタをうまく利用して, できる限り「構造体のコピー」のようなことは発生 しないようにします.
この辺が上で述べた「アルゴリズムを知る」部分と関係します.
教科書のプログラムで用いられているstrcpy は文字列をコピーするライブラリ関数ですが, 基本的に
ASCIIコード用に設計されているため,日本語が入った文字に使うには, 注意が必要です.
教科書では, 複素数を構造体を用いて定義していますが, 99年の仕様 C99で複素数型が定義されました. complex.hというヘッダファイルを探して読んでみてください.
3
レポート問題: 締め切り 7月27日(月) 10:00 JST
今回教科書で,成績処理プログラムの例が挙げられています. 計算機概論I でawkを用いた成績処理を例示 しました. 次回以降,同じような内容をCで書くという事を考えていきます. 今回は,成績を乱数発生機能を 利用して作成することにします. 件名: gengo2020-1 report 10
次のような出力をするプログラムmkdata.cを書け. 183101B 84
183102A 39 ...
中略
...
183139K 29 183140C 77
ここで, 1列目は, 2018年度入学の入学生40名の学籍番号(チェックディジット付)で,学籍番号とチェッ
クディジットは次のルールで与えられる.
18 3 1 01 B
入学年度 理学部は3 数理科学科は1 学科内の番号 チェックディジット α, βを0から9 までの数とし, 学籍番号の数字部分が1831αβであったとき,
7× 1 + 8× 6 + 5× 3 + 4× 1 + 3× α + 2× β
1桁目 2桁目 3桁目 4桁目 5桁目 6桁目
の値を11で割った余りの下1桁をγ とする. γ の値によってチェックディジットは,次の値になる.
0 1 2 3 4 5 6 7 8 9
B A K J H G F E D C
2列目は, C の処理系にある擬似乱数発生関数 rand()を用いて得点らしいデータを作成する. rand()を 利用するには,ヘッダファイルstdlib.hをincludeする必要があるので,プログラムの初めの方に
#include <stdlib.h>
を記述する.rand()は0からstdlib.hで与えられているRAND_MAXまでの整数値を生成する擬似乱数発生 関数なので, これを 0から100(99でも良い)の整数値に, 割り算を利用して変換する. (注意: 上手に割り算 しないと整数値のオーバーフローが起こる.) この変換では, double 型の値が出てくるが,これをint型に キャストするなり,「近い整数値にする」なりをする. 例えば,double型の変数 aに対して,(int) aはaの 値を整数値に変換する. (その際,四捨五入なのか切り捨てなのか切り上げなのかは, ここでは問題にしない.) 数学関数rint(a)はdouble型の変数値aにもっと近い整数値を返す.
4