PowerPoint プレゼンテーション

第9章 ポインタ
ポインタの意義
ポインタの宣言と型の意味
ポインタの使い方
メモリの動的確保
ポインタのポインタ
2次元配列の動的確保
ポインタの意義

プログラム(命令)も数値化してデータの一種に




ストアードプログラム方式(ノイマン型コンピュータ)
現在のコンピュータの大半はこの方式
コンピュータを万能マシンにした画期的なアイデア
メモリのアドレスもデータの一種に



変数(データ)は実際にはメモリに割り当てられる
割り当てられた場所の情報(アドレス)もデータ
アドレスを用いて処理するためにポインタ変数を用いる
ポインタの宣言と型の意味
int *p;




p がポインタ(アドレス変数)
*p は p のデータ(アドレス)が将来、示すで
あろうメモリの中身(データ)のこと
*p が int 型(4バイト整数)であるようなポイ
ンタを宣言している
int によりポインタのデータ(アドレス)の進
みかたは 4 (バイト)単位になる
sizeof(int)
宣言しただけでは中身(アドレス)は不定
ポインタの宣言と型の意味
double *xp;


*xp が double 型(8バイト浮動小数点数型)で
あるようなポインタを宣言している
double によりポインタのデータ(アドレス)の
進み方は 8 (バイト)単位になる
sizeof(double)
宣言しただけでは中身(アドレス)は不定
ポインタの使い方
宣言
2. 値(アドレス)の設定
3. 使用
#include
1.
pにはまだ適当な値が…
a のアドレスが設定
p(= a のアドレス)の
メモリの中身に代入
bに、p(=
a のアドレス)の
メモリの中身+1を代入
<stdio.h>
int main(void)
{
int a, b;
int *p; // 宣言
p = &a;
// 値の設定
*p = 100; // 使用
b = *p+1; // 使用
printf("a=%d, b=%d\n", a, b);
return 0;
}
プログラム例(pointer1.c)
#include <stdio.h>
#define N 10
int main(void)
1行めはアドレスも
中身もでたらめ
{
int i, a = 0, *p;
printf("%u: %d\n", p, *p);
p = &a;
for (i = 0; i < N; i++, p++)
printf("%u: %d\n", p, *p);
return 0;
}
アドレスは符号なし整数型
32bit OS では、利用可能な物理メモリは
232 = 4,294,967,296バイト = 4 GB まで
実行例
2行め以降、アドレスは
4 ずつ増加
4199151:
1310584:
1310588:
1310592:
1310596:
1310600:
1310604:
1310608:
1310612:
1310616:
1310620:
-1022311293
0
1310588
1310656
4199793
1
4263632
4263424
468547454
30108376
2147336192
2行めは 0 が入っているが、
あとはでたらめ
プログラム例(pointer2.c)
#include <stdio.h>
#define N 10
int main(void)
1行めはアドレスも
{
中身もでたらめ
int i;
double a = 0, *p;
printf("%u: %f\n", p, *p);
p = &a;
for (i = 0; i < N; i++, p++)
printf("%u: %f\n", p, *p);
return 0;
}
アドレスは符号なし整数型
32bit OS では、利用可能な物理メモリは
232 = 4,294,967,296バイト = 4 GBまで
実行例
2行め以降、アドレスは
8 ずつ増加
4199168:
1310580:
1310588:
1310596:
1310604:
1310612:
1310620:
1310628:
1310636:
1310644:
1310652:
0.000000
0.000000
0.000000
0.000000
0.000000
0.000000
-0.000000
0.000000
0.000000
0.000000
0.000000
2行めは 0.0 が入っているが、
あとはでたらめ?
プログラム例(sizeof_p.c)
変数、ポインタの
サイズを調べる
#include <stdio.h>
#define PRINTX(x) printf(#x" = %d\n", x)
int main(void)
{
式 x を引用符つき
int i, a, *pa;
文字列に展開する
double x, *px;
PRINTX(sizeof( a ));
PRINTX(sizeof(&a ));
PRINTX(sizeof( x ));
PRINTX(sizeof(&x ));
PRINTX(sizeof( pa));
PRINTX(sizeof(*pa));
PRINTX(sizeof( px));
PRINTX(sizeof(*px));
return 0;
}
整数変数
アドレス
浮動小数点数変数
アドレス
整数用のポインタ
ポインタが指す先のデータ
浮動小数点数用のポインタ
ポインタが指す先のデータ
実行例
sizeof( a )
sizeof(&a )
sizeof( x )
sizeof(&x )
sizeof( pa)
sizeof(*pa)
sizeof( px)
sizeof(*px)
=
=
=
=
=
=
=
=
4
4
8
4
4
4
4
8
メモリの動的確保
配列のような
連続した領域を確保
#include <stdlib.h>
確保に失敗したら
NULL を返す
int *p1, *p2;
p1 = (int *)malloc(sizeof(int)*N);

メモリ確保(全サイズ=1個分のサイズ×数)
p2 = (int *)calloc(N,sizeof(int));

初期化つきメモリ確保(数,変数1個分のサイズ)
free(p1);
free(p2);

メモリの開放
Cでよく出てくる構文(エラー処理)
if ((p1 = (int *)malloc(sizeof(int)*N)) == NULL) {
perror("I can't allocate p1."); abort();
}
NULL: ヌルポインタ
どのアドレスも指してないポインタ
実体は数字の 0
p1 ← (int *)malloc(sizeof(int)*N)
perror("I can't allocate p1.")
p1 == NULL
abort()
プログラム例(i_alloc.c)
#include <stdio.h>
Z:\nyumon2> i_alloc 200000000
#include <stdlib.h>
#define N 200000000 // < 500000000
int main(int argc, char *argv[])
コマンドライン
{
オプション入力
int i, n=N, p1, *p2;
if (argc > 1) sscanf(argv[1], "%d", &n);
if ((p1 = (int *)malloc(sizeof(int)*n)) == NULL) {
perror("I can't allocate p1."); abort();
}
if ((p2 = (int *)calloc(n,sizeof(int))) == NULL) {
perror("I can't allocate p2."); abort();
スタック領域
}
ポインタ自身のありか
printf("%u %u %d\n", &p1, &p2, &p2-&p1);
printf("%u %u %d\n", p1, p2, p2-p1);
動的に確保された場所
for (i=0; i<80; i++) printf(" %9d", *(p1+i)); printf("\n");
ヒープ領域
for (i=0; i<80; i++) printf(" %9d", *(p2+i));
free(p1); free(p2);
return 0;
}
プログラム例(d_alloc.c)
#include <stdio.h>
Z:\nyumon2> d_alloc 100000000
#include <stdlib.h>
#define N 100000000 // < 250000000
コマンドライン
int main(int argc, char *argv[])
オプション入力
{
int i, n=N;
double *p1, *p2;
if (argc > 1) sscanf(argv[1], "%d", &n);
if ((p1 = (double *)malloc(sizeof(double)*n)) == NULL) {
perror("I can't allocate p1."); abort();
}
if ((p2 = (double *)calloc(n,sizeof(double))) == NULL) {
スタック領域
perror("I can't allocate p2."); abort();
}
ポインタ自身のありか
printf("%u %u %d\n", &p1, &p2, &p2-&p1);
動的に確保された場所
printf("%u %u %d\n", p1, p2, p2-p1);
for (i=0; i<80; i++) printf(" %.7f", *(p1+i)); printf("\n"); ヒープ領域
for (i=0; i<80; i++) printf(" %.7f", *(p2+i));
free(p1); free(p2);
return 0;
}
ポインタのポインタ
int **p;



ポインタ(アドレス変数)のポインタ(アドレス変数)
宣言しただけでは何も役に立たない
2次元配列を動的に作成する際に使用
p
*p
**p
unsigned
unsigned
4ずつ増加
sizeof(型)ずつ増加
int
double
...
2次元配列の動的確保
a = (int **)malloc(sizeof(int *)*N);
for (i = 0; i < N; i++) {
*(a+i) = (int *)malloc(sizeof(int)*M);
}
a
a[0]
a[0][0]
*a
**a
a[0][3]
*(*a+1)
*(*a+2)
*(*a+3)
*(a+1)
**(a+1)
*(*(a+1)+1) *(*(a+1)+2) *(*(a+1)+3)
*(a+2)
**(a+2)
*(*(a+2)+1) *(*(a+2)+2) *(*(a+2)+3)
*(a+3)
**(a+3)
*(*(a+3)+1) *(*(a+3)+2) *(*(a+3)+3)
a[3]
a[3][0]
a[3][3]
2次元配列の動的確保(d_alloc2.c)
#include <stdio.h>
#include <stdlib.h>
#define N 1000
#define M 1000
int main(void)
{
int i;
double **a;
a = (double **)calloc(N, sizeof(double *));
for (i = 0; i < N; i++)
if ((*(a+i) = (double *)calloc(M, sizeof(double))) == NULL) {
perror("I can't allocate *(a+i)."); abort();
}
次ページに続く
(d_alloc2.c 続き)
printf("%u %u %u\n", &a, a, *a);
a[i][j]
for (i=0; i<N; i++)
for (j=0; j<M; j++) *(*(a+i)+j) = (i*M+j)*1e-7;
for (j=0; j<80; j++) printf(" %.7f", *(*a+j));
printf("\n");
for (i=0; i<80; i++) printf(" %.7f", **(a+i));
for (i=0; i<N; i++) free(*(a+i));
return 0;
}
a[i]
a[0][j]
a[i][0]
乱数の発生
#include <stdlib.h>
#include <time.h>
/* 実行するたびに異なる乱数を発生 */
srand((unsigned)time(NULL)); // シード設定
a = rand();
// 0,...,RAND_MAX(= 65535 = 216-1)
b = rand()%N;
// 0,...,N-1
x = rand()%2;
// 0,1 (コイントス)
y = rand()%3;
// 0,1,2 (じゃんけん)
z = rand()%6+1; // 1,2,3,4,5,6 (サイコロ)
時間計測:秒単位(time1.c)
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
← 時間関数用ヘッダ
int main(void)
{
int i, N=100000;
int t1, t2;
計測開始
t1 = time(NULL);
for (i=0; i<N; i++) printf(" %d", rand()%6 + 1);
printf("\n");
計測終了
t2 = time(NULL);
printf("%d,%d,%d sec\n", t1, t2, t2-t1);
return 0;
実行時間
}
時間計測:ミリ秒単位(time2.c)
#include <stdio.h>
#include <stdlib.h>
← Windows用ヘッダファイル
#include <windows.h>
#pragma comment(lib,"winmm.lib") ← 音楽用Windowsライブラリ
int main(void)
{
int i, N=100000;
int t1, t2;
計測開始
t1 = timeGetTime();
for (i=0; i<N; i++) printf(" %d", rand()%6 + 1);
printf("\n");
計測終了
t2 = timeGetTime();
printf("%d,%d,%d msec\n", t1, t2, t2-t1);
return 0;
実行時間
}
本日のパズル
次のプログラムは何が出力されるか?
#define PRINT(int) printf("%d\n",int)
main()
{
int x, y, z;
}
x = 2; y = 1; z = 0;
x = x && y || z; PRINT(x);
PRINT( x || ! y && z );
1
2
x
z
z
z
3
4
5
= y = 1;
= x ++ - 1; PRINT(x); PRINT(z);
+= - x ++ + ++ y; PRINT(x); PRINT(z);
= x / ++ x; PRINT(z);