Document

プログラミング入門2
第10回
ポインタ
芝浦工業大学情報工学科
青木 義満
ポインタがオブジェクトを「指す」
 ポインタptの値がオブジェクトnxのアドレスであるとき,
pt は nx を指す
という。
57
メモリ上の番地
int nx;
int *pt;
pt = &nx;
1000番地
=
nx
&nx
代入
nxのアドレス
pt
ptにはオブジェクトnxのアドレスが格納されているので,
ptはnxを指している といえる。
プログラミング入門2
2
ポインタとエイリアス(別名)
int nx;
int *pt;
nx = 57;
pt = &nx;
pt は x を指す
*pt はptが指すオブジェクトの
エイリアス(別名)
x
x
pt
*pt
pt
*ptはxの別名(エイリアス)
重要
ポインタpがオブジェクトxを指すとき,*pはxのエイリアス(別名)となる
プログラミング入門2
3
今回の講義内容
 ポインタと関数
 ポインタと配列
プログラミング入門2
4
ポインタと関数
 ソースファイル名:list1004-1.c
 関数の引数とポインタ(間違い)
#include <stdio.h>
void hiroko(int height)
{
if ( height < 180)
height = 180;
}
身長を180cmに伸ばしてくれる関数
Height before : 179
Height after : 179
int main(void)
{
int masaki = 179;
printf("height before:%d\n", masaki);
値が変更されていない
!
hiroko(masaki);
printf("height after:%d\n", masaki);
return (0);
}
プログラミング入門2
5
値の受け渡し
 実引数の値が仮引数の値にコピーされるだけ
#include <stdio.h>
void hiroko(int height)
{
if ( height < 180)
height = 180;
}
・heightには179という値がコピーされる
・関数内でheightの値は変更されるが,
main文の方のmasakiの値は変更されない!
int main(void)
{
int masaki = 179;
printf("height before:%d\n", masaki);
hiroko(masaki);
直接値を変更できない
↓
ポインタを利用して,間接的に変更!
printf("height after:%d\n", masaki);
return (0);
}
プログラミング入門2
6
ポインタと関数
 ソースファイル名:list1004-2.c
 関数の引数とポインタ(正解)
#include <stdio.h>
void hiroko(int *height)
{
if ( *height < 180)
*height = 180;
}
身長を180cmに伸ばしてくれる関数
Height before : 179
Height after : 180
int main(void)
{
int masaki = 179;
printf("height before:%d\n", masaki);
値が変更OK!
hiroko(&masaki);
printf("height after:%d\n", masaki);
return (0);
}
プログラミング入門2
7
関数の引数としてのポインタ
 ポインタを介して,間接的に変数の値を変更
int masaki;
masaki = 179;
masakiのアドレス(&masaki)
を関数に渡す
hiroko(&masaki);
int *height;
height = &masaki;
106番地
void hiroko(int *height)
{
if ( *height < 180)
*height = 180;
}
*heightはmasakiのエイリアス
106番地
179
masaki
179
*height
180
height
プログラミング入門2
8
関数におけるポインタの使用例(p.234)
ソースファイル名:list1005-1.c
 関数の引数とポインタ(間違い)

#include <stdio.h>
void swap(int nx, int ny)
{
int temp = nx;
nx = ny;
ny = temp;
}
int main(void)
{
int na, nb;
temp
nxの値を一時的に
tempに格納
nx
nyにtemp(元のnxの値)
を代入
ny
nyの値をnxに代入
puts("二つの整数を入力してください。");
printf("整数A:"); scanf("%d", &na);
printf("整数B:"); scanf("%d", &nb);
swap(na, nb);
変数の値を関数を使って変更したい
→ ポインタを使いましょう
puts("これらの値を交換しました。");
printf("整数Aは%dです。\n", na);
printf("整数Bは%dです。\n", nb);
return (0);
}
プログラミング入門2
9
関数におけるポインタの使用例(p.234)
ソースファイル名:list1005-2.c
 関数の引数とポインタ(正解)

#include <stdio.h>
void swap(int *nx, int *ny)
{
int temp = *nx;
*nx = *ny;
*ny = temp;
}
int main(void)
{
int na, nb;
puts("二つの整数を入力してください。");
printf("整数A:"); scanf("%d", &na);
printf("整数B:"); scanf("%d", &nb);
swap(&na, &nb);
puts("これらの値を交換しました。");
printf("整数Aは%dです。\n", na);
printf("整数Bは%dです。\n", nb);
return (0);
}
プログラミング入門2
10
関数とポインタの重要ポイント
 関数に対して,変数の値の変更を頼みたい時

関数にその変数へのポインタ(その変数のアドレス)
を渡す
ポインタ(オブジェクトのアドレス)を渡して,
関数側はそのポインタが指すオブジェクトに対して処理を行う
hiroko(&masaki);
void hiroko(int *height)
プログラミング入門2
11
関数におけるポインタの使用例(p.236)
ソースファイル名:list1007.c
 2つの整数の和と差を求める

#include <stdio.h>
void sum_diff( int n1, int n2, int *sum, int *diff )
{
*sum = n1 + n2;
*diff = (n1 > n2) ? n1 - n2 : n2 - n1;
}
int main(void)
{
int na, nb;
int wa = 0, sa = 0;
puts("二つの整数を入力してください。");
printf("整数A:"); scanf("%d", &na);
printf("整数B:"); scanf("%d", &nb);
sum_diff( na, nb, &wa, &sa );
何故,na, nbには&が不要?
printf("和は%dです。\n差は%dです。\n", wa, sa);
return (0);
}
プログラミング入門2
12
ポインタを使うか使わないか
 値を変更する必要があれば,ポインタで受け渡し
 そうでなければ,ポインタを使用する必要なし
(例えば,その値を計算にしようするだけ などの場合)
void sum_diff( int n1, int n2, int *sum, int *diff )
{
*sum = n1 + n2;
*diff = (n1 > n2) ? n1 - n2 : n2 - n1;
}
n1, n2 は和と差を求めるために使用するだけ
→値だけ使えればよい(ポインタを使う必要なし)
sum, diff は変数のアドレスを受け取り,和と差を求めた結果を代入
→値を変更する必要がある(ポインタを使う必要あり)
プログラミング入門2
13
値の並べ替え(ソート,p.237)
 ソースファイル名: list1008.c
 2つの整数値を小さい順に並べる(昇順ソート)
#include <stdio.h>
/*--- nx・nyが指すオブジェクトの値を交換 ---*/
int main(void)
{
int
void swap(int *nx, int *ny)
puts("二つの整数を入力してください。");
printf("整数A:"); scanf("%d", &na);
printf("整数B:"); scanf("%d", &nb);
{
int temp = *nx;
*nx = *ny;
*ny = temp;
sort2( &na, &nb );
}
puts("これらの値を昇順に並べました。");
printf("整数Aは%dです。\n", na);
printf("整数Bは%dです。\n", nb);
/*--- *n1≦*n2となるように並べる ---*/
void sort2(int *n1, int *n2)
{
if (*n1 > *n2)
swap(n1, n2);
}
na, nb;
return (0);
}
*n1, *n2, n1, n2には何の値が入っている?
プログラミング入門2
14
scanf関数とポインタ
 printf関数とscanf関数
 printf(“x = %d\n”, x);

scanf(“%d”, &x);
 printfはただ変数の値を表示するだけ
 変数の値を変更する必要なし
→ そのまま変数を渡す
 scanfは,キーボードから読み込んだ値を変数に格納する
 変数に値を代入(値を変更)する必要
→ ポインタ(アドレス)で渡す!
プログラミング入門2
15
ポインタと配列 (p.240)
ソースファイル: list1010.c
 配列とポインタ

#include <stdio.h>
int main(void)
{
int
i;
int
vc[5] = {10, 20, 30, 40, 50};
int
*ptr = &vc[0];
for (i = 0; i < 5; i++)
printf("vc[%d] = %d ptr[%d] = %d *(ptr + %d) = %d\n",
i, vc[i], i, ptr[i], i, *(ptr + i) );
return (0);
}
プログラミング入門2
16
配列とポインタ
 配列 vc の先頭要素vc[0]のアドレス &vc[0] を
ptrに代入
int *ptr = &vc[0];
ptr + i
Ptrが指すオブジェクトのi個後ろの要素
を指すポインタ
*(ptr + i)
Ptrが指すオブジェクトのi個後ろの要素
のエイリアス
*(ptr + i) = ptr[i]
vc[0]
*ptr
ptr[0]
vc[1]
vc[2]
*(ptr+1)
ptr[1]
*(ptr+2)
ptr[2]
vc[3]
vc[4]
*(ptr+3)
ptr[3]
*(ptr+4)
ptr[4]
ptr
ポインタに対して [ ]を適用すること
で,配列のように扱える!!
プログラミング入門2
17
配列とアドレス (p.242)
ソースファイル: list1011.c
 配列のアドレスを表示

#include <stdio.h>
int main(void)
{
int
vc[3];
printf("vc :%p\n", vc);
printf("vc[0]のアドレス:%p\n", &vc[0]);
printf("vc[1]のアドレス:%p\n", &vc[1]);
printf("vc[2]のアドレス:%p\n", &vc[2]);
return (0);
}
添え字演算子[ ]を使わずに単独に現れた配列名は,
その配列の先頭要素へのポインタとみなされる
プログラミング入門2
18
配列の受け渡し (p.244)
ソースファイル名:list1013.c
 関数への配列の受け渡し(復習)

#include <stdio.h>
void int_set( int
intvc[
*vc] )
{
int i;
for (i = 0; i < 5; i++)
vc[i] = 0;
}
復習
関数で配列を受け取る時,
int vc[ ] → int型のオブジェクトのアドレスを受け取る
= int *vc で受け取り可能!
int main(void)
{
int i;
int ary[ ] = {1, 2, 3, 4, 5};
int_set( ary );
配列名(ary)
=配列の先頭要素のアドレス
=&(ary[0]) int型のオブジェクトのアドレスを渡す
for (i = 0; i < 5; i++)
printf("ary[%d] = %d\n", i, ary[i]);
return (0);
}
プログラミング入門2
19
配列の受け渡し
 関数間での配列の受け渡し


先頭要素へのポインタで行う
受け取り側の関数は,そのポインタに対して添え字演算子 [ ]を適用する
ことで,配列のようにアクセスできる
int_set( ary );
500番地
502番地
504番地
506番地
508番地
ary[0]
*vc
vc[0]
ary[1]
*(vc+1)
vc[1]
ary[2]
*(vc+2)
vc[2]
ary[3]
*(vc+3)
vc[3]
ary[4]
*(vc+4)
vc[4]
&ary[0]
(500番地)
void int_set( int *vc )
int型オブジェクトのアドレスを受け取るので,
ポインタ(int *)で宣言
vc
プログラミング入門2
20
scanf関数とポインタ
 scanfは,キーボードから読み込んだ値を変数に格納する
 変数に値を代入(値を変更)する必要
→ ポインタ(アドレス)で渡す!
 文字列の場合

文字配列の先頭アドレスを渡してやればよい
&をつけずに,配列名でOK
char name[256];
name = &(name[0])
scanf( “%s”, name );
プログラミング入門2
21
二次元配列の関数への渡し方
 ソースファイル名: 2jigen.c
 関数への二次元配列データの受け渡し
#include <stdio.h>
void print_name( char x[ ][256] )
先頭の要素数は省略可能
{
int i;
for(i=0; i < 4; i++){
printf("name[%d] = %s\n", i, x[i] );
}
}
int main(void)
{
char name[4][256]={"aoki", "morishima", "tokunaga", "yanagisawa" };
print_name( name );
配列名で関数へ受け渡し
return(0);
}
プログラミング入門2
22
演習課題

ファイル名:kadai10-1.c
ポインタと関数,配列

3名分の氏名,身長,体重データが格納されている配列を受け取って,肥満度(BMI
値)を計算し,全員の肥満度を表示する関数を設計せよ

氏名、身長、体重、BMI値を格納する配列はMain関数で宣言、関数にそれらのデータ
を渡して関数内でBMI値を計算して配列BMIに結果を格納。

Main関数中でBMI値に基づき、判定結果を表示。

氏名,身長,体重のデータはキーボード読込み,ファイル読込みのどちらでも良い。
void calculate_bmi( char name[ ][256], double *height, double *weight, double *bmi )
名前の配列
身長の配列
体重の配列
BMIの配列
<BMI(Body mass index)の計算方法>
身長の二乗に対する体重の比で体格を表す指数
BMI=体重(kg)/(身長m)2
BMI 25以上
BMI 18.5以上25未満
BMI 18.5未満
→ 肥満
→ 標準体重
→ 低体重
プログラミング入門2
23
演習課題
ファイル名:kadai10-2.c
要素数noのint型配列データを受け取り,値の小さい順に要素を並べ替える(ソート)関数を設計し,
その動作を確認せよ。なお,並べ替えのアルゴリズムは以下の手順に従うこと。(他のアルゴリズムでも良い)
void select_sort( int *vc, int no )
セレクトソート法(最も単純なソート法)
※3, 5, 2, 4, 1 というデータを例に説明
A.まず、データ全体の中から、最小の値を探す
B.探し出した最小の値と、データの先頭の値を、入れ替える
C.次に、先頭から2番目より最後までの値の中から、最小の値を探す
D.探し出した最小の値と、先頭から2番目の値の値を、入れ替える
E.同様に、先頭から3番目より最後までの値の中から、最小の値を探す
F.探し出した最小の値と、先頭から3番目の値の値を、入れ替える
G.最後まで処理を繰り返す
この結果、順番が1, 2, 3, 4, 5 と並び替えられる
3, 5, 2, 4, 1
1, 5, 2, 4, 3
1, 5, 2, 4, 3
1, 2, 5, 4, 3
1, 2, 5, 4, 3
1, 2, 3, 4, 5
提出期限: 12月17日(月)13:00(厳守)
提出先: [email protected]
メールのタイトル:pro2-10 学籍番号+自分の苗字
例) pro2-10 L02001 aoki
プログラミング入門2
24