Cプログラミング演習
第7回 メモリ内でのデータの配置
1
例題1.棒グラフを描
整数の配列から,その棒グラフを表示
する
ループの入れ子で,棒グラフの表示を
(参考:第6回授業の例題3
棒グラフの1本の棒を画面に表示する
機能を持った関数を補助関数として作
2
#include "stdafx.h"
#include <math.h>
void draw_bar( int len )
{
int i;
for (i=0; i<len; i++) {
printf("*");
}
printf("\n");
return;
}
int _tmain()
{
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;
}
例題:棒グラ
配列の宣言
配列から読み出して,補助関数
draw_bar に渡している
各自でビルド,実行して,
動作を確認して下さい
補助関数
draw_bar
棒グラフの1本の棒を
画面に表示する機能
3
実行結果の例
棒グラフが表示される
4
プログラム実行順
普通,プログラム中の文は,上から下
へ順に実行される
関数呼び出しでは,関数の先頭に
「ジャンプ」する.
関数呼び出しの例) draw_bar(a[i]);
呼び出された関数の中で return に出
と,関数呼び出しの場所に戻る
このことは,C言語が「手続き型言語」と言われる理由の1つ
5
#include "stdafx.h"
#include <math.h>
void draw_bar( int len )
{
int i;
for (i=0; i<len; i++) {
printf("*");
}
printf("\n");
return;
}
int _tmain()
{
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回の繰り返し
関数 draw_bar を呼び出し
関数呼び出しの場所に戻る
6
#include "stdafx.h"
#include <math.h>
void draw_bar( int len )
{
int i;
for (i=0; i<len; i++) {
printf("*");
}
printf("\n");
return;
}
int _tmain()
{
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, ch, a
をメモリエリア中に確保
ここで使用
7
メインの関数内の変数
#include "stdafx.h"
#include <math.h>
void draw_bar( int len )
{
int i;
for (i=0; i<len; i++) {
printf("*");
}
printf("\n");
return;
}
int _tmain()
{
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;
}
この時点では
変数名 タイプ
i7int
ch 未使用 int
a6,4,7,1,5,3,2 int の配列
8
実際のメモリの中身
変数名 タイプ
i7int
ch 未使用 int
a6,4,7,1,5,3,2 int の配列
ich
a
9
#include "stdafx.h"
#include <math.h>
void draw_bar( int len )
{
int i;
for (i=0; i<len; i++) {
printf("*");
}
printf("\n");
return;
}
int _tmain()
{
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, ch, a
がメモリエリア中に確保され
ここで使用
変数 len, i
がメモリエリア中に確保される
ここで使用
10
#include "stdafx.h"
#include <math.h>
void draw_bar( int len )
{
int i;
for (i=0; i<len; i++) {
printf("*");
}
printf("\n");
return;
}
int _tmain()
{
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;
}
補助関数内の変数
この時点では
変数名 タイプ
len 6 など int
i6 など int
len , 6, 4, 7, 1, 5, 3, 2 の値を取り,
i の値は len と等しい
11
変数名 タイプ
len 6 など int
i6 など int
実際のメモリの中身
i
len
ページ10で見たメモリの中身とは,メモリアドレスが違う
個々の関数ごとに,個別のメモリエリアが割り当てられ12
家と住所
名前 住所
福岡市東区
箱崎1丁目1番
Aさんの家
Bさんの家 福岡市東区
箱崎2丁目2番
13
メモリアドレスと
メモリ
変数の中身 メモリアドレス
18
107.75
rate
age
変数名
14
メモリアドレスとは
すべてのデータには「メモリアドレス」
付けられている
変数の中身:
「18」 「107.75」 など
変数名: プログラム内で使ための名前
age, rate など
メモリアドレス: 変数のそれぞれに付けられた「住
所」の
なもの
15
例題2.変数のメモリアドレス
表示
次の3つの変数を使って,「底辺と高さ
ら,3角形の面積を計算するプログラム
を作る.
底辺 teihen 浮動小数データ
高さ takasa 浮動小数デー
面積 menseki 浮動小数データ
これら変数のメモリアドレスの表示も行
16
#include "stdafx.h"
#include <math.h>
int _tmain()
{
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;
}
&」はメモリアドレ
の取得
%p」はメモリアドレス
の表示 17
変数のメモリアドレス表示
実行結果の例
表示された
メモリアドレ*
メモリアドレスの値が
ここでの「例」と違っている
ことはある(動作は正しい)
18
メモリアドレスと変数
メモリ(模式図)
teihen
takasa
menseki 6.000000
4.000000
3.000000
変数名 メモリアドレス
0012FED0
0012FEC0
0012FEB0
19
実際のメモリの中身
変数名 タイプ
menseki 6 double
takasa 4 double
teihen 3double
teihen
takasa
menseki
double 型の変数は
8 バイトになっている
20
メモリアドレスの取得と表示
変数からメモリアドレスの取得
&: メモリアドレスを取得するための演算子
変数名(など)の前に付ける
メモリアドレスの表のための書式
%p: メモリアドレスを表示せよとい指示
printf 文などで使用
printf("address(teihen) = %p\n", &teihen );
メモリアドレス
の取得
メモリアドレス
の表示
21
例題2b.配列のメモリアド
レス
次の2つの配列を使って,ベクトル(1.9, 2.8,
3.7)と,ベクトル(4.6, 5.5, 6.4)の内積を求
めるプログラムを作る.
ベクトル(1.9, 2.8, 3.7u 要素数3の浮動小数
配列
ベクトル(4.6, 5.5, 6.4v 要素数3の浮動小数の
配列
これら配列の要素について,メモリアドレス
の表示も行22
#include "stdafx.h"
#include <math.h>
int _tmain()
{
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;
}
&」はメモリアドレス
の取得
%p」はメモリアドレス
の表示
各自で例題2、例題2bを
ビルド,実行して下さい
{1.9, 2.8, 3.7}」のように書いて,
u[0], u[1], u[2] に値をセット
23
配列のメモリアドレス
実行結果の例
表示された
メモリアドレス
24
メモリアドレス
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
25
配列とメモリアドレス
添字
配列 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
26
実際のメモリの中身
v[0],v[1],v[2]
double 型の変数は
8 バイトになっている
u[0],u[1],u[2]
27
バイナリ形式のファイル
バイナリ形式の
ファイル
プログラムが使う
メモリ空間
メモリの中身がそのまま
ファイルに入る
読み出し: fread を使用
書き込み: fwritre を使用 28
Windows ビットマップファイル
Windows ビットマップファイルの例
786,484 バイトのファイル
(バイナリ形式)
先頭14バイト ファイルヘッダ
先頭40バイト* ヘッダ
残り 画像データの本体
(画像の種類によっては40より長い)
画像データの本体が
先頭から何バイト目か
(bfOffBits) など
(biWidth)
高さ (biHeight)
1画素あたりのビット数
(biBitCount)
圧縮法 (BiCompression)
0 なら圧縮していない など
29
それぞれの格子が画素
30
参考: メモリダンプ用プログ
ラム
31
#include "stdafx.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" );
}
// 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 _tmain()
{char s[] = "89771843";
int ch;
dump( (unsigned char*)s, 16 );
printf( "Enter キーを1,2回押すと,プログラムが終了します\n" );
ch = getchar();
ch = getchar();
return 0;
}32