ce-7. メモリ内でのデータ配置
1
金子邦彦
C プログラミング応用)(全14回)
URL: https://www.kkaneko.jp/pro/c/index.html
C言語における変数・配列のメモリ配置の仕組み、メモリア
ドレスの取得・表示方法の理解。
学習内容の構成
1. 関数とメモリエリア:関数ごとに独立したメモリエリア
が割り当てられる仕組み
2. メモリアドレス:変数に付けられた「住所」としてのア
ドレス概念
3. アドレス操作:「&」演算子によるアドレス取得と
%p」による表示
4. 配列のメモリ配置:配列要素が連続したメモリ領域に順
番に格納される構造
5. バイナリ形式ファイル:メモリ内容をそのままファイル
に読み書きする方法
前提:C言語の基本文法、配列、関数の知識
意義:ポインタ理解への基盤構築、低レベルなデータ操作
の理解
2
例題1.棒グラフを描く
整数の配列から,その棒グラフを表示する
ループの入れ子で,棒グラフの表示を行う(参考:第
6回授業の例題3)
棒グラフの1本の棒を画面に表示する機能を持っ
た関数を補助関数として作る
3
#include "stdio.h"
#include <math.h>
void draw_bar( int len )
{
int i;
for (i=0; i<len; i++) {
printf("*");
}
printf("¥n");
return;
}
int main()
{
int i;
int ch;
int a[]={6,4,7,1,5,3,2};
for (i=0; i<7; i++) {
draw_bar(a[i]);
}
ch = getchar();
ch = getchar();
return 0;
}
例題:棒グラフ
4
配列の宣言
配列から読み出して,補助関
draw_bar に渡している
各自でビルド,実行して,
動作を確認して下さい
補助関数
draw_bar
棒グラフの1本の棒を
画面に表示する機能
実行結果の例
5
棒グラフが表示される
プログラム実行順
普通,プログラム中の文は,上から下へ順に実行
される
関数呼び出しでは,関数の先頭に「ジャンプ」す
る.
関数呼び出しの例) draw_bar(a[i]);
呼び出された関数の中で return 文に出会うと,関
数呼び出しの場所に戻る.
6
このことは,C言語が「手続き型言語」と言われる理由の1つ
#include "stdio.h"
#include <math.h>
void draw_bar( int len )
{
int i;
for (i=0; i<len; i++) {
printf("*");
}
printf("¥n");
return;
}
int main()
{
int i;
int ch;
int a[]={6,4,7,1,5,3,2};
for (i=0; i<7; i++) {
draw_bar(a[i]);
}
ch = getchar();
ch = getchar();
return 0;
}
プログラム実行順
7
補助関数
メインの関数
プログラム実行は
メインの関数から
始まる
複数の関数を含む
プログラム
7回の繰り返し
関数 draw_bar を呼び出し
関数呼び出しの場所に戻る
#include "stdio.h"
#include <math.h>
void draw_bar( int len )
{
int i;
for (i=0; i<len; i++) {
printf("*");
}
printf("¥n");
return;
}
int main()
{
int i;
int ch;
int a[]={6,4,7,1,5,3,2};
for (i=0; i<7; i++) {
draw_bar(a[i]);
}
ch = getchar();
ch = getchar();
return 0;
}
メインの関数内の変数
8
変数 i, ch, a
をメモリエリア中に確保
ここで使用
メインの関数内の変数
9
#include "stdio.h"
#include <math.h>
void draw_bar( int len )
{
int i;
for (i=0; i<len; i++) {
printf("*");
}
printf("¥n");
return;
}
int main()
{
int i;
int ch;
int a[]={6,4,7,1,5,3,2};
for (i=0; i<7; i++) {
draw_bar(a[i]);
}
ch = getchar();
ch = getchar();
return 0;
}
この時点では
変数名
タイプ
i 7 int
ch
未使用
int
a
6,4,7,1,5,3,
2
int の配列
実際のメモリの中身
変数名
タイプ
i 7 int
ch
未使用
int
a
6,4,7,1,5,3,
2
int の配列
i
ch
a
10
#include "stdio.h"
#include <math.h>
void draw_bar( int len )
{
int i;
for (i=0; i<len; i++) {
printf("*");
}
printf("¥n");
return;
}
int main()
{
int i;
int ch;
int a[]={6,4,7,1,5,3,2};
for (i=0; i<7; i++) {
draw_bar(a[i]);
}
ch = getchar();
ch = getchar();
return 0;
}
補助関数内の変数
11
変数 i, ch, a
がメモリエリア中に確保される
ここで使用
変数 len, i
がメモリエリア中に確保される
ここで使用
#include "stdio.h"
#include <math.h>
void draw_bar( int len )
{
int i;
for (i=0; i<len; i++) {
printf("*");
}
printf("¥n");
return;
}
int main()
{
int i;
int ch;
int a[]={6,4,7,1,5,3,2};
for (i=0; i<7; i++) {
draw_bar(a[i]);
}
ch = getchar();
ch = getchar();
return 0;
}
補助関数内の変数
12
この時点では
変数名
タイプ
len
6
int
i
6
int
len , 6, 4, 7, 1, 5, 3, 2 の値を取り,
i の値は len と等しい
変数名
タイプ
len
6
int
i
6
int
i
len
ページ10で見たメモリの中身とは,メモリアドレスが違う
個々の関数ごとに,個別のメモリエリアが割り当てられる
実際のメモリの中身
13
家と住所
14
名前 住所
福岡市東区
箱崎1丁目1番
Aさんの家
Bさんの家
福岡市東区
箱崎2丁目2番
メモリアドレスとは
15
メモリ
変数の中身
メモリアドレス
18
107.75
rate
age
変数名
メモリアドレスとは
すべてのデータには「メモリアドレス」が付けら
れている
変数の中身:
「18」 「107.75」 など
変数名: プログラム内で使うための名前
age, rate など
メモリアドレス:
変数のそれぞれに付けられた「住所」の
ようなもの
16
例題2.変数のメモリアドレス表示
次の3つの変数を使って,「底辺と高さから,3
角形の面積を計算するプログラム」を作る.
底辺 teihen 浮動小数データ
高さ takasa 浮動小数データ
面積 menseki 浮動小数データ
これら変数のメモリアドレスの表示も行う
17
#include "stdio.h"
#include <math.h>
int main()
{
double teihen;
double takasa;
double menseki;
int ch;
teihen = 3;
takasa = 4;
menseki = teihen * takasa * 0.5;
printf("menseki = %f¥n",menseki);
printf("address(teihen) = %p¥n", &teihen );
printf("address(takasa) = %p¥n", &takasa );
printf("address(menseki) = %p¥n", &menseki );
ch = getchar();
ch = getchar();
return 0;
}
18
&」はメモリアドレス
の取得
%p」はメモリアドレス
の表示
変数のメモリアドレス表示
19
実行結果の例
表示された
メモリアドレス*
メモリアドレスの値が
ここでの「例」と違っている
ことはある(動作は正しい)
メモリアドレスと変数
20
メモリ(模式図)
teihen
takasa
menseki
6.000000
4.000000
3.000000
変数名
メモリアドレス
0012FED0
0012FEC0
0012FEB0
実際のメモリの中身
変数名
タイプ
menseki 6 double
takasa 4 double
teihen
3
double
teihen
takasa
menseki
double 型の変数は
8 バイトになっている
21
メモリアドレスの取得と表示
変数からメモリアドレスの取得
&: メモリアドレスを取得するための演算子
変数名(など)の前に付ける
メモリアドレスの表示のための書式
%p: メモリアドレスを表示せよという指示
printf 文などで使用
22
printf("address(teihen) = %p¥n", &teihen );
メモリアドレス
の取得
メモリアドレス
の表示
例題2b.配列のメモリアドレ
次の2つの配列を使って,ベクトル(1.9, 2.8,
3.7)と,ベクトル(4.6, 5.5, 6.4)の内積を求め
るプログラムを作る.
ベクトル(1.9, 2.8, 3.7 u 要素数3の浮動小数の
配列
ベクトル(4.6, 5.5, 6.4 v 要素数3の浮動小数の
配列
これら配列の要素について,メモリアドレスの表
示も行う
23
#include "stdio.h"
#include <math.h>
int main()
{
double u[]={1.9, 2.8, 3.7};
double v[]={4.6, 5.5, 6.4};
int i;
double ip;
int ch;
ip = 0;
for (i=0; i<3; i++) {
ip = ip + u[i]*v[i];
}
printf("内積=%f¥n", ip);
printf("address(u[0]) = %p¥n", &u[0]);
printf("address(u[1]) = %p¥n", &u[1]);
printf("address(u[2]) = %p¥n", &u[2]);
printf("address(v[0]) = %p¥n", &v[0]);
printf("address(v[1]) = %p¥n", &v[1]);
printf("address(v[2]) = %p¥n", &v[2]);
ch = getchar();
ch = getchar();
return 0;
}
24
&」はメモリアドレス
の取得
%p」はメモリアドレス
の表示
各自でビルド,
実行して下さい
{1.9, 2.8, 3.7}」のように書いて,
u[0], u[1], u[2] に値をセット
配列のメモリアドレス
25
実行結果の例
表示された
メモリアドレス
メモリアドレス
26
v[0]
v[1]
v[2]
u[0]
u[1]
u[2]
メモリ(模式図)
メモリアドレス
0012FEC0
4.6
5.5
6.4
1.9
2.8
3.7
0012FEC8
0012FED0
0012F0A0
0012FEA8
0012FEB0
配列とメモリアドレス
27
添字
配列 u
(サイズは3)
添字
配列 v
(サイズは3)
2つの配列
メモリ内の配置
(配列の並びはそのままで
メモリに入る)
v[0]
v[1]
v[2]
u[0]
u[1]
u[2]
メモリアドレス
4.6
5.5
6.4
1.9
2.8
3.7
0012FEC0
0012FEC8
0012FED0
0012F0A0
0012FEA8
0012FEB0
実際のメモリの中身
v[0],v[1],v[2]
double 型の変数は
8 バイトになっている
u[0],u[1],u[2]
28
バイナリ形式のファイル
29
バイナリ形式の
ファイル
プログラムが使う
メモリ空間
メモリの中身がそのまま
ファイルに入る
読み出し: fread を使用
書き込み: fwritre を使用
Windows ビットマップファイル
30
Windows ビットマップファイルの例
786,484 バイトのファイル
(バイナリ形式)
先頭14バイト ファイルヘッダ
先頭40バイト* ヘッダ
残り 画像データの本体
(画像の種類によっては40より長い)
画像データの本体が
先頭から何バイト目か
(bfOffBits) など
(biWidth)
高さ (biHeight)
1画素あたりのビット数
(biBitCount)
圧縮法 (BiCompression)
0 なら圧縮していない など
それぞれの格子が画素
31
参考: メモリダンプ用プロ
グラム
32
#include "stdio.h"
void dump_line( unsigned char *top_address )
{
unsigned char *p;
// %p」はポインタの表示
printf( "%p :", top_address );
// 以下,16進数での表示を16バイト分行う
for ( p = top_address; p < (top_address + 16); p++ ) {
// %2x」は16進数2桁での表示
printf( " %2x", *p );
}
// メモリの中身が,16進数で 20 以上 7e 以下のときは,「文字」も表示
printf( "|" );
for ( p = top_address; p < (top_address + 16); p++ ) {
// %c」は文字(1文字)での表示
if ( ( (*p) >= 0x20 ) && ( (*p) < 0x7e ) ) {
printf( "%c", *p );
}
else {
printf( "." );
}
}
printf( "¥n" );
}
33
// 16進数でメモリの中身を表
void dump( unsigned char *address, int len )
{
unsigned char *current;
printf( "16 進数でメモリの中身を表示¥n" );
printf( " : 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef¥n" );
printf( "---------:------------------------------------------------|----------------¥n" );
current = (unsigned char*) ( ( ( (int) address ) / 16 ) * 16 );
while ( current < (address + len) ) {
dump_line( current );
current = current + 16;
}
return;
}
int main()
{
char s[] = "89771843";
int ch;
dump( (unsigned char*)s, 16 );
printf( "Enter キーを1,2回押すと,プログラムが終了します¥n" );
ch = getchar();
ch = getchar();
return 0;
}
34