こちら - tsukuba.ac.jp

応用理工学情報処理
第13回(2016年1月25日)
manaba
https://manaba.tsukuba.ac.jp
の応用理工学情報処理のページ
授業のHP
http://www.ims.tsukuba.ac.jp/~maeshima/joho
1
第10章
ポインタ
2
教科書p.260のプログラム(間違いあり)
#include <stdio.h>
void sum_diff(int n1, int n2, int sum, int diff)
{
sum = n1 + n2;
diff= (n1>n2) ? n1 - n2 : n2 - n1;
目的
2つの整数の和と差を
計算したい
}
int main(void)
{
int na, nb;
int wa = 0, sa = 0;
puts("二つの整数を入力してください");
printf("整数A: "); scanf("%d", &na);
printf("整数B: "); scanf("%d", &nb);
左のプログラムを作成して
実行
二つの整数を入力してください。
整数A: 57
整数B: 21
和は0です。
差は0です。
sum_diff(na, nb, wa, sa);
printf("和は%dです。\n差は%dです。\n", wa, sa);
return 0;
}
動作はするが、
望んでいる結果が
得られない
3
問題点: 関数の値渡し
int main(void) {
int na, nb;
int wa=0, sa=0;
sa=57, sb=21として
sum_diff(na, nb, wa, sa);
….
….
printf(“n=%d\n”,n);
.. }
main側から関数側に値が渡される
n1=57, n2=21, sum=0, diff=0
void sum_diff(int n1, int n2, int sum, int diff)
{
sum, diffは正しく計算される。
しかし、main側に関数側の
変数の値は(返却値にしない限り)
戻らない(p.140)。
main側
wa,saの値に変化なし
}
wa=0, sa=0のまま
関数側の結果を
main側に反映させる必要がある
4
変数(オブジェクト)とアドレス
プログラム内で
変数を宣言する
int n;
double x;
アドレス
メモリ内部で変数が
保存されている場所
メモリ内部に変数の値
を保存する場所が作ら
れる
n
x
コンピュータのメモリ内部
5
アドレス演算子
int n;
&n
int 型変数 n が保存されているアドレスを示す
コンピュータのメモリ内部
nが保存されている
場所を示すのが &n
n
配列の各要素に対しても同様
int a[3];
&a[0]
int 型配列aの要素a[0]が保存されているアドレス
6
アドレス演算子(p.262)
#include <stdio.h>
int main(void)
{
int n;
double x;
int a[3];
printf("n のアドレス: %p\n", &n);
printf("x のアドレス: %p\n", &x);
printf(“a[0] のアドレス: %p\n", &a[0]);
printf(“a[1] のアドレス: %p\n", &a[1]);
printf(“a[2] のアドレス: %p\n", &a[2]);
%pは アドレスを表示
するための変換指定
アドレスは16進数表記
return 0;
}
配列は連続した位置に保存される
a[0]
a[1]
a[2]
7
ポインタ:アドレスを保存できる変数
int n;
ポインタの宣言
ポインタへの
アドレスの代入
(まずint型変数nを宣言)
int *ptr;
int型変数に対する
ポインタが宣言される
ptr = &n;
ptrは int型変数 nのアドレスを保存
上のように、ptrにnのアドレスを代入すると
ptrで指定されるアドレスに保
存されている値
=(nの値)
*ptr
* は間接演算子(p.265) と呼ばれる
n
ptr
8
ポインタ
#include <stdio.h>
int main(void)
{
int na = 10, nb=20;
int *ptr;
ptrはポインタ
ptrは int型変数 naの
アドレスを保存
na の値を表示
ptr = &na;
printf("na のアドレス: %p\n", ptr);
printf("na の値
: %d\n", *ptr);
puts("---------");
ptr = &nb;
printf("nb のアドレス: %p\n", ptr);
printf("nb の値
: %d\n", *ptr);
ptrは int型変数 nbの
アドレスを保存
nb の値を表示
return 0;
}
9
ポインタ(2)
ptr = &na;
*ptr は ptrで指定されるアドレスに保存されている値
*ptr
は na そのもの
int na;
int *ptr;
ptr = &na;
*ptr = *ptr + 1;
*ptrが1増える
=
na が1増える
*ptr は naのエイリアス(別名)
10
ポインタ(2)
#include <stdio.h>
int main(void)
{
int na = 10;
int *ptr;
ptr = &na;
printf("na のアドレス: %p\n", ptr);
printf("na の値
: %d\n", *ptr);
*ptr = *ptr + 1;
printf("na+1 の値 : %d\n", *ptr);
printf("na+1 の値 : %d\n", na);
*ptrの値が 1 増える
na の値も 1 増える
return 0;
}
11
ポインタと関数(p.268)
#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);
printf("和は%dです。\n差は%dです。\n", wa,
sa);
p.260のプログラムの修正版
return 0;
}
12
ポインタと関数(p.268)
ポインタを使わない場合(p.260,間違いあり)
ポインタを使った場合
void sum_diff(int n1, int n2,
int sum, int diff)
{
sum = n1 + n2;
diff = (n1 > n2) ? n1 - n2 : n2 - n1;
}
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 wa=0, sa=0;
sum_diff(na, nb, wa, sa);
}
Int main(void)
{
int wa=0, sa=0;
sum_diff(na, nb, &wa, &sa);
}
main側:
wa sa
はメモリ上は
sum_diff側: sum, diff 全く別のもの
sum_diff内での計算結果は
main側には反映されない
wa = 0, sa=0のまま
ポインタ: sum, diff に wa, saの
アドレスが渡される
*sum, *diffはそれぞれ
wa, saのエイリアス
つまり*sum, *diffはwa, saと全く同じ
13
ポインタと関数(2)(p.270)
int main(void)
{
int na, nb;
#include <stdio.h>
呼
び
出
し
void swap(int *px, int *py)
{
int temp = *px;
*px = *py;
*py = temp;
}
puts("二つの整数を入力してください");
printf("整数A: "); scanf("%d", &na);
printf("整数B: "); scanf("%d", &nb);
sort2(&na, &nb);
呼び出し
void sort2(int *n1, int *n2)
{
if (*n1 > *n2)
swap(n1, n2);
}
n1, n2はポインタ変数なので
*をつけない n1, n2がアドレスになる
na, nbは普通の変数なので
アドレスは &na, &nb
puts("昇順にソートしました。");
printf("整数Aは%dです。\n", na);
printf("整数Bは%dです。\n", nb);
return 0;
}
10
ポインタと配列
1. 配列 a のi番目の要素 a[i]のアドレスは&a[i] (p.262)
int a[5];
int *p = &a[2];
a[0]
a[1]
a[2]
とすると
a[3]
a[4]
p
2. 配列名aはその配列の先頭要素へのポインタ
int a[5];
int *p = a;
a[0]
a[1]
とすると
a[2]
a[3]
a[4]
p
15
ポインタと配列
ポインタpを用いた配列aの各成分の表し方
int a[5];
int *p = a;
a[0]
a[1]
a[2]
a[3]
a[4]
p
以下の2通りの方法がある
1. *(p+i)
2. p[i]
p+i(-i)はpが示す要素
のi個だけ後方(前方)の
要素を指すポインタ
間接演算子 *
をつける
ポインタ p をあたかも配列のように扱
える
16
ポインタと配列(p.277)
#include <stdio.h>
int main(void)
{
int i;
int a[5] = {1, 2, 3, 4, 5};
int *p = a;
int a[5]={….}のように配列aを宣言すると
配列名aは配列の先頭要素へのポインタ
*(a+i)も前ページの*(p+i)と同じ解釈
→ 先頭からi番目の要素、つまりa[i]
for (i=0; i<5; i++)
printf("a[%d] = %d *(a+%d)=%d p[%d]=%d *(p+%d) = %d\n", i, a[i], i, *(a+i), i, p[i], i, *(p+i));
for (i=0; i<5; i++)
printf("&a[%d] = %p a+%d=%p &p[%d]=%p p+%d = %p\n", i, &a[i], i, a+i, i, &p[i], i, p+i);
return 0;
}
配列の受け渡し(p.280 c )
#include <stdio.h>
p.280 aのように int v[] でもOK
int main(void)
{
int i;
int a[] = {1, 2, 3, 4, 5};
void ary_set(int *v, int n, int val)
{
int i;
ary_set(a, 5, 99);
for (i = 0; i < 5; i++)
printf("a[%d] = %d\n", i, a[i]);
for (i = 0; i < n; i++)
v[i] = val;
}
return 0;
}
main側
ary_set(a,
5,
99);
関数側
void ary_set(int *v, int n, int val)
{
}
aは配列aの先頭要素のアドレス
ポインタ vにaの先頭
アドレスが代入される
18
受け取った配列への書き込み
呼び出された関数側で、仮引数として受け取った配列に書き込むと、
呼び出し側で実引数として与えた配列の内容も変わる(6章 p.152)
実は、main側から関数側に
配列 a のアドレスが渡されていた
int main(void){
int a[] = {1, 2, 3, 4, 5};
関数側の配列vはaのエイリアス
関数側で配列vに書き込むと、呼び
出し側の配列aの内容も変わる
ary_set( a, 5, 99);
:
}
void ary_set( int v[], int n, int val)
{
int i;
for ( i = 0; i < n; i++)
v[i] = val;
}
19
演習問題
3つのint型整数 n1, n2, n3を昇順( n1 < n2 < n3 )に並べ替える関数 void
sort3(int *n1, int *n2, int *n3)を作成せよ(教科書 p.271, 演習10-3) 。 この関
数を用いて、main内で宣言された3つのint型整数 a1, a2, a3 の値を並べ替え 、
その結果を表示するプログラムを作成せよ。 a1, a2, a3の値はscanfを用いて
読み込むものとする。
演習問題の提出・出席確認について
• 授業中にプログラムが完成した方は申し出てください。プログラムの確
認・出席の確認を同時に行います。完成が確認できた方は退出しても
構いません。
• 授業終了10分前(16:20)の時点でプログラムが未完成の方も、申し出て
ください。出席の確認のみを行います。
• 授業終了までにプログラムを完成できなかった方は次回の授業の際に
確認を行いますので、それまでに完成させておいてください。
20