200 likes | 503 Views
CPU とメモリ. CPU. メモリ. 32bit. 8bit. アドレス. アドレスバス. 演算部. 0000. 整数 レジスタ. 0001. 0002. 0003. データバス. ・ ・ ・. 64bit. 書き込み. 小数レジスタ. 読み出し. データの配置. int (32bit = 4byte). double (64bit = 8byte). メモリ. メモリ. メモリは 8bit=1byte を単位に,格納する情報の大きさに合わせて使用される 読み書きは,各領域をひとまとめにして扱う 左の例:
E N D
CPUとメモリ CPU メモリ 32bit 8bit アドレス アドレスバス 演算部 0000 整数 レジスタ 0001 0002 0003 データバス ・ ・ ・ 64bit 書き込み 小数レジスタ 読み出し
データの配置 int (32bit = 4byte) double (64bit = 8byte) メモリ メモリ • メモリは8bit=1byteを単位に,格納する情報の大きさに合わせて使用される • 読み書きは,各領域をひとまとめにして扱う • 左の例: • int a, b; とした場合a は 0120 - 0123 b は 0124 - 0127 • double r; とした場合r は 0120 - 0127 8bit 8bit アドレス アドレス ・ ・ 0120 0120 0121 0121 変数a 0122 0122 ・ ・ 変数r ・ ・ ・ ・ 変数b
配列型 8bit アドレス ・ • 配列はメモリ中に順に確保される • 左の例: • int a[3]; として配列を定義した場合 確保される領域は全部で 12byte a[x] は 0262 + (x * 4) から始まる 4byte • double d[10] のようにした場合 確保される領域は全部で 80byte d[x] は 0262 + (x * 8) から始まる 8byte 0262 0263 a[0] 0264 ・ ・ ・ a[1] a[2]
コンパイラの動作 8bit アドレス ・ • ・メモリを 12byte 確保 (開始位置:0262) • 「0262」に 10 を入れる • 「0266+0*4」に 20 を入れる • 「0266+0*4」と「0262」の和 を計算し,その結果を 「0266+1*4」に入れる main() { int a, b[2]; a = 10; b[0] = 20; b[1] = b[0] + a;} 0262 0263 a コンパイル 0264 0265 0266 ・ b[0] ・ ここでは,「x」は x から始まる4byteであるとする • コンパイラの動作 • 変数に必要なメモリの総量を計算する • 各変数名に実際のアドレスを対応づける • 数式を,アドレスを用いた手順に変換する b[1]
メモリのアクセス 変数のアクセスに必要な情報 • 読み書きすべき変数は,どのアドレスから始まるか • 変数が格納されている大きさは,いくらか コンパイラの役目・利点 • アドレスを手計算しなくてよい • 変数の大きさの代わりに型(int, double など)を用いるので,間違いにくい.また,大きさの食い違いをチェックしてくれる
ポインタの発想 8bit アドレス ・ main() { int a, b[2]; ・・・} • コンパイラが管理する情報の利用 • 変数 a の開始位置(アドレス)を知りたい • アドレスを用いてメモリ操作したい • など 0262 0263 a 0264 0265 0266 • コンパイラが • 管理する情報 • 変数 a開始:0262大きさ:4操作単位:4 • 変数 b開始:0266大きさ:8操作単位:4 ・ b[0] ・ 開始位置:変数名に対応 大きさ:sizeof(..) で取得可能 操作単位:型に対応 b[1]
ポインタ 8bit アドレス ・ & : 変数名からのアドレス取得&a は 0262 となる&(b[0]) は 0266 となる * : アドレスによるメモリアクセス *(0262) で変数 a へアクセス できるか? →操作単位の情報が必要 main() { int a, b[2]; ・・・} 0262 0263 a 0264 0265 0266 • コンパイラが • 管理する情報 • 変数 a開始:0262大きさ:4操作単位:4 • 変数 b開始:0266大きさ:8操作単位:4 ・ b[0] ・ b[1]
ポインタ変数とポインタの型 ・ • p は int 型の ポインタ変数 • p には int の変数の アドレスを代入する 事が出来る • *p で,p に格納され ているアドレスから,int の大きさで アクセスできる 0262 main() { int a, b, *p; a = 10; /* 1 */ p = &a; /* 2 */ b = *p; /* 3 */ *p = 20; /* 4 */} 0263 a 0264 0265 0266 ・ b ・ a に 10 が入る p に 262 が入る b に a の値(10)が入る a に 20 が入る p
int a;int *p;p = &a; 型と文法 の解釈 p はint変数へのポインタ a はint変数 int *p; p = &a; * はポインタの前に付けると,そのポインタが指す値を意味する.つまり,int 型 & は変数の前に付けると,その型へのポインタを意味する.つまり,int 型へのポインタ 両辺は「int型」 両辺は「int型へのポインタ」 右辺と左辺の釣り合いが取れていなければならない. int 型 int 型へのポインタ a *p &a p
関数とポインタ void change(int *x) { *x = 100; } int main() { int a = 10; change(&a); } a x • a に格納された値ではなく,変数 a のポインタ(アドレス)を関数 change へ伝える事で,変数 a の内容を書き換える事が出来る(関数から値を返す事が出来る)
ポインタと配列 int main() { int a[2] = {10, 20}; int *y = a;y[0] = 100; *(y+1) = 200; *a = 1000; *(a+1) = 2000; } a[0] *a a[1]*(a+1) y[0]*y y[1]*(y+1) • ポインタ変数への加算はアドレスへの加算ではなく,操作単位(型の大きさ)を乗じた値の加算となる • ポインタは配列として,また逆に配列はポインタとして使う事が出来る • a[0] と *a は等価 • a[1]と *(a+1)は等価 y
ポインタの応用例 int spacecnt(char *p) { int cnt = 0; for( ; *p != ‘¥0’; p++) { if(*p == ‘ ‘) { cnt++; } } return cnt; } • 渡された文字列のスペースの個数を数えて返す関数 • p++ で,文字列の次の要素へ処理対象を移動する
Malloc() と free() int *p; p = (int *)malloc(100 * sizeof(int)); *p = 100; p[99] = 200; free(p); • 必要なときに必要なだけメモリを確保する • 大きさの計算に注意(sizeof()を使う事) • 型の変換(キャスト)をきちんとするのが望ましい • 使い終わったら解放(free)する • 解放しなければどんどんメモリが浪費される
構造体 struct foo { int a; double d;};struct foo x; • x は大きさ 12byte • メンバ a は大きさ 4byte で, 開始位置は x の先頭から 0 • メンバ d は大きさ 8byte で, 開始位置は x の先頭から 4 コンパイル x.a • x の型は struct foo型 (fooはタグ名という) • x.a の型は int • x.d の型は double x x.d
構造体の利点 配列に対する利点 • 様々な型をひとまとめに出来る • 丸ごとコピーできるstruct hoge a, b; a = b; 読みやすさ,書きやすさ • 各要素に意味のある名前を付ける事が出来る • 構造体の宣言は,タグ名により再利用できる
構造体の作り方・使い方 struct st1 { int a, b;}struct st2 { struct st1 s; double c[3];}struct st1 x;struct st2 y;x.a =10;y.c[0] = 10.5;y.s = x;y.s.b = 20; 作り方(宣言と定義) • 構造体を入れることが可能 • 配列を入れることも可能 • タグ名を用いていくつでも同じ型の構造体を作る事が可能 使い方 • メンバへアクセスする演算子.は,続けて使う事が出来る
構造体へのポインタ struct st1 { int a, b;}struct st1 x;struct st1 *y;y = &x; (*y).a = 100;y->b = 200; 構造体を指すポインタが使える • 演算子*の優先順位は.や[]より低いことに注意! • 構造体へのポインタを専門に扱う演算子->が用意されているm->n は (*m).n と等価
複雑な変数定義 色を塗ってある部分が実際に確保されるメモリ int a[0] a[1] int int • int *a[3]; int *(a[3]) と同じ「a は要素数 3 の配列で,その配列の各要素はint へのポインタ」 • int (*b)[3];「b はポインタで,そのポインタが指す先は要素数 3 の配列.その配列の各要素は int」 • struct list { int data; struct list *p1, *p2;} x;「x は int 型の領域 data と,自分自身と同じ型の構造体を指すポインタpをメンバに持つ構造体」 a[2] int b int int data data data p1 p1 p1 p2 p2 p2 data data p1 p1 p2 p2