Powered by SmartDoc

ソフトウェア概論B (2010/11/12)
Ver. 1.0

2010年11月12日
栗野 俊一
kurino@math.cst.nihon-u.ac.jp
http://edu-gw2.math.cst.nihon-u.ac.jp/~kurino/2010/soft/soft.html
ソフトウェア概論 B2010年11月12日 の資料

目次

ポインターがなぜ難しい ?

主旨

  1. 理想は、「万が一間違えても、理解して、復帰できる」事だが、それにはある程度経験を必要とする。そこで、ここでは、「できるだけ間違えないためには、どのようなルールを守ればよいか」について説明する。

メモリ

主旨

メインメモリとは

計算機の内部にはメインメモりと呼ばれる装置が収まっている。メインメモリの役割は、数値を記録して置くことである。

メインメモリは沢山の記憶セルからなっており、個々の記憶セルは一般に1 byte ( = 8 bit )分の情報が保持できる( 0〜255 = 2^8 - 1 )あるいは、-128〜127の範囲の数値)。

個々の記憶セルはそれらを区別するために番地が付けられており、異なる番地のセルには異なる情報(数値)を記憶することができる。

セル数は、いわゆる「メインメモリのメモリサイズ」で、例えば、数学科の2008年度入学者に配布されている機種(vostro 1200)には、2 G byteのメモリが塔載されている([vostro 1200 のメインメモリのサイズを確認してみよう])。

vostro 1200 のメインメモリのサイズを確認してみよう

[スタートメニュー]を開き、その中の[コンピュータ]の所で、右クリックしてみよう。すると、メニューが表示されるはずだ。この中の[プロパティ(P)]を左クリックすると[システムとメンテナンス>>システム]のウィンドウが表示される。この中の「メモリ(RAM):」の後に記載されている2038MBが、このNote-PCに塔載されているメインメモリのサイズだ。

メモリの特徴をまとめると次のようになる。

ワード

メモリの最小単位(メモリセルの記憶容量)は、byteであり、これ単独で記憶できるのは、0〜255の数値だけでである。つまり、「単独のメモリセル」では、皆さんの小遣いさえ、記憶できないことになる。

そこで、複数のメモリセルをまとめて扱い、それが一つで、大きな数を表していると考える事にする。こうしてまとめられたセルの事をワード([ワード長と表現できる数の範囲])と呼ぶ事がある。

ワード長と表現できる数の範囲

K君:「ふむ、ふむ、なるほど、自分の10本の指で、足りなければ、友達の手の指を借りればよいわけか..」「一つのセルでは、250位ってことは、僕の月一万円の小遣いを記録するには、10000 / 250で40セルか?結構使うなぁ...、こんなんじゃ、2Gあっても足りなくならないか? 2Gって、20億位か、すると、これの250倍って、えっ?たった5000億?じゃあ、このコンピュータでは国家予算の計算もできないってこと??」

Y先生:「相変わらず、K君は慌てものだなぁ..。実はそんなことはないんだ。0〜255 ( = 256 - 1 )まで表現できるセルが二つあれば、実は、0〜65535 ( = 256 * 256 - 1 )まで表すことができる。つまり、君の御小遣いなら、セル二つで十分だってことだな。それに、我国の借金だって、1,000,000,000,000,000を越えないので、7 byteで十分だ。」

K君:「なるほど、和(256+256)ではなく、積(256*256)になるですね。」

Y先生:「そうそう、n個のセルを継いでできるワードが表現できる数の種類は、2^(8*n) = (2^8)^n = 256^nとなる。」

K君:「すると、友達の指が十の位を、自分の指が一の位を表すとすれば、なんと、0〜99の100個、すなわち、10^2 = 100個の数が表されるってことですね」

Y先生:「ふむ、その通りだ。それが位取法、十進法というわけだ。じゃあ、そこまで判ったんだったら、実は、片手でも32通り、両手なら1024通りまで表すことができることがわかるかな?」

K君:「えっ?こんな所でまで、宿題ですか?参ったなぁ..ええと..なるほど...。でも先生、普通の人は、こんな風に指を曲げることなんかできないですよねぇ..」

Y先生:「ふむ、それが『数学』というものだよ。はっはっはぁ...」

状態遷移計算モデル

計算機が「計算を行う」ということは、どうゆう意味を持つのだろうか?実は、これは「メモリを書き換えること」という観点で意味を付けることができる。

例えば、「最初に、メモリの個々のセルに値が与えられており、プログラムを実行した後にメモリ内のセルの値がどのように変化したか」で、「プログラムを意味」を定める事ができる。

実際には、「計算結果をメモリに書き込んでいる」ので、やっぱり計算を行っているではないかと思われるが、逆に考えれば、「計算をしても、その結果がメモリに書き込まれなければ無意味」であり、また、「メモリに書き込まれた結果さえ同じであれば、その計算方法はどうでもよい」という点を考えると、実は主役が計算側にはなく、メモリ側にあることが解る。

ポインター値とポインター変数

主旨

ポインターと配列の関係

主旨

講義で利用するサンプルプログラム

sample-001

Download : sample-001.c ( SJIS 版 )

sample-001.c
#include <stdio.h>

/*
	文字列を格納する配列
*/

#include <stdio.h>

int main (void)
{
  char arry_of_str[4];	/* 文字列を格納する配列 */ /* (@) str -> array_of_str */
  char *str = arry_of_str; /* (@) array_of_str の別名としての str を追加 */

  str[0] = 'A'; /* 代入 */
  str[1] = 'B'; /* 代入 */
  str[2] = 'C'; /* 代入 */
  str[3] = '\0'; /* 代入 */

  printf ( "文字列 str は %s です。\n", str ); /* 表示 */

  return (0);
}
sample-001.c の実行結果
C:\usr\c\> sample-001
char
&cv = 0xbfe5a5bf
sizeof(char) = 1
&cv + 1 = 0xbfe5a5c0
int
&iv = 0xbfe5a5b8
sizeof(int) = 4
&iv + 1 = 0xbfe5a5bc
double
&dv = 0xbfe5a5b0
sizeof(double) = 8
&dv + 1 = 0xbfe5a5b8
C:\usr\c\> 

sample-002

Download : sample-002.c ( SJIS 版 )

sample-002.c
#include <stdio.h>

int main()
{
  char cv;
  int iv;
  double dv;

  printf ( "char\n" );
  printf ( "&cv = %p\n", &cv );
  printf ( "sizeof(char) = %d\n", sizeof(char) );
  printf ( "&cv + 1 = %p\n", &cv + 1 );

  printf ( "int\n" );
  printf ( "&iv = %p\n", &iv );
  printf ( "sizeof(int) = %d\n", sizeof(int) );
  printf ( "&iv + 1 = %p\n", &iv + 1 );

  printf ( "double\n" );
  printf ( "&dv = %p\n", &dv );
  printf ( "sizeof(double) = %d\n", sizeof(double) );
  printf ( "&dv + 1 = %p\n", &dv + 1 );

  return (0);
}
sample-002.c の実行結果
C:\usr\c\> sample-002
char
&cv = 0xbf8d756f
sizeof(char) = 1
&cv + 1 = 0xbf8d7570
int
&iv = 0xbf8d7568
sizeof(int) = 4
&iv + 1 = 0xbf8d756c
double
&dv = 0xbf8d7560
sizeof(double) = 8
&dv + 1 = 0xbf8d7568
C:\usr\c\> 

sample-003

Download : sample-003.c ( SJIS 版 )

sample-003.c
#include<stdio.h>

main()
{
	int va;

	printf ( "%p\n", &va );

	return 0;
}
sample-003.c の実行結果
C:\usr\c\> sample-003
0xbff7b8ac
C:\usr\c\> 

sample-004

Download : sample-004.c ( SJIS 版 )

sample-004.c
#include<stdio.h>

main()
{
	int va;

	printf ( "%p\n", &va );

	va = 1;
	printf ( "va=%d\n", va );

	va = 100;
	printf ( "va=%d\n", va );

	va = va - 23;
	printf ( "va=%d\n", va );

	printf ( "??=%d\n", *((int *)0x0012FF68) );

	*((int *)0x0012FF68) = 987;

	printf ( "??=%d\n", *((int *)0x0012FF68) );
	printf ( "va=%d\n", va );

	return 0;
}
sample-004.c の実行結果
C:\usr\c\> sample-004
0xbf921ffc
va=1
va=100
va=77
va=77
C:\usr\c\> 

sample-005

Download : sample-005.c ( SJIS 版 )

sample-005.c
#include<stdio.h>

void sub(void)
{
	int va;

	printf ( "va=" );
	scanf ( "%d", &va );
	printf ( "va = %d\n", va );
}

int main()
{
	int va;

	printf ( "%p\n", &va );

	sub();

	printf ( "%d\n", *((int *)0x0012FF5C) );

	return 0;
}
入力例
10
sample-005.c の実行結果
C:\usr\c\> sample-005

sample-006

Download : sample-006.c ( SJIS 版 )

sample-006.c
#include<stdio.h>

int main(void)
{
	int va;

	va = 2;
	printf ( "%d\n", va );

	va = va + 3;
	printf ( "%d\n", va );

	return 0;
}
sample-006.c の実行結果
C:\usr\c\> sample-006
2
5
C:\usr\c\> 

sample-007

Download : sample-007.c ( SJIS 版 )

sample-007.c
#include<stdio.h>

int main()
{
	int va;

	*(&va) = 2;
	printf ( "%d\n", *(&va) );

	*(&va) = *(&va) + 3;
	printf ( "%d\n", *(&va) );

	return 0;
}
sample-007.c の実行結果
C:\usr\c\> sample-007
2
5
C:\usr\c\> 

sample-008

Download : sample-008.c ( SJIS 版 )

sample-008.c
#include <stdio.h>

void sub ( void )
{
	int va;

	printf ( "sub:&va = %p\n", &va );
}

int main ( void )
{
	int va;

	printf ( "main:&va = %p\n", &va );
	sub();

	return 0;
}
sample-008.c の実行結果
C:\usr\c\> sample-008
main:&va = 0xbfb1143c
sub:&va = 0xbfb1140c
C:\usr\c\> 

sample-009

Download : sample-009.c ( SJIS 版 )

sample-009.c
#include <stdio.h>

void sub ( void )
{
	int va;

	printf ( "sub:&va = %p\n", &va );
	va = 100;
	printf ( "sub:va = %d\n", va );

}

int main ( void )
{
	int va;

	printf ( "main:&va = %p\n", &va );

	va = 1;
	printf ( "main:va = %d\n", va );
	sub();
	printf ( "main:va = %d\n", va );
	printf ( "main:?? = %d\n", *((int *)0x0012FF5C) );


	return (0);
}
sample-009.c の実行結果
C:\usr\c\> sample-009

課題

ポインター値とポインター変数

課題20101112-01
「複数の単純変数のポインタ値を表示し、キーボードから、その内の一つのポインタ値を入力させ、その入力されたポインター値が指す変数の値を10倍し、その変化がどこにどのように影響したかを示すプログラムを作りなさい」
課題20101112-02
次のプログラムの実行結果を予想し、その後入力し、実行結果と自分の予想を比較しなさい。
#include<stdio.h>

int main() {
	int va = 10;
	int *ip = &va;
	char *cp = (char *)&va;

	printf ( "ip=%p\n", ip );
	printf ( "ip + 1 = %p\n", ip + 1 );
	printf ( "cp=%p\n", cp );
	printf ( "cp + 1 = %p\n", cp + 1 );

	va = 300;
	printf ( "va = %d\n", va );

	*ip = 0;
	printf ( "va = %d\n", va );

	va = 300;
	printf ( "va = %d\n", va );

	*cp = 0;
	printf ( "va = %d\n", va );
}
  1. 人間が画面に表示されている三つのポインター値の内の一つを選ばない可能性があるが、今回は、その可能性は配慮しなくてよい。つまり、人間は必ず、上記の三つの内の一つを入力すると仮定してよい。

ポインターと配列の関係

課題20101112-03
次の例題に従って、テキストの次の課題の指定した配列をそれぞれポインター変数に書き換えて、実行結果が変らない事を確認しなさい。