[先週の内容] データ構造 (C 言語内で) データの組み合わせたものを一つのデータとする仕組み 例 : x, y ともに浮動小数点数型 (x,y) : この二つのものを組み合わせたもの => 「点」という新しいデータ型を作る事になる ! 二つの浮動小数点数型から「点」型を作るのは「概念上」の話 (C 言語は「言語」なので..) このような概念をどう表現するか ? 複数の型から、その直積集合を作る => 構造体 新しい「型」を作るための基本ブロック # プログラム構造に対して、「順接」と同じ仕組み typedef struct { double x; double y; } Point; Pint p; p.x => 点 P の x 座標 p.y => 点 P の y 座標 p.x = 0; p.y = 1; 同じ型を複数組み合わせる(「劣化[同じ型しか組み合わせられない]型構造体」? ) => 配列 double px[2]; /* 点 P を表現 */ px[0] => x 座標 px[1] => y 座標 # 「同じ型の組み合わせ」なので、 # (配列の)メンバが、「添え字」を利用して参照可能 # => 「添え字」:メンバの場所を「式」で表現可能 # => メンバへの参照を、動的に行える # 例: # p.x = 1; /* 点 P の x 座標を 1 にする:固定 */ # # i = 0; # px[i] = 1; /* 同上 : i の値が変われば、意味も変わる */ # px[0] = 1; /* 同上 : この場合はありがたみがない */ # プログラム構造に対して、「繰り返し」と同じ仕組み px[0]=0; px[1]=1; for ( i = 0; i < 2; i++ ) { /* 2 回繰り返すパターン */ px[i] = i; /* 0 から始めるのは、配列処理との相性を考えた.. */ } # for ( i = 1; i <= 2; i++ ) [今日の内容] # 折角、配列を学んだので、そのいろいろな応用をやりたい.. # ! 配列は、メンバ参照をする場合に、効率 *も* 良い # ! => 配列は、コンピュータが持つメモリと同じ構造をしている 共用体: 異なる型のデータのいずれかを意味するデータ型 [実例] /* 例 : 「人」の例 人が、結婚しているか、していないかで、対応を変えたい している場合は、手当をつける していない場合は、結婚を奨励 */ typedef struct { char namae[100]; int kikkon; /* 結婚しているかどうか */ int haiguusya; /* 配偶者手当 (結婚しているとき) */ char dokusin[100]; /* 結婚紹介所の名前(結婚していないとき) */ } Person; /* この構造体では、 haiguusya と dokusin のどちらか一方しか、利用されれない => このままだと、メモリ(記憶容量)が「無駄」になる どちらか、一方しか使わないなら、「共有」してもよい.. Person kurino; kurino.kikkon = 1; kurino.haiguusya = 10000; Person kurihara; kurihara.kikkon = 0; kurihara.haiguusya = "適当な結婚紹介所"; */ typedef struct { char namae[100]; int kikkon; /* 結婚しているかどうか */ union { int haiguusya; /* 配偶者手当 (結婚しているとき) */ char dokusin[100]; /* 結婚紹介所の名前(結婚していないとき) */ } taiou; } Person; Person kurino; kurino.kikkon = 1; kurino.taiou.haiguusya = 10000; Person kurihara; kurihara.kikkon = 0; kurihara.taiou.haiguusya = "適当な結婚紹介所"; 共用体の構文 ( 構造体の struct を union にするだけ ) union タグ名 { 共有する要素並び } 共用体の意味 一つの共用体型の変数には、その要素がいずれか一つだけ入る # cf. 構造体の方は、すべての要素が入る # 数学的な意味は、「和集合」を作っている # 現在は、むしろ、同じビットパターン(メモリ上のデータの状態)を # 複数の型として扱うという目的のために、利用される事が多い # => コンピュータのハードウェアを直接利用する場合に多用される 共用体は、実は条件分岐に対応するデータ型 if ( kurino.kekkon == 1 ) { kurino.taio.haigusya = 10000; } else { kurino.taio.dokusin = "紹介"; } # 次のポインターの概念が導入されると、union を使って # できることが、すべて、ポインターで実現でき、かつ、 # ポインターの方が(歴史的な経緯により..)多用されるので、結局、union は消えている ## さらに、最新言語では、「ポインター」の「高度化版」があり、union は、用意されていない [配列の応用] 配列と「文字列」の関係 # 「文字列」は C 言語の「基本データ型」では *ない* # cf. 基本的なデータ型 : char, int, double, .. # 「文字列」を表現する場合の「型」として 「char *」としてきた (思い出してほしい..) int a[3]; /* a[0], a[1], a[2] */ 「a[0]」は、配列 a の 「0 番目」を意味する ところで... [] の使い方は、前にも出てきたことがある "abc"[0] は、'a' の事 # "abc"[0] == *("abc"+0) == *("abc") == 'a' "abc"[1] は、'b' の事 # "abc"[1] == *("abc"+1) == *("bc") == 'b' # 文字列の操作 # 1 を加えると、先頭の文字が欠けて、文字列の長さが短くなる # * を前につけると、先頭の文字が取り出せる # 文字列の後ろに[] をつけると # 文字列に[]の中身を加えて、頭に * をつけたのと同じ # "abc"[1] == *("abc"+1) 一方、配列では初めから、[] を利用する形だが... 実は、文字列と同じように、 int a[3]; a[1] の代わりに *((a)+(1)) == *(a+1) と書いてよい ということは... 「文字列」って「char 型配列」なの (疑惑..) C 言語では、char 配列の仕組みを利用して、「文字列」を 疑似的に表現している。 違い: 文字列は、 要素が変更不能な、 固定サイズの char 型配列で、 必ず、最後に EOS ('\0') が要素として含まれる もの "abc" のように「"」を使って、簡便にかけるようにしている 逆の言い方をすると、 char 型の配列は、「文字列」を表現する事もできる いままで、「文字列」と呼んでいたもの 「"」で挟まれていいた「文字の列」は、 char 型の配列と同じ形で、内容が変更できない(リテラル:値の変更できないもの)もの 今日から、「文字列」の意味 char 型の配列と同じ形で、 要素の最後に '\0' がはいっているものを改めて「文字列」と呼び、これまでの「文字列」は、「文字列リテラル」と呼んで、区別する 以下、「文字列の操作」という言い方をするときには、 「文字列の制限(最後に'\0'が入っている)」を守った形での文字配列の操作を意味する 関数に引数として、「配列を渡す」事ができる 実引数には、「配列名」が指定できる 仮引数では、「配列をサイズ無しで宣言できる」 実引数で指定した配列の要素は、関数内でも参照可能で、 値が得られるだけでなく、要素に代入も可能 needs : 配列は、その一部だけを変更する場合が多い # 一部だけを変更するようなアプリケーションでは配列を用いる 配列を扱う関数では、配列の要素のコピーは無駄が多いので、やらない(という方針) => 効率が高められる # 配列要素が、呼び出し元と呼び出し先で「共有」される => 良し悪し 悪し : 共有されているので、データが壊される可能性がある 良い : 共有されているので、データの(効率的な)変更が可能 今日の所 : 配列は、配列名を引数に指定する事により、 関数の呼び出し元と呼び出し先で、配列の要素を共有できる # why / how は、次回