基礎演習3 C言語の基礎(5) メモリとポインタの概念 ビットとバイト メモリと

基礎演習3 C言語の基礎(5)
メモリとポインタの概念
第05回(2010年07月07日)
ビットとバイト


メモリとアドレス
計算機内部では、データは2進数で保存している。
計算機は、メモリにデータを蓄えている。
 bit
1bit
0 もしくは 1 のどちらかを保存
 byte
1bitが8つ集まっている。
1byte


byte が、メモリの基本単位として使用される。
メモリの概念図
メモリは、1byteを保存で
きる場所が大量にある。
0000
0001
0002
それぞれの場所には、
0003
アドレスがふられている。
0004
0005
0006
アドレスがふられている
C言語の変数とメモリ

アドレスの参照とは
C言語で用いる変数等も、実際の値はメモリ上にある。
 メモリ上のどこに置くかとかは、OSやコンパイラが
適時やってくれる。
0000
0001
0002
0003
0004
これは此処
に入れるか
変数T
計算機
変数Tを
使用と・・・
人

C言語では、実際にどこにデータが保存されているの
か、プログラム中で参照できる。
0000
0001
0002
0003
0004
変数Tの
アドレスは?
計算機
変数T
0002です
人
アドレスの参照の方法
アドレスにある値の参照
変数の前に & をつけると、その値をどこに保存して
いるか(アドレス)を参照できる

例: #include
#include <stdio.h>
<stdio.h>
その場所に入っている値は何?
main(){
main(){
int
int i;
i;
}}
『どこに保存されているか』はわかる。
 次にやりたいことは?
アドレスの前に * をつけると、そのアドレスに保存
している値を参照できる
pirntf("%u¥n",&i);
pirntf("%u¥n",&i);

更に、C言語では、アドレスを保存する型も存在する。
&i とすることで、i が保存されているアドレスを参照できる。
ポインタとアドレス




ポインタの宣言(1)
ポインタ → アドレスを保存している変数です。
このポインタを型として宣言することができます。
整数型や文字型と合わせて宣言されます。
『変数 poi はポインタである』と宣言するのではない!。
『変数 poi は整数型の値を保存する場所のポインタ』
『変数 poi は文字型の値を保存する場所のポインタ』
のように、どの型を保存しているアドレスを保存している
のかを区別。
ポインタの宣言(2)
#include
#include <stdio.h>
<stdio.h>
main(){
main(){
int
int i;
i;
int
int *poi_01;
*poi_01;
int
int arr[10];
arr[10];
}}
整数型の変数 *poi_01 を宣言
poi_01はアドレス
サイズ10の整数型の配列
arrは配列の最初の要素のアドレス
&i
値を参照するには
i
int *poi_01; poi_01(または &*poi_01)
*pot_01
int arr[10];
arr[0]
arr
ポインタの宣言の仕方
例: #include
#include <stdio.h>
<stdio.h>
*poi_01は整数型である。
main(){
main(){
int
int i;
i;
int
int *poi_01;
*poi_01;
int
int arr[10];
arr[10];
}}
poi_01の部分が
整数型のデータを保存する
アドレスを保存するポインタ
となる。
宣言するときに * をつけると、
* の次の文字は、ポインタになる。
サンプルプログラム
整数型の変数 i を宣言
アドレスを参照するには
int i;
ポインタ
#include
#include <stdio.h>
<stdio.h>
int
int main(){
main(){
unsigned
unsigned int
int i=101;
i=101;
unsigned
unsigned int
int *poi_01;
*poi_01;
unsigned
unsigned int
int arr[10]={103,2,3,4,5,6,7,8,9,10};
arr[10]={103,2,3,4,5,6,7,8,9,10};
*poi_01=104;
*poi_01=104;
printf("000
printf("000 %u
%u --- %u
%u --- %u
%u ¥n",i,
¥n",i, &i,
&i, *&i);
*&i);
printf("001
printf("001 %u
%u --- %u
%u ¥n",*poi_01,
¥n",*poi_01, poi_01);
poi_01);
printf("%u
printf("%u --- %u
%u --- %u
%u ¥n",arr,
¥n",arr, arr[0],
arr[0], &arr[0]);
&arr[0]);
}}
復習:
C言語と関数

関数の自作の基礎
C言語の「関数」とは、入力を与えると出力を返すよう
な"もの"です。
入力



関数
出力
個々の関数について、「何を何個入力として与えるか」
「何を出力するか」は、決められています。
C言語は、基本的に関数からなりたちます。
関数は、自作することもできます。
13/59
関数の自作とは

同様の処理をプログラムの彼方此方で使う場合、同
じソースコードを何回も書くのは面倒!
関数として準備しておこう!


C言語では、自分で関数を自作することができる。
自作するためには、以下が必要。
 関数の入出力をプロトタイプ宣言として書く
 関数の実際の動作を書く
関数を自作するには
#include<stdio.h>
#include<stdio.h>
#include<pow.h>
#include<pow.h>
main(){
main(){
文
文
x=pow(10);
x=pow(10);
y=sqrt(10);
y=sqrt(10);
}}
関数が並んでいる
ヘッダファイルをinclude。
どんな入出力の
関数を使うのかを指定
ソースファイル中に
プロトタイプ宣言として記述
mainは必ずある
実際の関数の動作は、
ライブラリにバイナリとして存在
内容はソースファイル中に記述する
復習:
典型的なソースファイルの構成
#include<stdio.h>
#include<stdio.h>
#include<pow.h>
#include<pow.h>
main(){
main(){
文
文
x=pow(10);
x=pow(10);
y=sqrt(10);
y=sqrt(10);
}}
関数が並んでいる
ヘッダファイルをinclude。
どんな入出力の
関数を使うのかを指定
mainは必ずある
既存の関数の実際の動作は、
ライブラリにバイナリとして存在
関数制作の計画
1. 関数の設計をする。
 入力は何か?
 出力は何か?
 処理内容は何か?
2. 関数の入出力をプロトタイプ宣言として書く
3. 関数の実際の動作を書く
入力
関数
出力
典型的なファイルの構成
#include<stdio.h>
#include<stdio.h>
プロトタイプ宣言の記述
どんな入出力か、
プロトタイプ宣言を記述
int
int sample01(int
sample01(int x,int
x,int y);
y);
入力
int
int main(){
main(){
int
int value;
value;
}}
戻り値の型を指定
value=sample01(10,12);
value=sample01(10,12);
printf("%d¥n",value);
printf("%d¥n",value);
既存の関数と同様に使用可能
int
int sample01(int
sample01(int x,int
x,int y){
y){
x=x*y*10;
x=x*y*10;
return
x;
return x;
}}
関数名
関数の内容を記述する
19/59
 その関数の入出力を指定する。
 引数の型を宣言するときは、変数毎に型を宣言。
double x,y,z のように、まとめて宣言することはできない。
20/59
 戻り値は1つだけなので、型だけを指定する。
戻り値について
引数と引数の型を指定
int
int sample01(int
sample01(int max
max ,int
,int st){
st){
max=max+st+2;
max=max+st+2;
return
return max;
max;
}}

文
戻り値を指定
構造 戻り値の型を指定
戻り値の型を指定 関数名(引数と引数の型を指定){
関数名(引数と引数の型を指定){
文
文
return
return 戻り値;
戻り値;
}}

return 文の次にある値を返り値として返す。


21/59
関数の戻り値の型として指定できるもの。
 今まで変数として宣言してきた型
 返り値を返さない場合は、void を指定する。
関数は、return 文の次にある値を返り値として返す。
 返り値が無い場合は、単にreturn;とすればよい。
 returnが無い場合は、関数の末尾まで実行したら、
戻り値を返さずに、関数が終了する。
mainも関数なので、返り値がある。
 型を指定しなかった場合は、intを返す関数と解
釈される。
22/59
変数と引数について


関数内で使用する変数名と
引数の型を指定
関数名を指定
int
int sample01(int
sample01(int max
max ,int
,int st);
st);
実際の動作を書く部分
戻り値の型を指定
出力
関数
値渡しのイメージ図
変数は、それぞれの関数が持っている。
同じ名前でも、main が持っている x と sample01 が
持っている x は別物!
関数の引数には、2パターンあります。
 値渡し
関数に、値を渡します。
 アドレス渡し
関数に、変数のアドレスを渡します。
それぞれの特徴を捕らえて、適宜使い分ける必要が
あります。
23/59

値渡し:
関数 sa (int x) を、main内で sa (k)として使用
10
10
k
値を渡す
箱を準備
x
k
10
10
x
自分の
箱xに
保存
作業
返値を
返す
50
x
廃棄
24/59
アドレス渡しのイメージ図
値渡し
値渡し:
関数 sa (int *x) を、main内で sa (&k)として使用

10
*
*
k
場所を渡す

50
10
k

*
10
kx
10を代入するよ
返値を
返す
xと命名
* *

k
50
作業
*
k *x
25/59
main(){
int scan_value=10;
value=sample01(scan_value);
}



value=sample01(&scan_value);
}
main(){
int scan_value=10;
アドレスを渡すので、
&がついている。
関数main内の
scan_valueのアドレス
int sample01(int *tmp){
*tmp=*tmp+2;
return *tmp;
}
27/59

関数sample01内の
*tmpのアドレス
28/59
簡単な例:
どういうときに使うのか?

=
関数sample01内で*tmpの値を変えると、
関数main内のscan_valueの値も変わる
アドレス渡し その3

int sample01(int *tmp){
*tmp=*tmp+2;
return *tmp;
}
value=sample01(&scan_value);
}
使うよ
アドレスを
渡すよ
int sample01(int tmp){
tmp=tmp+2;
return tmp;
}
26/59
アドレス渡し その2
関数の引数として、(変数の)アドレスを渡す方法。
関数に渡されるのは、『変数のアドレス』です。
関数内部では、自分で使用する変数のアドレスを、
引数として渡されたアドレスに設定します。
関数内部と外部で名前は異なる変数かもしれませ
んが、保存する場所は一緒になります。 そのアドレス
main(){
int scan_value=10;
値は
10だよ
アドレスを貰うので、
ポインタを宣言。
アドレス渡し その1

関数の引数として、値を渡す方法。
関数に渡されるのは、あくまでも『値のみ』です。
変数が渡されるわけではありません。
関数内部では、渡された値を、自前の変数に代入し、
処理を行います。
自前の変数tmpに
関数に配列を渡したいとき
 配列の内容を、まとめて値渡しする事はでき
ない。
関数から、2つ以上の結果を手に入れたいとき。
 戻り値は1つしか指定できない!。
 変数のアドレスを渡して、そのアドレスに保
存されている値を関数内で弄ってしまう。
29/59
main(){
main(){
int
int v,w,value;
v,w,value;
省略
省略
value=sample02(v,&w);
value=sample02(v,&w);
省略
省略
}}


double
double sample02(int
sample02(int x,
x, int
int *y){
*y){
x++;
x++;
*y=x;
*y=x;
return
return x;
x;
}}
main中の
vの値
wのアドレス
sample02中の
xの値
*yのアドレス
sample02中で*yの値を変更 = main中のwの値を変更
sample02中でxの値を変更  main中のvの値を変更
30/59
例:値渡しとアドレス渡し 1-1
その1/2:
例:値渡しとアドレス渡し 1-2
#include<stdio.h>
#include<stdio.h>
その2/2:
int
int sa01(int
sa01(int x,int
x,int *y,
*y, int
int *z);
*z);
int
int main(){
main(){
int
int value=50;
value=50;
int
int *k;
*k;
int
int x=20,y=30;
x=20,y=30;
}}
x=x*10;
x=x*10;
k=x+
k=x+ *y
*y ++ *z;
*z;
*y=1020;
*y=1020;
*z=500;
*z=500;
*k=10;
*k=10;
printf("000
printf("000 %d
%d %d
%d %d
%d %d
%d %d
%d ¥n",
¥n",
&value,
&value, &x,&y,k);
&x,&y,k);
value=sa01(x,
value=sa01(x, &y,
&y, k);
k);
printf("001
printf("001 %d
%d %d
%d %d
%d %d
%d %d
%d ¥n",
¥n",
&value,
&x,&y,k);
&value, &x,&y,k);
printf("002
printf("002 %d
%d %d
%d %d
%d %d
%d ¥n",
¥n",
&k,&x,y,&*z);
&k,&x,y,&*z);
}}
31/59
例:値渡しとアドレス渡し 2-1
その1/2:
int
int sa01(int
sa01(int x,int
x,int *y,
*y, int
int *z){
*z){
int
int k;
k;
return
return k;
k;
32/59
例:値渡しとアドレス渡し 2-2
#include<stdio.h>
#include<stdio.h>
その2/2:
int
int sa01(int
sa01(int x,int
x,int *y,
*y, int
int *z);
*z);
int
int main(){
main(){
int
int value=50;
value=50;
int
int *k;
*k;
int
x=20,y=30;
int x=20,y=30;
}}
int
int sa01(int
sa01(int x,int
x,int *y,
*y, int
int *z){
*z){
int
int k;
k;
x=x*10;
x=x*10;
k=x+
k=x+ *y
*y ++ *z;
*z;
*y=1020;
*y=1020;
*z=500;
*z=500;
*k=10;
*k=10;
printf("000
printf("000 %d
%d %d
%d %d
%d %d
%d %d
%d ¥n",
¥n",
value,
value, x,y,*k);
x,y,*k);
value=sa01(x,
&y,
k);
value=sa01(x, &y, k);
printf("001
printf("001 %d
%d %d
%d %d
%d %d
%d %d
%d ¥n",
¥n",
value,
value, x,y,*k);
x,y,*k);
printf("002
printf("002 %d
%d %d
%d %d
%d %d
%d ¥n",
¥n",
k,x,*y,*z);
k,x,*y,*z);
33/59
}}
return
return k;
k;
34/59
スコープ その1
#include<stdio.h>
#include<stdio.h>
int
int scan_value;
scan_value;
変数のスコープ
35/59
int
int sample01(int
sample01(int max){
max){
int
int i;
i;
int
int value=0;
value=0;
略
略
}}
main(){
main(){
int
int value;
value;
略
略
}}
 それぞれの場所で宣言された
変数は、どこで参照可能か?
 関数 main の中で宣言
(value)
 関数 sample01 の中で宣言
(i, value, max)
 関数 main と sample01 の外
(scan_value)
36/59
スコープ その2
#include<stdio.h>
#include<stdio.h>
int
int scan_value;
scan_value;
int
int sample01(int
sample01(int max){
max){
int
int i;
i;
int
int value=0;
value=0;
略
略
}}
main(){
main(){
int
int value;
value;
略
略
}}
int
int sample01(int
sample01(int max){
max){
int
int i;
i;
int
int value=0;
value=0;
略
略
}}
main(){
main(){
int
int value;
value;
略
略
}}
関数の中で宣言された変数は、そ
の関数中でしか使用できない。

関数の外で宣言された変数は、ど
の関数からでもみることができる。
スコープ その3
 それぞれの場所で宣言された
変数は、どこで参照可能か?
 関数の中で宣言された変数
は、その関数中でしか使用
できない。
 関数の外で宣言された変数
は、どの関数からでもみる
ことができる。
37/59
#include<stdio.h>
#include<stdio.h>
int
int scan_value;
scan_value;
int
int sample01(int
sample01(int max){
max){
int
int i;
i;
int
int value=0;
value=0;
略
略
}}
main(){
main(){
int
int value;
value;
略
略
}}
全体で参照できる scan_value
sample01の中でのみ参照できる
i,value
value
mainの中でのみ参照できる
38/59

関数の中で宣言された変数は、そ
の関数中でしか使用できない。

関数の中で宣言された変数は、そ
の関数中でしか使用できない。

関数の外で宣言された変数は、ど
の関数からでもみることができる。
スコープ その5

関数の外で宣言された変数は、ど
の関数からでもみることができる。
スコープ その4
#include<stdio.h>
#include<stdio.h>
int
int scan_value;
scan_value;

全体で参照できる scan_value
sample01の中でのみ参照できる
i,value
参照できる範囲が
被ってないので、
名前が同じでも問題ない
value
mainの中でのみ参照できる
39/59
#include<stdio.h>
#include<stdio.h>
int
int scan_value;
scan_value;
int
int sample01(int
sample01(int max){
max){
int
int i;
i;
int
int value=0;
value=0;
略
略
}}
main(){
main(){
int
int st,value;
st,value;
st=sample01(value)
st=sample01(value)
}}
全体で参照できる(大域変数)
sample01の中でのみ参照できる
(ローカル変数)
mainの中でのみ参照できる
(ローカル変数)
 大域変数を使用しないなら、
関数間の値の受け渡しは、
引数や戻り値を通して行う。
40/59