C/C++の重要な題目であるメモリ管理を ・概要(なぜメモリ管理が必要か?) ・変数の型 ・変数のスコープ ・変数の寿命 ・ポインタ ・構造体、クラス ・アライメント ・動的なメモリの確保 ・placement new ・よくやる間違えと対策 ・メモリリーク検出デバック技術 気づき次第+気力あり次第追記・・・ 概要 †プログラム上での「メモリ」というものが何を意味するのか? たとえば、 ここでいう0x01番地(0x02番地)がアドレスでAさん(Bさん)がメモリの中身です。 また、AさんとBさんが住む場所を入れ替わった時は 住所(アドレス)をみると住んでいる人(メモリ)も当然入れ替わっていることになります <<重要>>:変数はメモリとアドレスがセットで作成され(割り当てられ)ます。 このことはメモリを管理する上でかなり重要です。 変数の型 †変数というものはアドレスとメモリがセットになっているものと説明しました。 int型(整数格納用メモリ) char型(文字格納用メモリ) float型(浮動小数用メモリ) double型(倍精度小数用メモリ) などのほかに ポインタ(アドレス格納用メモリ:ポインタの項目で後述) 構造体(自分で定義したメモリの格納方式:構造体の項目で後述) クラス(自分で定義したメモリの格納方式+関数のセット:クラスの項目で後述) の変数宣言が使えます 変数毎に確保されるメモリの領域(大きさ)が異なり、 たとえば またconstキーワードを変数の型の前につけることでその変数のメモリを初期化時以外に書き換えることを不能にします const int a = 5; // 初期化時のみ代入可能 a = 10; // ←書き換え不能、コンパイルエラー int b = a; // aのメモリのデータでほかのデータを書き換えることはできる constキーワードを変数につけることでその変数は書き換えがない定数として扱うことができます 変数のスコープ †変数のスコープというのはある変数のメモリが処理計算に使える有効範囲です というとわかりにくいと思うので例をあげます #include <stdio.h> int main() { int a = 10; //int型(整数用メモリ格納型)変数aの宣言 { int b = 5; //int型(整数用メモリ格納型)変数bの宣言 }// bのスコープの終わり(このブロックをでるとbは使えない) //a += b; // ←bのメモリは計算に使えない return 0; }// aのスコープの終わり ここでいいたいことは、ブロックの}を抜けたときに #include <stdio.h> int a = 0; // グローバル変数 int main() { int b = a; printf("%d\n",b); int a = 5;// ローカル変数 int c = a; printf("%d\n",c); { int a = 10; // ローカル変数 int d = a; printf("%d\n",d); } int d = a; printf("%d\n",d); return 0; } 局所的な計算で関数を作るまでもない処理の場合、 また関数などで #include <stdio.h> void func(int a) { a += 5; // main関数のaとは別物 } int main() { int a = 10; func(a); printf("%d\n",a); return 0; } とやっても、main関数のaとfunc関数のaのアドレスが違う(関数の引数はメモリの値だけコピーされる) 複数のファイルにまたがって変数の場合 変数の寿命 †変数の寿命というのはある変数のメモリが確保されてからそのメモリが使えなくなるまでの期限です 次のようなキーワードを変数の型の前につけてその変数の寿命を決定します。 auto 自動 static 静的 キーワードつけない場合は自動的にautoキーワードが付いているものとして扱われます ブロック内で宣言されたautoキーワードの変数の場合、ブロックをぬけるまでが変数の寿命となります たとえば先ほどの例では #include <stdio.h> int main() { int a; //autoキーワードが省略されているint型(整数用メモリ格納型)変数a return 0; }// aのスコープの終わり(見えなくなる)かつ変数の寿命(メモリ自体が使えなくなる) ポインタ †構造体、クラス †アライメント †処理系ごとに計算がしやすいという単位が決まっています 対策としては32bitマシンの場合、 #pragma自体も処理系依存ですが #pragma pack(push,アライメント)を使うことでアライメントを指定してやることができます #pragma packはほとんどのCコンパイラで使えるらしい・・・(確認は必要) // アライメント #include <stdio.h> typedef struct s1 { char ch1; int i1; } t1; // アライメントを指定してやる // 1,2,4,16,32(2の倍数単位) #pragma pack(push,1) typedef struct s2 { char ch1; int i1; } t2; #pragma pack(pop) int main(int argc,char *argv[]) { printf("size of t1=%d\n",sizeof(t1)); printf("size of t2=%d\n",sizeof(t2)); return 0; } 動的なメモリの確保 †placement new †通常、動的なメモリを確保するにはmallocやnewを使いますが // placement new #define _CRT_SECURE_NO_DEPRECATE 1 /* VisualC++2005 での警告抑制 */ #include <new> #include <iostream> #include <cstring> char global_area[1000]; // グローバル領域 int main() { // グローバル領域に、char10個分の領域を確保して、そこを指すポインタを得る char* p = new(global_area) char[10]; ::strcpy( p, "aaaaaaaaa" ); // 同じ結果が出力されることを確認する std::cout << p << std::endl; std::cout << global_area << std::endl; return 0; } // プログラム終了時にglobal_areaは解放されるのでメモリリークにはならない よくやる間違えと対策 †メモリリーク検出デバック技術 †投票 †修正・追記の参考したいので |