Lecture MemoryAllocation final - Keio University...1 プログラミング第3同演習 補足資料...
Transcript of Lecture MemoryAllocation final - Keio University...1 プログラミング第3同演習 補足資料...
1
プログラミング第3同演習
補足資料
メモリ空間
メモリ(記憶領域)は8bit(1byte)毎にアドレス(番地)が割り当てられる
32bitOSの場合,アドレスは
0x00000000~0xffffffffまで
あるアドレスを基準としたと
0xffffffff
(上位)
あるアドレスを基準としたときに,そのアドレスより0x00000000側を「下位アドレス」0xffffffff側を「上位アドレス」と呼ぶ
プログラムはコード,データともに,メモリ上に「ロード」される
0x00000000
0x00000001
0x00000002
8bit
0110 0001 (=‘a’)0110 0010 (=‘b’)
(下位)
プログラムで使用するメモリの種類
コード
実行する命令
:
s = 0;
for ( ; n > 0; n = n - 1 ) {
s = s + n;
}
データ
命令によって扱われるデータ
}
:
int x, y;
char a[10];
char *p;
プログラムのメモリ割り当て(コード)
コンパイラによって,アセンブリ言語に変換され,さらにアセンブラによってマシン語に変換される.
:s = 0;for ( ; n > 0; n = n - 1 ) {
s = s + n;}
:
C言語のプログラム
:
:move $s0, $zero
L: beq $zero, $s1, Exitnopadd $s0, $s0, $s1addi $s1, $s1, -1j L
Exit::
:00 00 10 2110 20 00 0400 00 00 0000 43 10 2108 00 00 0124 21 ff ff
::
アセンブリ言語 マシン語
コンパイラ
% gcc ex8-1.c
※アセンブラはgccによって自動的に呼び出される
プログラムのメモリ割り当て(コード)
マシン語がメモリにロードされる
:s = 0;for ( ; n > 0; n = n - 1 ) {
s = s + n;}
:0010 0000 (0x20)
0000 0000 (0x00)
0000 0100 (0x04)
(上位)
:
:00 00 10 2110 20 00 0400 00 00 0000 43 10 2108 00 00 0124 21 ff ff
:
:
0000 0000 (0x00)0000 0000 (0x00)
1000 0000 (0x10)0010 0001 (0x21)
:
※ビッグエンディアンの場合
0001 0000 (0x10)
( )
(下位)
プログラムのメモリ割り当て(データ)
コンパイラによって,メモリ領域が確保される
(もしくは,メモリ領域を確保するようなアセンブリ言語がプログラム中に自動的に埋め込まれる)
int x, y;
char a[10];
・int型(32bit/4byte)をそれぞれx,yという名前で1個づつ
・char型(8bit/1bye)をaという名前で10個
※32bitOSの場合
2
プログラムのメモリ割り当て(データ)
?
(上位)
:
?
?
?
= a[0]でアクセス可能な1byte(8bit)のメモリ
= a[1]でアクセス可能な1byte(8bit)のメモリ
:
= a[2]でアクセス可能な1byte(8bit)のメモリ
?
?
?
?
:
?
?
?
(下位)
int x, y;
char a[10];
xという名前でアクセス可能な4byte(32bit)のメモリ
yという名前でアクセス可能な4byte(32bit)のメモリ
※32bitOSの場合
※32bitOSの場合
とすることで,コンパイラがメモリ上に割り当てた変数cのアドレスを得ることができる.
アドレスとポインタ
アドレス(&)
?
(上位)
:
?
?
?char c = ’a’;
0xaaaa1231
&c
#include <stdio h>
0xaaaa1232
0xaaaa1233
0xaaaa1234
?
?
?
?
:
?
?
0x61(’a’)
(下位)
cでアクセス可能な1byte(8bit)のメモリ
0xaaaa1230
0xaaaa122f
0xaaaa122e
0xaaaa122d
0xaaaa1231 #include <stdio.h>
main()
{
char c = ’a’;
printf(“%p¥n”,&c);
}
アドレスとポインタ
ポインタ(*)
char c = ’a’;char *p;p = &c;
0xaa
(上位)
:
0x12
0x2f
?
0xaaaa1231
0xaaaa1232
0xaaaa1233
0xaaaa1234
#include <stdio.h>
?
?
?
?
:
?
0xaa
0x61(’a’)
(下位)
0xaaaa122f
0xaaaa122e
0xaaaa122d
0xaaaa1231
cでアクセス可能な1byte(8bit)のメモリ
0xaaaa1230main()
{
char c = ’a’;
char *p = &c;
printf(“%p¥n”,&c);
printf(“%p¥n”,p);
}
pでアクセス可能な4byte(32bit)のメモリ
※ビッグエンディアンの場合※32bitOSの場合
アドレスとポインタ
ポインタ
: アドレスを表す
: アドレスの中身を表す
0xaa
(上位)
:
0x12
0x2f
?
0xaaaa1231
0xaaaa1232
0xaaaa1233
0xaaaa1234
#include <stdio.h>
p
*p
?
?
?
?
:
?
0xaa
0x61(’a’)
(下位)
0xaaaa122f
0xaaaa122e
0xaaaa122d
0xaaaa1231
0xaaaa1230main()
{
char c = ’a’;
char *p = &c;
printf(“%p¥n”,&c);
printf(“%p¥n”,p);
printf(“%c¥n”,*p);
}
※ビッグエンディアンの場合※32bitOSの場合
ポインタと配列
ポインタと配列(char型)
0xaa
0xaa
(上位)
:
0x12
0x29
0xaaaa1231
0xaaaa1230
0xaaaa1232
0xaaaa1233
0xaaaa1234 #include <stdio.h>
main()
{
char c[7] = ”hello!”;
char *p;
0x68(’h’)0x65(’e’)
0x6c(’l’)0x6c(’l’)
:
0x6f(’o’)
0x00(’¥0’)0xaaaa122f
0xaaaa122e
0xaaaa122d
0xaaaa1230
p = &c[0];
printf(”%p¥n”,c);
printf(”%p¥n”,p);
}0xaaaa122c
0xaaaa122b
0xaaaa122a
0x21(’!’)
0xaaaa1229cが表すアドレス
c[0]でアクセス可能な1byte(8bit)のメモリ
※ビッグエンディアンの場合※32bitOSの場合
ポインタと配列
ポインタと配列(char型)
0xaa
0xaa
(上位)
:
0x12
0x29
0xaaaa1231
0xaaaa1230
0xaaaa1232
0xaaaa1233
0xaaaa1234 #include <stdio.h>
main()
{
char c[7] = ”hello!”;
char *p;
0x68(’h’)0x65(’e’)
0x6c(’l’)0x6c(’l’)
:
0x6f(’o’)
0x00(’¥0’)0xaaaa122f
0xaaaa122e
0xaaaa122d
0xaaaa1230
for(p = &c[0]; *p !=’¥0’; p++){
putchar(*p);
}
}0xaaaa122c
0xaaaa122b
0xaaaa122a
0x21(’!’)
0xaaaa1229
※ビッグエンディアンの場合※32bitOSの場合
ループ1回目
3
ポインタと配列
ポインタと配列(char型)
0xaa
0xaa
(上位)
:
0x12
0x2a
0xaaaa1231
0xaaaa1230
0xaaaa1232
0xaaaa1233
0xaaaa1234 #include <stdio.h>
main()
{
char c[7] = ”hello!”;
char *p;
0x68(’h’)0x65(’e’)
0x6c(’l’)0x6c(’l’)
:
0x6f(’o’)
0x00(’¥0’)0xaaaa122f
0xaaaa122e
0xaaaa122d
0xaaaa1230
for(p = &c[0]; *p !=’¥0’; p++){
putchar(*p);
}
}0xaaaa122c
0xaaaa122b
0xaaaa122a
0x21(’!’)
0xaaaa1229
※ビッグエンディアンの場合※32bitOSの場合
ループ2回目
ポインタと配列
ポインタと配列(char型)
0xaa
0xaa
(上位)
:
0x12
0x2b
0xaaaa1231
0xaaaa1230
0xaaaa1232
0xaaaa1233
0xaaaa1234 #include <stdio.h>
main()
{
char c[7] = ”hello!”;
char *p;
0x68(’h’)0x65(’e’)
0x6c(’l’)0x6c(’l’)
:
0x6f(’o’)
0x00(’¥0’)0xaaaa122f
0xaaaa122e
0xaaaa122d
0xaaaa1230
for(p = &c[0]; *p !=’¥0’; p++){
putchar(*p);
}
}0xaaaa122c
0xaaaa122b
0xaaaa122a
0x21(’!’)
0xaaaa1229
※ビッグエンディアンの場合※32bitOSの場合
ループ3回目
ポインタと配列
ポインタと配列(char型)
0xaa
0xaa
(上位)
:
0x12
0x2c
0xaaaa1231
0xaaaa1230
0xaaaa1232
0xaaaa1233
0xaaaa1234 #include <stdio.h>
main()
{
char c[7] = ”hello!”;
char *p;
0x68(’h’)0x65(’e’)
0x6c(’l’)0x6c(’l’)
:
0x6f(’o’)
0x00(’¥0’)0xaaaa122f
0xaaaa122e
0xaaaa122d
0xaaaa1230
for(p = &c[0]; *p !=’¥0’; p++){
putchar(*p);
}
}0xaaaa122c
0xaaaa122b
0xaaaa122a
0x21(’!’)
0xaaaa1229
※ビッグエンディアンの場合※32bitOSの場合
ループ4回目
ポインタと配列
ポインタと配列(char型)
0xaa
0xaa
(上位)
:
0x12
0x2d
0xaaaa1231
0xaaaa1230
0xaaaa1232
0xaaaa1233
0xaaaa1234 #include <stdio.h>
main()
{
char c[7] = ”hello!”;
char *p;
0x68(’h’)0x65(’e’)
0x6c(’l’)0x6c(’l’)
:
0x6f(’o’)
0x00(’¥0’)0xaaaa122f
0xaaaa122e
0xaaaa122d
0xaaaa1230
for(p = &c[0]; *p !=’¥0’; p++){
putchar(*p);
}
}0xaaaa122c
0xaaaa122b
0xaaaa122a
0x21(’!’)
0xaaaa1229
※ビッグエンディアンの場合※32bitOSの場合
ループ5回目
ポインタと配列
ポインタと配列(char型)
0xaa
0xaa
(上位)
:
0x12
0x2e
0xaaaa1231
0xaaaa1230
0xaaaa1232
0xaaaa1233
0xaaaa1234 #include <stdio.h>
main()
{
char c[7] = ”hello!”;
char *p;
0x68(’h’)0x65(’e’)
0x6c(’l’)0x6c(’l’)
:
0x6f(’o’)
0x00(’¥0’)0xaaaa122f
0xaaaa122e
0xaaaa122d
0xaaaa1230
for(p = &c[0]; *p !=’¥0’; p++){
putchar(*p);
}
}0xaaaa122c
0xaaaa122b
0xaaaa122a
0x21(’!’)
0xaaaa1229
※ビッグエンディアンの場合※32bitOSの場合
ループ6回目
ポインタと配列
ポインタと配列(char型)
0xaa
0xaa
(上位)
:
0x12
0x2f
0xaaaa1231
0xaaaa1230
0xaaaa1232
0xaaaa1233
0xaaaa1234 #include <stdio.h>
main()
{
char c[7] = ”hello!”;
char *p;
0x68(’h’)0x65(’e’)
0x6c(’l’)0x6c(’l’)
:
0x6f(’o’)
0x00(’¥0’)0xaaaa122f
0xaaaa122e
0xaaaa122d
0xaaaa1230
※ビッグエンディアンの場合※32bitOSの場合
for(p = &c[0]; *p !=’¥0’; p++){
putchar(*p);
}
}0xaaaa122c
0xaaaa122b
0xaaaa122a
0x21(’!’)
0xaaaa1229
ループ7回目
4
ポインタと配列
ポインタと配列(int型)
0xaa
0xaa
0x02
0x12
0x18
0xaaaa1231
0xaaaa1230
0xaaaa1232
0xaaaa1233
0xaaaa1234
:
#include <stdio.h>
main()
{
int v[10];
int *p;
0x000x00
0x000x01
:
0x00
0x02
0xaaaa1224
0xaaaa1223
0xaaaa1222
※ビッグエンディアンの場合※32bitOSの場合
0xaaaa1221
0xaaaa1220
0xaaaa1219
0x00
0xaaaa1218
v[0] = 1;
v[1] = 2;
:
p = &v[0];
printf(”%d¥n”,*p);
p++;
printf(”%d¥n”,*p);
}
0x000xaaaa1224
コンパイラはポインタの「型」の大きさに応じて,ポインタに対する演算の方法を変更する
ポインタと配列
ポインタと配列(int型)
0xaa
0xaa
0x02
0x12
0x22
0xaaaa1231
0xaaaa1230
0xaaaa1232
0xaaaa1233
0xaaaa1234
:
#include <stdio.h>
main()
{
int v[10];
int *p;
0x000x00
0x000x01
:
0x00
0x02
0xaaaa1224
0xaaaa1223
0xaaaa1222
0xaaaa1221
0xaaaa1220
0xaaaa1219
0x00
0xaaaa1218
v[0] = 1;
v[1] = 2;
:
p = &v[0];
printf(”%d¥n”,*p);
p++;
printf(”%d¥n”,*p);
}
0x000xaaaa1224
※ビッグエンディアンの場合※32bitOSの場合
コンパイラはポインタの「型」の大きさに応じて,ポインタに対する演算の方法を変更する
関数へのポインタ
関数へのポインタ
#include <stdio.h>void func(){
printf(”hoge¥n”);}
main()0000 0100 (0 04)
(上位):
0x00
0x00
0x12
0x30
0xaaaa1231
0xaaaa1232
0xaaaa1233
0xaaaa1234
0xaaaa1230:
: main(){
void (*f)();
f = func;(*f)();printf(”%p¥n”,f);
}
0000 0000 (0x00)0000 0000 (0x00)
1000 0000 (0x10)0010 0001 (0x21)
:
0001 0000 (0x10)
0010 0000 (0x20)
0000 0000 (0x00)
0000 0100 (0x04)
(下位)0xaaaa1230
0xaaaa1231
0xaaaa1232
0xaaaa1233
0xaaaa1234
0xaaaa1235
0xaaaa1236
0xaaaa1237
fでアクセス可能な4byte(32bit)のメモリ
funcが表すアドレス
※ビッグエンディアンの場合※32bitOSの場合 funcの中身のマシン語が格納されているメモリ
関数へのポインタの配列
#include <stdio.h>
void func1(){
:}void func2(){
:}
f[0]f[1]f[2]
}void func3(){
:}
main(){
void (*f[3])();
f[0] = func1; f[1] = func2;f[2] = func3;for(i = 0; i < 3; i++){
(*f[i])();}
}
func1のコード
func2のコード
func3のコード
関数へのポインタの配列
#include <stdio.h>
void func1(){
:}void func2(){
:}
f[0]f[1]f[2]
}void func3(){
:}
main(){
void (*f[3])();
f[0] = func1; f[1] = func2;f[2] = func3;for(i = 0; i < 3; i++){
(*f[i])();}
}
func1のコード
func2のコード
func3のコード
関数へのポインタの配列
#include <stdio.h>
void func1(){
:}void func2(){
:}
f[0]f[1]f[2]
}void func3(){
:}
main(){
void (*f[3])();
f[0] = func1; f[1] = func2;f[2] = func3;for(i = 0; i < 3; i++){
(*f[i])();}
}
func1のコード
func2のコード
func3のコード
5
アドレスとポインタのまとめ
変数は’&’をつけると,そのアドレスを示す.
ポインタはアドレスを格納する特殊な変数.(ポインタの値を格納するためのメモリも割当てられる.)
ポインタは’*’をつけるとそのアドレスの内容を示す.
printf(”%p¥n”,&x);
ポインタは,その型に応じて演算の動作が異なる.(型に合わせた値が加減算される)
int x = 10;int *p = &x;printf(”%d¥n”,*p);
アドレスとポインタのまとめ
配列名や関数名は,その内容が存在するメモリのアドレスを示す.(メモリは割当てられない)
char x[10] = ”Hello!”;char *p = x;putchar(*p);
void func(){……
}
main(){void (*f)();f = func;(*f)();
}
アドレスとポインタのまとめ
配列の名前をポインタに代入 : OK
ポインタを配列の名前に代入 : NG
char x[10] = ”Hello!”;char *p;
p = x; // OK
x = p; // NG
p = &x[0]; // お勧め
メモリ領域の種類
メモリは大きく以下の4つの領域に分けられる
コード領域
コードを割り当てる領域
データ領域
静的に確保されるメモリを
0xffffffff(上位)
ヒープ領域
スタック領域
データ
静的に確保されるメモリを割り当てる領域
ヒープ領域
動的に確保されるメモリを割り当てる領域
スタック領域
一時的なデータを割り当てる領域0x00000000
(下位)
コード領域
ヒ プ領域
データ領域
コード
データ領域
「外部変数」,「static宣言した内部変数」が割当てられる領域
0xffffffff(上位)
ヒープ領域
スタック領域
int x;
int y = 10;
0x00000000(下位)
コード領域
ヒ プ領域
データ領域
y
char str[] = “abcdefg”;
int func(){
static int z;
static long count = 0;
:
}
スタック領域
「内部変数」が割当てられる
0xffffffff(上位)
ヒープ領域
スタック領域
int func(int x, char c){
int y;
float val[100];
0x00000000(下位)
コード領域
ヒ プ領域
データ領域
[ ]
:
return 0;
}
※関数の引数には,多くの場合はCPUのレジスタが用いられるが,引数の数が多くなった場合などはスタックが用いられる※内部変数にもCPUのレジスタが用いられる場合もある※関数の戻り値にはレジスタが用いられる.
6
スタックの動作
上位アドレスから下位アドレスに向って成長するメモリ領域
pushスタックに値を積む
:
?
?
0xfffffffe
0xffffffff
(上位)
?
?0xfffffffd
0xfffffffc
popスタックから値を取り出す
現在のスタックのアドレスはCPUのスタックポインタ(レジスタ)で表わされる.
(下位)
sp:0xfffffffc
CPU
※ビッグエンディアンの場合※32bitOSの場合
スタックの動作(push動作)
int型のデータ1つ(0x01234567とする)をpush
:
?
?
0xfffffffe
0xffffffff
(上位)
?
?0xfffffffd
0xfffffffc
CPU
(下位)
sp:0xfffffffc
CPU
※ビッグエンディアンの場合※32bitOSの場合
スタックの動作(push動作)
int型のデータ1つ(0x01234567とする)をpush
?
?
0xfffffffe
0xffffffff
(上位)
?
?0xfffffffd
0xfffffffc0x67
0x450xfffffffb
CPU0xfffffffa
:
(下位)
0x23
0x01 sp:0xfffffff8
CPU0xfffffffa
0xfffffff9
0xfffffff8
※ビッグエンディアンの場合※32bitOSの場合
スタックの動作(push動作)
int型のデータ(0x89abcdefとする)をもう一つpush
?
?
0xfffffffe
0xffffffff
(上位)
?
?0xfffffffd
0xfffffffc0x67
0x450xfffffffb
CPU0xfffffffa
(下位)
0x23
0x01 sp:0xfffffff4
CPU0xfffffffa
0xfffffff9
0xfffffff8
:
0xef
0xcd
0xab
0x89
0xfffffff7
0xfffffff6
0xfffffff5
0xfffffff4
※ビッグエンディアンの場合※32bitOSの場合
スタックの動作(pop動作)
int型のデータをpop→ 0x89abcdefが取り出される
?
?
0xfffffffe
0xffffffff
(上位)
?
?0xfffffffd
0xfffffffc0x67
0x450xfffffffb
CPU0xfffffffa
(下位)
0x23
0x01 sp:0xfffffff4
CPU0xfffffffa
0xfffffff9
0xfffffff8
0xfffffff7
0xfffffff6
0xfffffff5
0xfffffff4
?
?
?
?取り出された後のメモリの値は不定
:
※ビッグエンディアンの場合※32bitOSの場合
スタックの動作(pop動作)
もう一つint型のデータをpop→ 0x01234567が取り出される
?
?
0xfffffffe
0xffffffff
(上位)
?
?0xfffffffd
0xfffffffc?
?0xfffffffb
CPU0xfffffffa
取り出された後のメモリの値は不定
(下位)
?
? sp:0xfffffff4
CPU0xfffffffa
0xfffffff9
0xfffffff8
:
0xfffffff7
0xfffffff6
0xfffffff5
0xfffffff4
?
?
?
?
※ビッグエンディアンの場合※32bitOSの場合
7
内部変数のメモリ割り当て
もともとスタックポインタが0xbfc14e0cを示していたとする.
main(){
:1
sp:0xbfc14e0c
CPU
?0xbfc14e0a
0xbfc14e0b
(上位)
?
?0xbfc14e09
0xbfc14e08?
0xbfc14e07
?0xbfc14e0c
x = 1;func(x);:
}
void func(int x){
int y;:
y = 0x100;:
}(下位)
?
?
?
0xbfc14e07
0xbfc14e06
0xbfc14e05
0xbfc14e04
:
0xbfc14e03
0xbfc14e02
0xbfc14e01
0xbfc14e00
?
?
?
?
※ビッグエンディアンの場合※32bitOSの場合
内部変数のメモリ割り当て
関数の引数がスタックに積まれる
main(){
:1
0x000xbfc14e0a
0xbfc14e0b
(上位)
0x00
0x000xbfc14e09
0xbfc14e08?
0xbfc14e07 sp:0xbfc14e08
CPU
0x010xbfc14e0c
x = 1func(x);:
}
void func(int x){
int y;:
y = 0x100;:
}(下位)
?
?
?
0xbfc14e07
0xbfc14e06
0xbfc14e05
0xbfc14e04
:
0xbfc14e03
0xbfc14e02
0xbfc14e01
0xbfc14e00
?
?
?
?
※ビッグエンディアンの場合※32bitOSの場合
内部変数のメモリ割り当て
関数からのリターンアドレスがスタックに積まれる(例:0x12233445)
main(){
:1
0x000xbfc14e0a
0xbfc14e0b
(上位)
0x00
0x000xbfc14e09
0xbfc14e080x45
0xbfc14e07 sp:0xbfc14e04
CPU
0x010xbfc14e0c
x = 1func(x);:
}
void func(int x){
int y;:
y = 0x100;:
}(下位)
0x34
0x23
0x12
0xbfc14e07
0xbfc14e06
0xbfc14e05
0xbfc14e04
:
0xbfc14e03
0xbfc14e02
0xbfc14e01
0xbfc14e00
?
?
?
?
※ビッグエンディアンの場合※32bitOSの場合
内部変数のメモリ割り当て
内部変数がスタック上に割当てられる
main(){
:1
0x000xbfc14e0a
0xbfc14e0b
(上位)
0x00
0x000xbfc14e09
0xbfc14e080x45
0xbfc14e07 sp:0xbfc14e00
CPU
0x010xbfc14e0c
x = 1func(x);:
}
void func(int x){
int y;:
y = 0x100;:
}(下位)
0x34
0x23
0x12
0xbfc14e07
0xbfc14e06
0xbfc14e05
0xbfc14e04
:
0xbfc14e03
0xbfc14e02
0xbfc14e01
0xbfc14e00
?
?
?
?
※ビッグエンディアンの場合※32bitOSの場合
内部変数のメモリ割り当て
内部変数を使って演算が行われる.
main(){
:1
0x000xbfc14e0a
0xbfc14e0b
(上位)
0x00
0x000xbfc14e09
0xbfc14e080x45
0xbfc14e07 sp:0xbfc14e00
CPU
0x010xbfc14e0c
x = 1func(x);:
}
void func(int x){
int y;:
y = 0x100;:
}(下位)
0x34
0x23
0x12
0xbfc14e07
0xbfc14e06
0xbfc14e05
0xbfc14e04
:
0xbfc14e03
0xbfc14e02
0xbfc14e01
0xbfc14e00
0x00
0x01
0x00
0x00
※ビッグエンディアンの場合※32bitOSの場合
内部変数のメモリ割り当て
内部変数が破棄され,リターンアドレスに戻る.
main(){
:1
0x000xbfc14e0a
0xbfc14e0b
(上位)
0x00
0x000xbfc14e09
0xbfc14e080x45
0xbfc14e07 sp:0xbfc14e04
CPU
0x010xbfc14e0c
x = 1func(x);:
}
void func(int x){
int y;:
y = 0x100;:
}(下位)
0x34
0x23
0x12
0xbfc14e07
0xbfc14e06
0xbfc14e05
0xbfc14e04
:
0xbfc14e03
0xbfc14e02
0xbfc14e01
0xbfc14e00
?
?
?
?
※ビッグエンディアンの場合※32bitOSの場合
8
内部変数のメモリ割り当て
内部変数が破棄され,リターンアドレスに戻る.
main(){
:1
0x000xbfc14e0a
0xbfc14e0b
(上位)
0x00
0x000xbfc14e09
0xbfc14e08?
0xbfc14e07 sp:0xbfc14e08
CPU
0x010xbfc14e0c
x = 1func(x);:
}
void func(int x){
int y;:
y = 0x100;:
}(下位)
?
?
?
0xbfc14e07
0xbfc14e06
0xbfc14e05
0xbfc14e04
:
0xbfc14e03
0xbfc14e02
0xbfc14e01
0xbfc14e00
?
?
?
?
※ビッグエンディアンの場合※32bitOSの場合
内部変数のメモリ割り当て
スタックに積まれた関数への引数が破棄される.
main(){
:1
?0xbfc14e0a
0xbfc14e0b
(上位)
?
?0xbfc14e09
0xbfc14e08?
0xbfc14e07 sp:0xbfc14e0c
CPU
?0xbfc14e0c
x = 1func(x);:
}
void func(int x){
int y;:
y = 0x100;:
}(下位)
?
?
?
0xbfc14e07
0xbfc14e06
0xbfc14e05
0xbfc14e04
:
0xbfc14e03
0xbfc14e02
0xbfc14e01
0xbfc14e00
?
?
?
?
※ビッグエンディアンの場合※32bitOSの場合
値渡しと参照渡し
値渡しの場合
#include <stdio.h>
int add(int x, int y){
return x + y;}
ヒープ領域
(上位)
スタック領域
main(){
int x,y,z;x = 10;y = 20;
z = add(x,y);}
ヒ プ領域
(下位)
コード領域
データ領域
値渡しと参照渡し
値渡しの場合
#include <stdio.h>
int add(int x, int y){
return x + y;}
ヒープ領域
(上位)
スタック領域
x=?,y=?,z=?
main(){
int x,y,z;x = 10;y = 20;
z = add(x,y);}
ヒ プ領域
(下位)
コード領域
データ領域
値渡しと参照渡し
値渡しの場合
#include <stdio.h>
int add(int x, int y){
return x + y;}
ヒープ領域
(上位)
スタック領域
x=10,y=20,z=?
main(){
int x,y,z;x = 10;y = 20;
z = add(x,y);}
ヒ プ領域
(下位)
コード領域
データ領域
値渡しと参照渡し
値渡しの場合
#include <stdio.h>
int add(int x, int y){
return x + y;}
ヒープ領域
(上位)
スタック領域
x=10,y=20,z=?
10,20
main(){
int x,y,z;x = 10;y = 20;
z = add(x,y);}
ヒ プ領域
(下位)
コード領域
データ領域
引数の値がスタック上にコピーされる
(CPUのレジスタの場合もある)
9
値渡しと参照渡し
値渡しの場合
#include <stdio.h>
int add(int x, int y){
return x + y;}
ヒープ領域
(上位)
スタック領域
x=10,y=20,z=?
10,20
main(){
int x,y,z;x = 10;y = 20;
z = add(x,y);}
ヒ プ領域
(下位)
コード領域
データ領域リターンアドレスがスタックに積まれる
値渡しと参照渡し
値渡しの場合
#include <stdio.h>
int add(int x, int y){
return x + y;}
ヒープ領域
(上位)
スタック領域
x=10,y=20,z=?
10,20
引数を用いて演算が行われる
main(){
int x,y,z;x = 10;y = 20;
z = add(x,y);}
ヒ プ領域
(下位)
コード領域
データ領域
が行われる
値渡しと参照渡し
値渡しの場合
#include <stdio.h>
int add(int x, int y){
return x + y;}
ヒープ領域
(上位)
スタック領域
x=10,y=20,z=30
main(){
int x,y,z;x = 10;y = 20;
z = add(x,y);}
ヒ プ領域
(下位)
コード領域
データ領域
リータンアドレスに戻り,戻り値をzにコピーする
値渡しと参照渡し
参照渡しの場合
#include <stdio.h>
void add(int *x, int *y){
*x = *x + *y;}
ヒープ領域
(上位)
スタック領域
x=?,y=?
main(){
int x,y;x = 10;y = 20;
add(&x,&y);}
ヒ プ領域
(下位)
コード領域
データ領域
値渡しと参照渡し
参照渡しの場合
#include <stdio.h>
void add(int *x, int *y){
*x = *x + *y;}
ヒープ領域
(上位)
スタック領域
x=10,y=20
main(){
int x,y;x = 10;y = 20;
add(&x,&y);}
ヒ プ領域
(下位)
コード領域
データ領域
値渡しと参照渡し
参照渡しの場合
#include <stdio.h>
void add(int *x, int *y){
*x = *x + *y;}
ヒープ領域
(上位)
スタック領域
x=10,y=20
x,yのアドレス
main(){
int x,y;x = 10;y = 20;
add(&x,&y);}
ヒ プ領域
(下位)
コード領域
データ領域
引数の値がスタック上にコピーされるこの場合は,xとyのアドレスになる
10
値渡しと参照渡し
参照渡しの場合
#include <stdio.h>
void add(int *x, int *y){
*x = *x + *y;}
ヒープ領域
(上位)
スタック領域
x=10,y=20
x,yのアドレス
main(){
int x,y;x = 10;y = 20;
add(&x,&y);}
ヒ プ領域
(下位)
コード領域
データ領域リターンアドレスがスタックに積まれる
値渡しと参照渡し
参照渡しの場合
#include <stdio.h>
void add(int *x, int *y){
*x = *x + *y;}
ヒープ領域
(上位)
スタック領域
x=30,y=20
x,yのアドレス
引数を用いて演算が行main(){
int x,y;x = 10;y = 20;
add(&x,&y);}
ヒ プ領域
(下位)
コード領域
データ領域
引数を用いて演算が行われる
同時に,引数として与えられたポインタが指す内容も書き換えられる
値渡しと参照渡し
参照渡しの場合
#include <stdio.h>
void add(int *x, int *y){
*x = *x + *y;}
ヒープ領域
(上位)
スタック領域
x=30,y=20
main(){
int x,y;x = 10;y = 20;
add(&x,&y);}
ヒ プ領域
(下位)
コード領域
データ領域
リータンアドレスに戻る
関数の再帰呼び出しと内部変数/外部変数int num = 0;
main(){
:x = 15; y = 9;gcd = euclid(x, y);:
}
(上位)
ヒープ領域
スタック領域
int euclid(int x, int y){
num++;
if (y == 0) {return x;
} else if ((x % y) == 0){return y;
} else {return euclid(y, x % y);
}}
(下位)
コード領域
ヒ プ領域
データ領域
num = 0
関数の再帰呼び出しと内部変数/外部変数int num = 0;
main(){
:x = 15; y = 9;gcd = euclid(x, y);:
}
(上位)
ヒープ領域
スタック領域
x,y = 15, 9
int euclid(int x, int y){
num++;
if (y == 0) {return x;
} else if ((x % y) == 0){return y;
} else {return euclid(y, x % y);
}}
(下位)
コード領域
ヒ プ領域
データ領域
num = 0
1回目の呼び出し
関数の再帰呼び出しと内部変数/外部変数int num = 0;
main(){
:x = 15; y = 9;gcd = euclid(x, y);:
}
(上位)
ヒープ領域
スタック領域
x,y = 15, 9
int euclid(int x, int y){
num++;
if (y == 0) {return x;
} else if ((x % y) == 0){return y;
} else {return euclid(y, x % y);
}}
(下位)
コード領域
ヒ プ領域
データ領域
num = 1
1回目の呼び出し
11
関数の再帰呼び出しと内部変数/外部変数int num = 0;
main(){
:x = 15; y = 9;gcd = euclid(x, y);:
}
(上位)
ヒープ領域
スタック領域
x,y
y,x%y
= 15, 9= 9, 6
int euclid(int x, int y){
num++;
if (y == 0) {return x;
} else if ((x % y) == 0){return y;
} else {return euclid(y, x % y);
}}
(下位)
コード領域
ヒ プ領域
データ領域
num = 1
2回目の呼び出し
関数の再帰呼び出しと内部変数/外部変数int num = 0;
main(){
:x = 15; y = 9;gcd = euclid(x, y);:
}
(上位)
ヒープ領域
スタック領域
x,y
y,x%y
= 15, 9= 9, 6
int euclid(int x, int y){
num++;
if (y == 0) {return x;
} else if ((x % y) == 0){return y;
} else {return euclid(y, x % y);
}}
(下位)
コード領域
ヒ プ領域
データ領域
num = 2
2回目の呼び出し
ヒープ領域
関数の再帰呼び出しと内部変数/外部変数int num = 0;
main(){
:x = 15; y = 9;gcd = euclid(x, y);:
}
(上位)
スタック領域
x,y
y,x%y
y,x%y
= 15, 9= 9, 6= 6,3
ヒ プ領域
int euclid(int x, int y){
num++;
if (y == 0) {return x;
} else if ((x % y) == 0){return y;
} else {return euclid(y, x % y);
}}
(下位)
コード領域
データ領域
num = 2
3回目の呼び出しヒープ領域
関数の再帰呼び出しと内部変数/外部変数int num = 0;
main(){
:x = 15; y = 9;gcd = euclid(x, y);:
}
(上位)
スタック領域
x,y
y,x%y
y,x%y
= 15, 9= 9, 6= 6,3
ヒ プ領域
int euclid(int x, int y){
num++;
if (y == 0) {return x;
} else if ((x % y) == 0){return y;
} else {return euclid(y, x % y);
}}
(下位)
コード領域
データ領域
num = 3
3回目の呼び出し
ヒープ領域
関数の再帰呼び出しと内部変数/外部変数int num = 0;
main(){
:x = 15; y = 9;gcd = euclid(x, y);:
}
(上位)
スタック領域
x,y
y,x%y
y,x%y
= 15, 9= 9, 6= 6,3
ヒ プ領域
int euclid(int x, int y){
num++;
if (y == 0) {return x;
} else if ((x % y) == 0){return y;
} else {return euclid(y, x % y);
}}
(下位)
コード領域
データ領域
num = 3
3回目の呼び出し
戻り値:3
ヒープ領域
関数の再帰呼び出しと内部変数/外部変数int num = 0;
main(){
:x = 15; y = 9;gcd = euclid(x, y);:
}
(上位)
スタック領域
x,y
y,x%y
= 15, 9= 9, 6
ヒ プ領域
int euclid(int x, int y){
num++;
if (y == 0) {return x;
} else if ((x % y) == 0){return y;
} else {return euclid(y, x % y);
}}
(下位)
コード領域
データ領域
num = 3
2回目の呼び出し
戻り値:3
12
ヒープ領域
関数の再帰呼び出しと内部変数/外部変数int num = 0;
main(){
:x = 15; y = 9;gcd = euclid(x, y);:
}
(上位)
スタック領域
x,y = 15, 9
ヒ プ領域
int euclid(int x, int y){
num++;
if (y == 0) {return x;
} else if ((x % y) == 0){return y;
} else {return euclid(y, x % y);
}}
(下位)
コード領域
データ領域
num = 3
1回目の呼び出し
戻り値:3
ヒープ領域
関数の再帰呼び出しと内部変数/外部変数int num = 0;
main(){
:x = 15; y = 9;gcd = euclid(x, y);:
}
(上位)
スタック領域
戻り値:3
ヒ プ領域
int euclid(int x, int y){
num++;
if (y == 0) {return x;
} else if ((x % y) == 0){return y;
} else {return euclid(y, x % y);
}}
(下位)
コード領域
データ領域
num = 3
内部変数と関数の戻り値
main(){
:char *p = mystr();printf(“%s¥n”,p);
ヒープ領域
スタック領域
(上位)
CPUのスタックポインタ
:}
char *mystr(){
char a[10] = ”abcdefg”;return a;
}
ヒ プ領域
コード領域
データ領域
(下位)
内部変数と関数の戻り値
関数の呼び出し
main(){
:char *p = mystr();printf(“%s¥n”,p);
ヒープ領域
スタック領域
(上位)
リターンアドレスがスタックに積まれる
CPUのスタックポインタ
:}
char *mystr(){
char a[10] = ”abcdefg”;return a;
}
ヒ プ領域
コード領域
データ領域
(下位)
内部変数と関数の戻り値
関数の内部変数がスタック上に確保される
main(){
:char *p = mystr();printf(“%s¥n”,p);
ヒープ領域
スタック領域
(上位)
aという配列が取られ
abcdefg
CPUのスタックポインタ
:}
char *mystr(){
char a[10] = ”abcdefg”;return a;
}
ヒ プ領域
コード領域
データ領域
(下位)
aという配列が取られ,“abcdefg”に初期化される
内部変数と関数の戻り値
関数のリターン
main(){
:char *p = mystr();printf(“%s¥n”,p);
ヒープ領域
スタック領域
(上位)
関数がリターンし,pにaのアドレスが代入される
abcdefg
CPUのスタックポインタ
:}
char *mystr(){
char a[10] = ”abcdefg”;return a;
}
ヒ プ領域
コード領域
データ領域
(下位)
pが指すアドレス
13
内部変数と関数の戻り値
次の関数の呼び出し
main(){
:char *p = mystr();printf(“%s¥n”,p);
ヒープ領域
スタック領域
(上位)
関数の引数とリターンアドレスがスタックに積まれる
CPUのスタックポインタ
:}
char *mystr(){
char a[10] = ”abcdefg”;return a;
}
ヒ プ領域
コード領域
データ領域
(下位)
積まれる
pが指すアドレス
”abcdefg”の内容が上書きされてしまう
内部変数と関数の戻り値
ありがちなミス
// 文字列s1とs2を連結した結果を返す関数char *mystrcat(const char *s1, const char *s2){
char str[100];char *p = &str[0];
for(; *s1 != ’¥0’; s1++){for(; *s1 != ¥0 ; s1++){*p++ = *s1;
}for(; *s2 != ’¥0’; s2++){
*p++ = *s2;}*p = ’¥0’;
return &str[0];}
内部変数と関数の戻り値
対応その1 :呼び出し側で結果を格納する領域を用意しておく
main(){
char res[100];:mystrcat(&res[0], str1, str2);mystrcat(&res[0], str1, str2);:
}
void mystrcat(char *res, const char *s1, const char *s2){
for(; *s1 != ’¥0’; s1++){*res++ = *s1;
}for(; *s2 != ’¥0’; s2++){
*res++ = *s2;}*res = ’¥0’;
}
内部変数と関数の戻り値
対応その2 :static宣言された内部変数もしくは外部変数を使う
char *mystrcat(const char *s1, const char *s2){
static char str[100];char *p = &str[0];
スタック領域
for(; *s1 != ’¥0’; s1++){*p++ = *s1;
}for(; *s2 != ’¥0’; s2++){
*p++ = *s2;}*p = ’¥0’;
return &str[0];}
ヒープ領域
コード領域
データ領域
※但し,スレッドプログラミングなどで問題が発生する可能性がある.
内部変数と関数の戻り値
対応その2 :static宣言された内部変数もしくは外部変数を使う
char str[100];
char *mystrcat(const char *s1, const char *s2){
char *p = &str[0];
スタック領域
char *p = &str[0];
for(; *s1 != ’¥0’; s1++){*p++ = *s1;
}for(; *s2 != ’¥0’; s2++){
*p++ = *s2;}*p = ’¥0’;
return &str[0];}
ヒープ領域
コード領域
データ領域
※但し,スレッドプログラミングなどで問題が発生する可能性がある.
内部変数と関数の戻り値
対応その3 :ヒープにメモリ領域を確保する
char *mystrcat(const char *s1, const char *s2){
char *str, *p;
str = (char *)malloc(strlen(s1) + strlen(s2) + 1);
スタック領域
str (char )malloc(strlen(s1) strlen(s2) 1);
for(p = str; *s1 != ’¥0’; s1++){*p++ = *s1;
}for(; *s2 != ’¥0’; s2++){
*p++ = *s2;}*p = ’¥0’;
return str;}
ヒープ領域
コード領域
データ領域
14
ヒープ領域
動的にメモリが確保/破棄される領域
メモリの確保
0xffffffff(上位)
ヒープ領域
スタック領域
void *malloc(size_t size);
void *calloc(size_t nmemb, size_t size);
メモリの破棄
0x00000000(下位)
コード領域
データ領域
void free(void *ptr);
それぞれ,プロトタイプ宣言が”stdlib.h”に記述されているので,使用する時にはstdlib.hをインクルードする
ヒープ領域のメモリの確保
mallocの使い方0xffffffff
(上位)
ヒープ領域
スタック領域
#include <stdlib.h>
main(){
char *p;
void *malloc(size_t size);
0x00000000(下位)
コード領域
データ領域
int *q;:p = (char*)malloc(10);q = (int*)malloc(sizeof(int)*20);:free(p);free(q);
}
mallocが10byteの領域を空いているヒープ領域から確保してくれる
※ビッグエンディアンの場合※32bitOSの場合
ヒープ領域のメモリの確保
mallocの使い方0xffffffff
(上位)
ヒープ領域
スタック領域
#include <stdlib.h>
main(){
char *p;
mallocが4x20=80byteの領域を空いているヒープ領域から確保してくれる
void *malloc(size_t size);
0x00000000(下位)
コード領域
データ領域
int *q;:p = (char*)malloc(10);q = (int*)malloc(sizeof(int)*20);:free(p);free(q);
}
pが指すアドレス
※ビッグエンディアンの場合※32bitOSの場合
ヒープ領域のメモリの解放
freeの使い方0xffffffff
(上位)
ヒープ領域
スタック領域
#include <stdlib.h>
main(){
char *p;qが指すアドレス
void free(void *ptr);
0x00000000(下位)
コード領域
データ領域
int *q;:p = (char*)malloc(10);q = (int*)malloc(sizeof(int)*20);:free(p);free(q);
}
pが指すアドレス
※ビッグエンディアンの場合※32bitOSの場合
freeの引数になるアドレスは,malloc関数が返したものでなくてはいけない
ヒープ領域のメモリの解放
freeの使い方0xffffffff
(上位)
ヒープ領域
スタック領域
#include <stdlib.h>
main(){
char *p;qが指すアドレス
void free(void *ptr);
0x00000000(下位)
コード領域
データ領域
int *q;:p = (char*)malloc(10);q = (int*)malloc(sizeof(int)*20);:free(p);free(q);
}
※ビッグエンディアンの場合※32bitOSの場合
freeの引数になるアドレスは,malloc関数が返したものでなくてはいけない
ヒープ領域のメモリの解放
freeの使い方0xffffffff
(上位)
ヒープ領域
スタック領域
#include <stdlib.h>
main(){
char *p;
void free(void *ptr);
0x00000000(下位)
コード領域
データ領域
int *q;:p = (char*)malloc(10);q = (int*)malloc(sizeof(int)*20);:free(p);free(q);
}
※ビッグエンディアンの場合※32bitOSの場合
freeの引数になるアドレスは,malloc関数が返したものでなくてはいけない
15
ヒープを利用した例
main(){
char *p;:p = mystrcat(“test”,”hogehoge”);:free(p);
}
char *mystrcat(const char *s1 const char *s2)char *mystrcat(const char *s1, const char *s2){
char *str, *p;
str = (char *)malloc(strlen(s1) + strlen(s2) + 1);
for(p = str; *s1 != ’¥0’; s1++){*p++ = *s1;
}for(; *s2 != ’¥0’; s2++){
*p++ = *s2;}*p = ’¥0’;
return str;}
ヒープ領域メモリの割り当て/解放の注意
割り当てたメモリは必ず解放する必要がある
解放せず,確保し続けると「メモリリーク」が発生する
(なるべくmallocとfreeはセットで使用する.)
解放する時にfree関数に与えるアドレスは,必ずmalloc関数が返したものでなければならない
以下のようなコードはNGchar *p = (char*)malloc(10);
:for(; p != ’¥0’; p++){
*p = *str++;}free(p);
:
mallocとcallocの違い
引数には確保する「総バイト数」を指定する
ヒープ領域から「総バイト数分」のメモリを確保し,その先頭のアドレスを返す
void *malloc(size_t size);
id ll ( i t b i t i )
引数には,要素の個数(nmemb)と1つの要素のサイズ(size)を指定する
ヒープ領域から「(nmemb×size)バイト」のメモリを確保し,その先頭のアドレスを返す
確保されたメモリ領域は,「0」に初期化される
void *calloc(size_t nmemb, size_t size);
メモリ領域の種類のまとめ
メモリ領域には,大きく分けて以下の4つがあるコード : プログラムコードが配置される
データ : 外部変数,static宣言された内部変数が配置される
スタック : 内部変数が配置される
ヒープ : 動的に確保/破棄されるメモリ領域
関数の戻り値に内部変数へのポインタを用いてはいけない
ヒープ領域からメモリを確保/破棄する関数
malloc/callocとfreeはセットで使わなければならない
void *malloc(size_t size);
void *calloc(size_t nmemb, size_t size);
void free(void *ptr);
補足:バッファオーバーフローの手口
ヒープ領域
(上位)
スタック領域#include <stdlib.h>
main(){
char str[10];:gets(&str[0]);:
}ヒ プ領域
(下位)
コード領域
データ領域
}
は標準入力からEOFもしくは’¥0’が入力されるまでの文字列sで示される場所に格納する関数
char *gets(char *s);
補足:バッファオーバーフローの手口
ヒープ領域
(上位)
スタック領域#include <stdlib.h>
main(){
char str[10];:gets(&str[0]);:
}
main関数のリターンアドレス
ヒ プ領域
(下位)
コード領域
データ領域
}
は標準入力からEOFもしくは’¥0’が入力されるまでの文字列sで示される場所に格納する関数
char *gets(char *s);
16
補足:バッファオーバーフローの手口
ヒープ領域
(上位)
スタック領域#include <stdlib.h>
main(){
char str[10];:gets(&str[0]);:
}ヒ プ領域
(下位)
コード領域
データ領域
}
は標準入力からEOFもしくは’¥0’が入力されるまでの文字列sで示される場所に格納する関数
char *gets(char *s);
補足:バッファオーバーフローの手口
ヒープ領域
(上位)
スタック領域#include <stdlib.h>
main(){
char str[10];:gets(&str[0]);:
}ヒ プ領域
(下位)
コード領域
データ領域
}
は標準入力からEOFもしくは’¥0’が入力されるまでの文字列sで示される場所に格納する関数
char *gets(char *s);
10文字以上入力されると,main関数のリターンアドレスが上書きされる.リターンアドレスをスタック内に設定し,スタック内に悪意のあるコードを置くことで,main関数終了とともに,そのコードを実行させることが可能.