情報基礎演習B 後半第5回 担当 岩村 TA 谷本君
前回の復習1 構造体 新しい変数の型を定義する メンバの参照 ドットを用いる 変数名.メンバ
前回の復習2 ファイルポインタの宣言 FILE *fp; ファイルのオープン fp=fopen(“ファイル名”, “モード”); r: 読み込み(ファイルがなければエラー) w: 書き込み(既にファイルがあれば破棄して上書き) a: 追加(ファイルがあれば。なければwと同じ) ファイルのクローズ fclose(fp);
先週の課題 任意の人数の名前、身長、体重をファイルから読み込み、BMIを計算して出力するプログラムを作成しなさい データファイルは用意してあるものを使う データファイルの構造 人数、(1人目の)名前、身長、体重、 (2人目の)名前、身長、体重… ただし身長の単位はcm、体重はkg
課題の解答
後半の予定 関数 ポインタ(前半) ポインタ(後半) & メモリの動的確保 構造体 & ファイルの入出力 プリプロセッサ & 復習+α (おまけ)文字列処理 & コマンドライン引数の処理
プリプロセッサ コンパイル前にプログラムを書き換える処理 例) #include <stdio.h> #define N 50
ファイルの挿入(#include) 関数は使う前に定義が必要 /* stdio.h */ printfの定義… fprintfの定義… 修正後のプログラム /* stdio.h */ printfの定義… fprintfの定義… scanfの定義… fscanfの定義… /* stdio.h */ printfの定義… fprintfの定義… scanfの定義… fscanfの定義… #include<stdio.h> int main(void){ printf(“hello!!\n"); return(0); } int main(void){ printf(“hello!!\n"); return(0); } コンパイル前に プログラムを修正
マクロ定義 (#define) 文字列の置き換え マクロの定義 #define N 50 #define wa(a,b) a+b 副作用があるので注意が必要
文字列の置き換え コンパイル前に文字列を置き換える 書式 例 効能 #define 置き換える文字列 置き換えられる文字列 #define N 50 #define FILENAME “data.txt” 効能 後で数値やファイル名などを変えたいとき、一括して変えられる
マクロの定義 コンパイル前に文字列を置き換えて、関数のような処理を行う 例) #define wa(a,b) a+b printf("wa(x,y) = %f\n",wa(x,y)); 文字列の置き換えによって wa(x,y)x+y を実現
サンプルプログラム1(マクロの定義) #define wa(a,b) a+b #include<stdio.h> int main(void){ float x, y; printf("x: "); scanf("%f", &x); printf("y: "); scanf("%f", &y); printf("wa(x,y) = %f\n",wa(x,y)); return(0); }
マクロの定義と関数の違い 関数との違い 変数の型がない 余計な処理時間がかからない 注意しないと意図した動作をしない場合がある(副作用) 単にソースコードを変更しただけなので 余計な処理時間がかからない 普通関数を呼び出すと多少時間がかかる マクロは単にソースコードを変更しただけ 注意しないと意図した動作をしない場合がある(副作用)
マクロの定義の副作用 プログラム #define kake(a,b) a*b printf("wa(x,y) = %f\n",kake(x,y+10)); マクロの展開 kake(x,y+10)x*y+10 ≠x*(y+10) 括弧がない!
これまでの復習+α ポインタについて アドレスを扱う変数 関数の呼び出し方法の違い 値を渡す アドレスを渡す(配列を渡す) 配列の先頭のアドレスを関数に渡せば、配列を関数に渡したことになる
ポインタ アドレスを扱う変数 具体的には、変数が格納されているメモリの先頭番地を記憶する
動的な配列の確保(malloc) ポインタを用意すれば、mallocが配列を確保してくれる 一方、配列だと… 関数の先頭でなくてもよい 配列の大きさはプログラムの実行後に決定可能 一方、配列だと… 関数の先頭でないと宣言できない 配列の大きさはコンパイル時に決定(定数のみ) 配列でよくある間違い int n; int a[n]; nは変数! これはmallocでしか実現できない
動的な配列の確保(malloc)の動作
動的でない配列(int a[3]) a自体がポインタみたいなもの
関数への値の渡し方 関数wa main関数 a,bの値をx,yにコピー int wa(int x, int y) { int z; z=x+y; return z; } … sum=wa(a, b); zの値をsumにコピー 関数wa(参照渡しversion) main関数 void wa(int *z, int x, int y) { *z=x+y; } … wa(&sum, a, b); sumの中身を変更 sumのアドレスをzにコピー
サンプルプログラム(関数へ配列を渡す) a = (int *)malloc(sizeof(int)*n); ポインタを渡す=配列を渡す #include <stdio.h> int wa_all(int n, int *a) { int i, t=0; for (i=0; i<n; i++) { t += a[i]; } return(t); int main(void) { int *a, n=5, total, i; a = (int *)malloc(sizeof(int)*n); if (a==NULL) { printf("Cannot allocate memory\n"); exit(1); } for (i=0; i<n; i++) { a[i] = i; total = wa_all(n, a); printf("Total: %d\n", total); free(a); return(0); ポインタを渡す=配列を渡す
配列を関数へ渡す方法 mallocで配列を確保した場合 int a[5]で配列を確保した場合 int wa_all(int n, int *a) { int i, t=0; for (i=0; i<n; i++) { t += a[i]; } return(t); int *a, n=5, total; a = (int *)malloc(sizeof(int)*n); … total = wa_all(n, a); free(a); int a[5]で配列を確保した場合 int wa_all(int n, int a[]) { int i, t=0; for (i=0; i<n; i++) { t += a[i]; } return(t); int a[5], n=5, total; … total = wa_all(n, a);