プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。)

Slides:



Advertisements
Similar presentations
配列の宣言 配列要素の初期値 配列の上限 メモリ領域 多次元配列 配列の応用
Advertisements

プログラミング演習II 2004年11月 30日(第6回) 理学部数学科・木村巌.
15.1 文字列処理の基本 15.2 文字列処理用ライブラリ関数
プログラミング入門2 第4回 配列 for文 変数宣言 初期化
情報基礎演習B 後半第5回 担当 岩村 TA 谷本君.
数理情報工学演習第一C プログラミング演習 (第3回 ) 2014/04/21
プログラミング入門2 第10回 動的な領域確保 情報工学科 篠埜 功.
データ構造とアルゴリズム 第10回 mallocとfree
プログラミング演習Ⅱ 第12回 文字列とポインタ(1)
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
第8回 プログラミングⅡ 第8回
プログラミング演習Ⅰ 課題2 10進数と2進数 2回目.
構造体 構造体, 構造体とポインタの組み合わせ,.
10: ファイル入出力 C プログラミング入門 基幹2 (月4) Linux にログインし、以下の講義ページ を開いておくこと
プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。)
プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。)
C言語講座 第3回 ポインタ、配列.
プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。)
プログラミング論 関数ポインタ と 応用(qsort)
ちょっとした練習問題① 配列iroを['R', 'W', 'R', 'R', 'W' , 'W' , 'W']を宣言して、「W」のときの配列の番号をprintfで表示するようなプログラムを記述しなさい。
プログラミング2 関数
プログラミング論 ファイル入出力
Cプログラミング演習 第7回 メモリ内でのデータの配置.
精密工学科プログラミング基礎 第10回資料 (12/18実施)
プログラミング 4 記憶の割り付け.
2005年度 データ構造とアルゴリズム 第3回 「C言語の復習:再帰的データ構造」
プログラミング演習I 2003年5月7日(第4回) 木村巌.
第10章 これはかなり大変な事項!! ~ポインタ~
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
メモリの準備 メモリには、その準備の方法で2種類ある。 静的変数: コンパイル時にすでにメモリのサイズがわかっているもの。 普通の変数宣言
プログラミング入門2 第11回 情報工学科 篠埜 功.
前回の練習問題.
プログラミング入門2 第11回 情報工学科 篠埜 功.
第7回 プログラミングⅡ 第7回
地域情報学 C言語プログラミング 第5回 ポインタ、関数、ファイル入出力 2017年11月17日
プログラミング論 ファイル入出力
P n ポインタの基礎 5 q m 5 7 int* p; int 型の変数を指すポインタ int* q; int 型の変数を指すポインタ int n=5, m=7; int 型の変数 int array[3]; int* pArray[3]; p = &n; ポインタにアドレスを代入しているのでOK.
プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。)
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
岩村雅一 知能情報工学演習I 第12回(C言語第6回) 岩村雅一
配列変数とポインタ 静的確保と動的確保 ポインタ配列 2次元配列 時間計測 第1回レポートの課題
メモリとメモリアドレス, ポインタ変数,関数へのポインタ渡し
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
プログラミング言語論 第六回 理工学部 情報システム工学科 新田直也.
精密工学科プログラミング基礎Ⅱ 第5回資料 今回の授業で習得してほしいこと: 構造体 (教科書 91 ページ)
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
15.1 文字列処理の基本 15.2 文字列処理用ライブラリ関数
文字列へのポインタの配列 static char *lines[MAXLINES]; lines[0] NULL
高度プログラミング演習 (09).
情報基礎演習B 後半第2回 担当 岩村 TA 谷本君.
プログラミング論 ポインタ
ネットワーク・プログラミング Cプログラミングの基礎.
第5回 プログラミングⅡ 第5回
プログラミング入門2 第5回 配列 for文 変数宣言 初期化
岩村雅一 知能情報工学演習I 第12回(後半第6回) 岩村雅一
11: 動的メモリ確保 C プログラミング入門 基幹2 (月4) Linux にログインし、以下の講義ページ を開いておくこと
プログラミング演習I 2003年6月11日(第9回) 木村巌.
プログラミング演習II 2004年11月 16日(第5回) 理学部数学科・木村巌.
プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。)
15.1 文字列処理の基本 15.2 文字列処理用ライブラリ関数
プログラミング演習II 2003年12月10日(第7回) 木村巌.
プログラミング入門2 第5回 配列 変数宣言、初期化について
プログラミング入門2 第3回 条件分岐(2) 繰り返し文 篠埜 功.
岩村雅一 知能情報工学演習I 第13回(後半第7回) 岩村雅一
プログラミング演習II 2003年10月29日(第2,3回) 木村巌.
プログラミング演習I 補講用課題
プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。)
12: コマンドライン引数 C プログラミング入門 基幹2 (月4) Linux にログインし、以下の講義ページ を開いておくこと
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
Presentation transcript:

プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。) C言語入門 第12週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。)

動的配列

動的配列の基本 malloc で確保し free で解放する 講義資料 第6週p.59, 第9週pp.30-34. unsigned char *img; int w, h; // 動的にサイズを決める fprintf(stderr, "w = ?"); scanf("%d", &w); fprintf(stderr, "h = ?"); scanf("%d", &h); if ((img = malloc(3 * w * h)) == NULL) { // img の動的確保 fprintf(stderr, "Error: in %s line %d: malloc failed\n", __FILE__, __LINE__); exit(EXIT_FAILURE); } // ここで img に対する処理を行う free(img); // 使い終わったimgの解放

動的配列の例 1週目の bmptest.c と同じグラデーション 講義資料 第6週p.59, 第9週pp.30-34. dynamic_array_test.c 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 void print_ppm(unsigned char *img, int w, int h, int q); int main() { unsigned char *img; int w, h, x, y; fprintf(stderr, "w = ? "); scanf("%d", &w); fprintf(stderr, "h = ? "); scanf("%d", &h); if ((img = malloc(3 * w * h)) == NULL) { fprintf(stderr, "Error: in %s line %d: malloc failed\n", __FILE__, __LINE__); exit(EXIT_FAILURE); } for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { img[3 * (w * y + x) + 0] = 255; // R img[3 * (w * y + x) + 1] = 255 * x / w; // G img[3 * (w * y + x) + 2] = 255 * y / h; // B print_ppm(img, w, h, UCHAR_MAX); free(img); return EXIT_SUCCESS; malloc で確保したメモリは 普通に1次元配列のように使える a.bmp mintty + bash + GNU C $ gcc dynamic_array_test.c print_ppm.c && ./a | convert - a.bmp w = ? 255 h = ? 255

動的配列によるカラー画像 1次元配列を3次元配列のように使う 講義資料 第6週pp.10-30,57-59. dynamic_array_test.c unsigned char *img; を unsigned char img[h][w][3]; と同じように使う 18 19 20 21 22 23 24 for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { img[3 * (w * y + x) + 0] = 255; // R img[3 * (w * y + x) + 1] = 255 * x / w; // G img[3 * (w * y + x) + 2] = 255 * y / h; // B } img[3 * (w * y + x) + 0] x img R G B ... : ⋱ y h w*3

テキスト形式 PPM 画像の書き出し 第6週p.53.のプログラムを関数化した例 講義資料 第6週pp.47-53. print_ppm.c 関数のプロトタイプ宣言 #include <stdio.h> void print_ppm(unsigned char *img, int w, int h, int q) { int c, x, y; printf("P3\n"); printf("%d %d\n", w, h); printf("%d\n", q); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { printf(" "); for (c = 0; c < 3; c++) { printf(" %3d", img[3 * (w * y + x) +c]); } printf("\n"); // 作った関数の本体(並括弧の中身)を取り除き // 末尾に ; (セミコロン)を付ける void print_ppm(unsigned char *img, int w, int h, int q); x c R G B ... : ⋱ y

テキスト形式 PPM 画像の読み込み 読み込んだパラメータに応じて大きさを変更 講義資料 第6週pp.47-53. read_ppm_p3_core.c 実用上は、これ+エラー処理が必要 詳細は read_ppm_p3.c 参照 unsigned char *read_pnm_p3(int *w, int *h, int *q) { char s[10]; unsigned char *img; int y, x, c; scanf("%9s%d%d%d", s, w, h, q); if ((img = calloc(*h * *w * 3, 1)) == NULL) { return NULL; } for (y = 0; y < *h; y++) { for (x = 0; x < *w; x++) { for (c = 0; c < 3; c++) { scanf("%d", &img[3 * (*w * y + x) + c]); return img; パラメータの読み込み パラメータに応じて 動的配列の確保 配列に格納する値の 読み込み 確保し値を読み込んだ 動的配列を返す

添え字0~N-1の範囲を自由変更出来ないか? ポインタの応用: 配列の添え字調整 C言語における配列の宣言 型名 変数名[要素数]; 例: 要素数N個でint型の配列a int a[N]; 利用可能な要素は a[0]~a[N-1] 添え字0~N-1の範囲を自由変更出来ないか? ↓ ポインタを利用すれば可能

ポインタの応用: 配列の添え字調整 1元配列とメモリ上の配置 ⋮ a[-1] a[ 0] a[ 1] a[ 2] a[ 3] ⋮ p[-1] ... 確保されたメモリ 確保されたメモリ外 p[0] が a のどこに対応するか 調整してやれば良い = = int a[3]; int *p; として p = &a[0] とした場合 p = &a[1] とした場合 a[ 0]~a[ 2] を p[-1]~p[ 1] として使える

ポインタの応用: 配列の添え字調整 オフセットを与えてポインタを格納する array_offset_test1.c p は a[1] のアドレスを指す つまり p[0] が a[1] 結果、以下の要素が対応する a[ 0], a[ 1], a[ 2] p[-1], p[ 0], p[ 1] int a[3] = {2,3,5}; int *p = a + 1; int i; for (i = 0; i < 3; i++) { printf("%p: a[% d] = %d\n", &a[i], i, a[i]); } for (i = -1; i <= 1; i++) { printf("%p: p[% d] = %d\n", &p[i], i, p[i]); mintty + bash + GNU C $ gcc array_offset_test1.c && ./a 0x22aab0: a[ 0] = 2 0x22aab4: a[ 1] = 3 0x22aab8: a[ 2] = 5 0x22aab0: p[-1] = 2 0x22aab4: p[ 0] = 3 0x22aab8: p[ 1] = 5

ポインタの応用: 配列の添え字調整 2次元配列の場合 多次元配列も 結局は1次元の メモリアドレスに 割り振られる 2次元配列とメモリ上の配置 ⋮ a[ 0][ 0] a[ 0][ 1] a[ 0][ 2] a[ 1][ 0] a[ 1][ 1] a[ 1][ 2] a[ 2][ 0] a[ 2][ 1] a[ 2][ 2] ⋮ a[ 0][ 0] a[ 0][ 1] a[ 0][ 2] a[ 0][ 3] a[ 0][ 4] a[ 0][ 5] a[ 0][ 6] a[ 0][ 7] a[ 8][ 8] ⋮ a[ 1][-3] a[ 1][-2] a[ 1][-1] a[ 1][ 0] a[ 1][ 1] a[ 1][ 2] a[ 1][ 3] a[ 1][ 4] a[ 1][ 5] ⋮ a[ 2][-6] a[ 2][-5] a[ 2][-4] a[ 2][-3] a[ 2][-2] a[ 2][-1] a[ 2][ 0] a[ 2][ 1] a[ 2][ 2] ... 確保されたメモリ 添え字の範囲外だが 確保されたメモリ内 確保されたメモリ外 = 添え字の範囲外でも メモリ上で連続であれば アクセスは可能

ポインタの応用: 配列の添え字調整 2次元配列の場合 2次元配列とポインタのオフセット ⋮ a[ 0][ 0] a[ 0][ 1] a[ 0][ 2] a[ 1][ 0] a[ 1][ 1] a[ 1][ 2] a[ 2][ 0] a[ 2][ 1] a[ 2][ 2] ⋮ (*p)[-1][-1] (*p)[-1][ 0] (*p)[-1][ 1] (*p)[ 0][-1] (*p)[ 0][ 0] (*p)[ 0][ 1] (*p)[ 1][-1] (*p)[ 1][ 0] (*p)[ 1][ 1] int [3][3] へのポインタ p を作り p が a[ 1][ 1] を指すよう調整すれば a[ 0][ 0]~ a[ 2][ 2] を (*p)[-1][-1]~(*p)[ 1][ 1] として使える! = int a[3][3]; int (*p)[3][3]=(int (*)[3][3])&a[1][1];

ポインタの応用: 配列の添え字調整 2次元配列の場合 2次元配列とポインタのオフセット p = a とした時の (*p)[-1][-1]~(*p)[1][1]の範囲 ⋱ ⋮ ⋰ ... a[-1][-1] a[-1][ 0] a[-1][ 1] a[-1][ 2] a[-1][ 3] a[ 0][-1] a[ 0][ 0] a[ 0][ 1] a[ 0][ 2] a[ 0][ 3] a[ 1][-1] a[ 1][ 0] a[ 1][ 1] a[ 1][ 2] a[ 1][ 3] a[ 2][-1] a[ 2][ 0] a[ 2][ 1] a[ 2][ 2] a[ 2][ 3] a[ 3][-2] a[ 3][ 0] a[ 3][ 1] a[ 3][ 2] a[ 3][ 3] ... 確保されたメモリ 添え字の範囲外だが確保されたメモリ内 確保されたメモリ外 p = &a[1][1] とした時の (*p)[-1][-1]~(*p)[1][1]の範囲

ポインタの応用: 配列の添え字調整 2次元配列の場合 オフセットを与えてポインタを格納する array_offset_test2_1.c a[1][1]は int[3][3]型ではなく int型なのでキャストが必要 int a[3][3] = { { 2, 3, 5}, { 7,11,13}, {17,19,23}, }; int (*p)[3][3] = (int (*)[3][3]) &a[1][1]; int x, y; printf("sizeof(a) = %d\n", sizeof(a)); printf("sizeof(*p) = %d\n", sizeof(*p)); for (y = 0; y < 3; y++) { for (x = 0; x < 3; x++) { printf("%p: a[% d][% d] = %d\n", &a[y][x], y, x, a[y][x]); } for (y = -1; y <= 1; y++) { for (x = -1; x <= 1; x++) { printf("%p: (*p)[% d][% d] = %d\n", &(*p)[y][x], y, x, (*p)[y][x]); int [3][3] へのポインタは int (*)[3][3]

ポインタの応用: 配列の添え字調整 2次元配列の場合 オフセットを与えてポインタを格納する array_offset_test2_2.c int a[3][3] = { { 2, 3, 5}, { 7,11,13}, {17,19,23}, }; int (*p)[3] = (int (*)[3]) &a[1][1]; int x, y; printf("sizeof(a) = %d\n", sizeof(a)); printf("sizeof(*p) = %d\n", sizeof(*p)); for (y = 0; y < 3; y++) { for (x = 0; x < 3; x++) { printf("%p: a[% d][% d] = %d\n", &a[y][x], y, x, a[y][x]); } for (y = -1; y <= 1; y++) { for (x = -1; x <= 1; x++) { printf("%p: p[% d][% d] = %d\n", &p[y][x], y, x, p[y][x]); int [3][3] へのポインタは int (*)[3][3] だが int (*)[3] でも格納可能 使う時は *p としなくて良いので 後者の方が使い易い? 1次元配列でも int [3] への ポインタではなく int 型への ポインタを 使っていた

ポインタの応用: 配列の添え字調整 2次元配列の場合 mintty + bash + GNU C mintty + bash + GNU C $ gcc array_offset_test2_1.c && ./a sizeof(a) = 36 sizeof(*p) = 36 0x22aa90: a[ 0][ 0] = 2 0x22aa94: a[ 0][ 1] = 3 0x22aa98: a[ 0][ 2] = 5 0x22aa9c: a[ 1][ 0] = 7 0x22aaa0: a[ 1][ 1] = 11 0x22aaa4: a[ 1][ 2] = 13 0x22aaa8: a[ 2][ 0] = 17 0x22aaac: a[ 2][ 1] = 19 0x22aab0: a[ 2][ 2] = 23 0x22aa90: (*p)[-1][-1] = 2 0x22aa94: (*p)[-1][ 0] = 3 0x22aa98: (*p)[-1][ 1] = 5 0x22aa9c: (*p)[ 0][-1] = 7 0x22aaa0: (*p)[ 0][ 0] = 11 0x22aaa4: (*p)[ 0][ 1] = 13 0x22aaa8: (*p)[ 1][-1] = 17 0x22aaac: (*p)[ 1][ 0] = 19 0x22aab0: (*p)[ 1][ 1] = 23 $ gcc array_offset_test2_2.c && ./a sizeof(a) = 36 sizeof(*p) = 12 0x22aa90: a[ 0][ 0] = 2 0x22aa94: a[ 0][ 1] = 3 0x22aa98: a[ 0][ 2] = 5 0x22aa9c: a[ 1][ 0] = 7 0x22aaa0: a[ 1][ 1] = 11 0x22aaa4: a[ 1][ 2] = 13 0x22aaa8: a[ 2][ 0] = 17 0x22aaac: a[ 2][ 1] = 19 0x22aab0: a[ 2][ 2] = 23 0x22aa90: p[-1][-1] = 2 0x22aa94: p[-1][ 0] = 3 0x22aa98: p[-1][ 1] = 5 0x22aa9c: p[ 0][-1] = 7 0x22aaa0: p[ 0][ 0] = 11 0x22aaa4: p[ 0][ 1] = 13 0x22aaa8: p[ 1][-1] = 17 0x22aaac: p[ 1][ 0] = 19 0x22aab0: p[ 1][ 1] = 23 sizeof(*p)は異なる int (*p)[3][3]; だと sizeof(*p) は sizeof(int[3][3]) int (*p)[3]; だと sizeof(int[3])

ポインタの応用: 配列の添え字調整 2次元配列の場合 配列のサイズが必要になるのが欠点 array_offset_test2_2.c int a[3][3] = { { 2, 3, 5}, { 7,11,13}, {17,19,23}, }; int (*p)[3] = (int (*)[3]) &a[1][1]; int x, y; printf("sizeof(a) = %d\n", sizeof(a)); printf("sizeof(*p) = %d\n", sizeof(*p)); for (y = 0; y < 3; y++) { for (x = 0; x < 3; x++) { printf("%p: a[% d][% d] = %d\n", &a[y][x], y, x, a[y][x]); } for (y = -1; y <= 1; y++) { for (x = -1; x <= 1; x++) { printf("%p: p[% d][% d] = %d\n", &p[y][x], y, x, p[y][x]); 最後の次元以外は要素数が既知でないと 配列へのポインタを作れない つまり動的配列に対して使えない

ポインタの応用: 配列の添え字調整 2次元配列の場合 動的配列の場合は自力でアドレスを計算する array_offset_test3.c int a[3][3] = { { 2, 3, 5}, { 7,11,13}, {17,19,23}, }; int *p = &a[1][1], w = 3, h = 3; int x, y; for (y = 0; y < 3; y++) { for (x = 0; x < 3; x++) { printf("%p: a[% d][% d] = %d\n", &a[y][x], y, x, a[y][x]); } for (y = -1; y <= 1; y++) { for (x = -1; x <= 1; x++) { printf("%p: p[w * % d + % d] = %d\n", &p[w * y + x], y, x, p[w * y + x]); ポインタを1次元配列として用いる 但しアドレスを計算し易いオフセットを あらかじめ設定しておく アドレスは自力で計算

確認問題 以下の状況で9行目の時点の *p と p の値を16進数で答えよ。 第9週講義資料p.16. hoge.c 7 8 9 int a = 0x12345678; int *p = &a; printf("&a = %p\n", &a); mintty + bash + GNU C $ gcc hoge.c && ./a &a = 0x22aac4

確認問題 int 型へのポインタ変数 a, b を宣言する場合正しいのは以下のうちどれか? 第9週講義資料p.17. hoge.c

演習: strtosign.c 文字列sの先頭1文字を見て、「-」なら-1、「+」なら+1、それ以外なら+1を返す関数 strtosign を作成せよ int strtosign(const char *s, char **endp); 引数 s: 文字列 endp: NULL 以外の時、*endp に符号の識別子(つまり'-','+')の次の文字へのポインタを返す 戻り値 文字列sの先頭2文字に応じて、2、8、10、16の何れかを返す。 endp が NULL 以外の時、*endp に符号の識別子(つまり'-','+')の次の文字へのポインタを返す strtosign_test.c と共にコンパイルして動作を確認せよ 第13週へ移動+改訂

演習: strtobase.c 文字列sの先頭2文字を見て、0なら8進数、0bなら2進数、0xなら16進数、それ以外なら10進数と判別する関数 strtobase を作成せよ int strtobase(const char *s, char **endp); 引数 s: 文字列 endp: NULL 以外の時、*endp に基数判別の識別子(0, 0b, 0x)の次の文字へのポインタを返す 戻り値 文字列sの先頭2文字に応じて、2、8、10、16の何れかを返す。 endp が NULL 以外の時、*endp に基数判別の識別子(0, 0b, 0x)の次の文字へのポインタを返す strtobase_test.c と共にコンパイルして動作を確認せよ 第13週へ移動+改訂

参考文献 [1] B.W.カーニハン/D.M.リッチー著 石田晴久 訳、プログラミング言語C 第2版 ANSI 規格準拠、共立出版(1989)