前回の内容 : データ構造 (6) 講義 ポインタ型 変数のアドレスを利用して、 変数の内容を間接的に操作するためのデータ型 値 変数のアドレス値 : printf ( "%p" ) で出力する値 変数に対応するメモリの先頭セルのアドレス(番地) そのアドレスに置かれている変数の型の情報ももっている この型情報 ( コンパイル時の情報 / 実行時にはない ) sizeof(int) コンパイル時点 「int」という型情報をもっている この型情報を利用して、コンパイラが、 sizeof(int) を 4 に変換する 実行時点 「4」という整数値しかもっていない コンパイル時 ポインタ値 -+-- アドレス値 ( 実行時 ) | +-- 型情報 ( 「どんな型」の変数のアドレスか ) => * を付けると、その型の変数としてふるまうモノになる +---+-- どんな範囲の値/どんな演算 | +-- 型のサイズ : sizeof (型名) => その型の値を保存する変数が メモリ上で、占めるセル数 アドレス演算子「&」 変数(のようふるまうモノ:メモリのセル並び)のポインタ値を得る 間接(参照)演算子「*」 ポインタ値から変数を得る !! ポインタ値は、常に、対応する変数と対に考える !! 変数(メモリのセル並び)に関する情報がポインタ値 !! 変数の素描 !! 名前で区別 !! 値を参照したり、代入したりできるもの !! # 実態 : メモリセルの並び !! ポインタ値をもっているもの !! # 名前を使って操作する代わりに !! # ポインタ値を使って操作できる !! # => 変数名を使って操作できる 変数にできる事 変数名 値の代入と参照 値でないの値の操作ができない ポインタ値 値の代入と参照 値なので、値の操作ができる !! 値の操作 !! 関数へ渡す/値として返す !! 色々な計算ができる 「変数」を導入した理由 変数宣言 変数名 名前なので、区別がしやすい 変数を作る(メモリの一部を占有) ポインタ値 値のなので、区別がしにくい 変数を作ってから取り出す情報 型変換と型の昇格 型が違う 型 値の取りうる範囲 ( その型の値が入る集合 ) char (ASCII Code で表現される)「文字」なので char 型の値の集合 = { ... 'A', 'B', .., 'a', .. } int 整数値 ( - 2^31 〜 2^31 - 1 ) int 型の値の集合 = { n \in Z | -2^31 <= n <= 2^31-1 } double ( 実数値の一部 ) double 型の値の集合 { .., -0.1, 0.0, 1.0, 2.0, 3.5, ... } その型の値に対する演算の集まり int 型 : 四則 ( +, -, *, / ) や余り ( % ) !! int 型の割り算の結果は int 値 ( 小数点以下、切り捨て ) double 型 : 四則 !! double 型の割り算の結果は double 型 ( 小数点数以下も計算 ) # どんな演算ができるかだけででなく、 # 演算する内容(それを実行するプログラムが違う)が異なる 一つの式の中に、複数の型が含まれている場合の計算 !! 数学の場合は、自然に、大きな集合の中で計算するだけ !! 例: 部分空間の要素の計算は、全体の空間の要素の計算に一致する !! C 言語の場合は、上記の関係が保証されれない !! 例 : int 型にも、double 型にも 1 (1.0), 2 (2.0) !! 1/2 -> 0 !! 1.0/2.0 -> 0.5 型が混在する場合は、 (一つの型の中で[矛盾なく]計算するため) 値の型変換 ( 狭い方から広い方への変換 : 型の昇格 ) が行われる 1/2 -> 0 ( int 型 x int 型 -> int 型 ) 1.0/2.0 -> 0.5 ( double 型 x double 型 -> double 型 ) 1/2.0, 1.0/2.0 int 型 x double 型 -> double 型 x double 型 # int 型 => double 型 (昇格) 1/2.0 -> 1.0/2.0 -> 0.5 1.0/2 -> 1.0/2.0 -> 0.5 (自然に[コンパイラが..]行う) 型の昇格 => 狭い方から広い方へ 逆の場合や、自動的に起きない場合に強制したい場合の型変換 => キャスト演算子を使う キャスト演算 表現 : 「(型名)」 例: (char), (int), (double) 意味 : 値の前に先行しておく事により、 その値に対して、(可能なら..)型変換を行い、 その型(と値)を持つ、値にする 例: 1.0 => double 型の 1.0 (int)1.0 => int 型の 1 (int) 1.5 => int 型の 1 (double)1 => double 型の 1.0 1/2 => int 型の 0 ((double)1)/2 => double 型の 0.5 -> 1.0/2 -> 1.0/2.0 -> 0.5 自然な型変換の場合は、 狭い方から広い方なので、情報が失われない 強制した場合は、逆の事がおき、情報落ちが生じる可能性がある 例 : (int)1.5 -> 1 ( 小数点以下の情報が失われる ) # これらの操作は、コンパイル時に行われる # 実行時では、型の情報は存在しない.. # 型を使った操作 ( キャストなど.. ) は、コンパイル時にすべて、やっている 1 bit の情報 => 2 つのもの内の一つを表現 1 byte = 8 bit => 2^8 = 256 通りの区別ができる 4 byte = 4 * 8 bit = 32 bit => 2^32 = 2^2 * 2^30 = 2^2 * (2^10)^3 '=, 4 * (10^3)^3 = 4 * 10^9 byte 数が多い => 区別できる値が増える => (離散型の場合は) 表現できる値の範囲が増える => (連続型の場合は) 表現の精度も高める事ができる 例: 2 bit => 2^2 = 4 通り 範囲 精度 { 0, 1, 2, 3 } 0 〜 3 1 刻み { 0, 0.25, 0.5, 0.75 } 0 〜0.75 0.25 刻み !! 連続型の場合は、同じ bit (byte) 数でも、 !! 実は、範囲を重視するか精度を重視するかによって、 !! 異なる「浮動小数点数」になる可能性がある !! => IEEE の規格で固定されていて、現在は一通り 色々な基本型 離散型(整数型)の色々な基本系 1 = char <= short (int) <= int <= long (int) 基本、型サイズが(広義)単調増加 型サイズが大きくなると、表現範囲が増える printf ( "%d" ) => int 型の出力 printf ( "%d", 'A' ) 'A' (char 型) => 65 (int 型) => %d で出力 long 型を出力する時には、 "%ld" を使う 連続型(浮動小数点数型)の色々な基本系 float <= double 型サイズが大きくなると、表現範囲と精度が良くなる signed (符合付き) と unsigned (符合無し) 整数型での数の表現の差の違い 符合無しの場合は、0 〜 char (符号あり) : -128 〜 127 ( 256 = 2^8 通り ) unsigned char (符号無し) : 0 〜 255 ( 256 = 2^8 通り ) 注 char も実は 0 〜 255 でなく -128 〜 127 # 昔の C コンパイラは char を無条件に unsigned にしているものもあった # 今は(規格上) signed になっている 基本型名の前に unsigned を先行させる事により、符合無しにできる 符合付き 符合無し char unsigned char short int unsigned short int int unsigned int long in unsigned long int unsigned 型を出力する時には、 "%u" を使う unsigned long 型を出力する時には、 "%lu" (sizeof の結果は unsigned long 型) 昇格(型が混在しているときの自動型変換) ともに、離散型あるいは、連続型同士の場合は、型サイズの大きい方へ 離散型と連続型が混在する場合は、連続型にする == 変数宣言 メモリ上の(未使用な..セル)を、その変数のために割り当て、 その(割り当てられたセル)をその変数名で参照(代入)できるようにする機能(を指定する表現) 例: int i; sizeof(int) 個のセル(メモリの一部)が確保され、 以下 i と書くと、このセル(の並び)を指定する事になる !! 変数宣言には、メモリの空いている場所を、 !! (変数として利用するために..)確保する機能を持つ 複数のセル並び ( 変数のようなもの.. ) を、 変数宣言せずに得る事ができれば、 変数名の無い、変数のようなものが実現できる !! 変数宣言 !! 自動的に適切なサイズの変数を確保 !! しかも、名前で参照できる(追加サービス)がある !! 必要なならば & で、ポインタ値もえられる 変数宣言は、コンパイル時の表現 => 実行時点で(個数が..) 変えられない (静的な情報) (単純)変数の個数は、宣言した個数だけ 配列の宣言でも、配列の要素数は、宣言時の固定 # メモリは有限なので、 # 使いっぱなしはこまる # 変数宣言では、不要になったら、自動的に回収される # コンパイラが自動的に行う(ように指示)する # => 後始末の心配はいらない # <= 後始末の自動化を楽にするために、固定長 # # GC : 後始末の複雑な自動化 (固定じゃなくてもよい) # => 動的に変数(のようなもの..)を増やしたい # 実行時で変数の個数や配列の要素を増やしたい 次の二つのライブラリ関数を利用して、動的なメモリ確保が可能 malloc => 空き領域から、指定したサイズをセルを確保する # これを変数として利用す場合は、キャストを行う free => malloc で確保したセルを開放する # 後しまつを自分でする必要がある