第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);
© Copyright 2024 ExpyDoc