構造体(struct) 配列では、複数のデータをひとまとまりにして操作する ことが出来る。しかし、それぞれのデータは同じ型(例えば

Slides:



Advertisements
Similar presentations
システムプログラミング 第7回、8回 ファイルシステム関連の システムコール
Advertisements

プログラミング入門2 第4回 配列 for文 変数宣言 初期化
情報基礎演習B 後半第5回 担当 岩村 TA 谷本君.
数理情報工学演習第一C プログラミング演習 (第3回 ) 2014/04/21
プログラミング入門2 第10回 動的な領域確保 情報工学科 篠埜 功.
第12回新しい型と構造体.
第13回構造体.
データ構造とアルゴリズム 第10回 mallocとfree
解答 1 複素数を構造体として定義し、二つの複素数の積(結果は複素数)を返す 関数 を定義せよ。
第12回構造体.
プログラミング入門2 第10回 構造体 情報工学科 篠埜 功.
プログラミング入門2 第10回 構造体 情報工学科 篠埜 功.
システムプログラミング 第5回 情報工学科 篠埜 功 ヒアドキュメント レポート課題 main関数の引数 usageメッセージ
問題提起その 1 一文字ずつ文字(数字)を読み込み、それぞれの文字が何回入力されたかを数えて出力するプログラム。
第13回 プログラミングⅡ 第13回
記憶クラス 変数をどのような記憶領域に割り当てるかを指定するのが記憶クラス 記憶クラスには、自動変数、静的変数、外部変数などがある。
第8回 プログラミングⅡ 第8回
構造体.
プログラミング演習Ⅰ 課題2 10進数と2進数 2回目.
精密工学科プログラミング基礎 第9回資料 (12/11 実施)
ファイル操作と文字列の利用.
プログラミング 3 構造体(1).
第10回 プログラミングⅡ 第10回
10: ファイル入出力 C プログラミング入門 基幹2 (月4) Linux にログインし、以下の講義ページ を開いておくこと
ちょっとした練習問題① 配列iroを['R', 'W', 'R', 'R', 'W' , 'W' , 'W']を宣言して、「W」のときの配列の番号をprintfで表示するようなプログラムを記述しなさい。
Cプログラミング演習 第6回 ファイル処理と配列.
プログラミング 2 ファイル処理.
プログラミング論 ファイル入出力
関数の定義.
情報・知能工学系 山本一公 プログラミング演習Ⅱ 第2回 ファイル処理 情報・知能工学系 山本一公
プログラミング演習I 2003年6月25日(第10回) 木村巌.
2005年度 データ構造とアルゴリズム 第3回 「C言語の復習:再帰的データ構造」
プログラミング入門2 第11回 情報工学科 篠埜 功.
第13章 文字の取り扱い方 13.1 文字と文字型変数 13.2 文字列 13.3 文字型配列への文字列の代入
第13章 文字の取り扱い方 13.1 文字と文字型関数 13.2 文字列 13.3 文字型配列への文字列の代入
プログラミング入門2 第11回 情報工学科 篠埜 功.
第7回 プログラミングⅡ 第7回
復習 前回の関数のまとめ(1) 関数はmain()関数または他の関数から呼び出されて実行される.
プログラミング言語論 第五回 理工学部 情報システム工学科 新田直也.
プログラミング論 ファイル入出力
第11回 プログラミングⅡ 第11回
システムプログラミング 第7回、8回 ファイルシステム関連の システムコール
精密工学科プログラミング基礎Ⅱ 第4回資料 今回の授業で習得してほしいこと: 文字列の扱い ファイル入出力の方法 コマンドライン引数の使い方
第14章 ファイル操作 14.1 ファイルへの書き込み 14.2 ファイルからの読み込み 14.3 ファイルへの追加書き込み
第4回 ファイル入出力方法.
プログラミング入門2 第10回 構造体 情報工学科 篠埜 功.
システムプログラミング 第7回、8回 ファイルシステム関連の システムコール
メモリとメモリアドレス, ポインタ変数,関数へのポインタ渡し
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
プログラミング言語論 第六回 理工学部 情報システム工学科 新田直也.
第14章 ファイル操作 14.1 ファイルへの書き込み 14.2 ファイルからの読み込み 14.3 ファイルへの追加書き込み
C言語 はじめに 2016年 吉田研究室.
アルゴリズムとプログラミング (Algorithms and Programming)
プログラミング演習I 2003年7月2日(第11回) 木村巌.
ポインタとポインタを用いた関数定義.
第13章 文字の取り扱い方 13.1 文字と文字型変数 13.2 文字列 13.3 文字型配列への文字列の代入
ファイルの読み込み, ファイルからのデータの取り出し, ファイルの書き出し
11.1 標準ライブラリ関数 11.2 関数呼び出しのオーバーヘッド 11.3 大域変数 11.4 プロトタイプ宣言 11.5 関数引数
第5回 プログラミングⅡ 第5回
オブジェクト指向言語論 第五回 知能情報学部 新田直也.
プログラミング入門2 第5回 配列 for文 変数宣言 初期化
モジュール分割.
プログラミング演習I 2003年6月11日(第9回) 木村巌.
四則演算,変数 入力文,出力文,代入文, ライブラリ関数
プログラミング演習II 2004年11月 2日(第3回) 理学部数学科・木村巌.
コンピュータープログラミング (C言語)(10) 1.ファイル入出力
プログラミング入門2 第5回 配列 変数宣言、初期化について
printf・scanf・変数・四則演算
第14章 ファイル操作 14.1 ファイルへの書き込み 14.2 ファイルからの読み込み 14.3 ファイルへの追加書き込み
プログラミング 2 静的変数.
Presentation transcript:

構造体(struct) 配列では、複数のデータをひとまとまりにして操作する ことが出来る。しかし、それぞれのデータは同じ型(例えば 整数、あるいは浮動小数点数)出なければならない。 型の違うデータをひとまとまりにして扱う方法に、構造体 がある。 構造体 配列 文字 文字 文字 文字 文字 名前 (文字列) 0 1 2 3 整数 成績(理科)(整数) 学籍番号(整数) 成績(数学)(整数) 浮動小数点数 成績(英語)(整数) 身長(小数点数)

構造体の宣言 構造体を宣言するには、struct宣言を使う。 構造体の要素(メンバ)について、それぞれ変数の型と 名前を付ける。 struct four{ int a; int b; double c; double d; }; struct four x; typedef struct four Four; Four y; 構造体としてfourを定義 変数の型としてFourを定義 毎回 struct four と書くのは 面倒だが、このようにすると Four という型が定義される。

typedef宣言 構造体を値としてもつ変数を宣言するには、 struct 構造体名 変数名; のように、structをつける必要がある。 struct 構造体名 までがintやdoubleのような変数の型名となる。毎回structを つけるのは面倒ですが、これを簡略する方法としてtypedef宣言がある。    typedef 型名 別名; の様に、ある型名に対して、別名をつけることが出来る。 struct complex{ double real; double imag; }; typedef struct complex Complex; Complexをintやdoubleの様に 変数の型として使用することが出来る Complex a, b; Complex x[10];

typedef 宣言で別名をつける型としては、構造体の名前でなくとも よい。例えば、 typedef int int32; int32 a, b, c; とすれば、整数型(int)に別名(int32)をつけることができる。 また、 typedef int vector[3]; と宣言すると、vectorはintの配列(サイズ3)になる。 vector a, b, c; /* a,b,cは配列として宣言される*/ a[1] = 10;

構造体の参照 構造体の要素を参照するには、構造体の変数名にドットつけて、 そのあと要素名をつける。 構造体としてfourを定義 struct four{ int a; int b; double c; double d; }; struct four x、y; x.a = 10; x.d = 3.1415926;   printf(“a = %d\n”, x.a);  y = x; 構造体としてfourを定義 構造体を値とする変数x、yを宣言 構造体の要素aとdに値を代入 構造体 x全体をyに代入する

配列との違い 配列は、要素として同じ型の変数しか取れない。 配列の要素の参照は、添字を付けて a[0], a[1],... 構造体では要素名を指定して、 x.a, x.c, ... 構造体では、全体を代入する事が出来る。 int a[5] = {1,2,3,3,5}; int b[5]; a = b; これはダメ! 構造体へ初期値を 代入するのは配列 と同じように{}で 囲む。要素の型と 対応するように指定 する。 struct foo { int a,b,c; } x, y={1,2,3}; x = y; これはOK!

構造体の例 struct complex{ double real; double imag; }; struct complex x = {1.0, 0.0}; struct complex y = {0.0, 1.0}; struct complex z; z.real = x.real * y.real - x.imag * y.imag; z.imag = x.real * y.imag + x.imag * y.real; struct complex xx[10]; 10 個の構造体からなる配列

メンバに構造体を持つ構造体 構造体のメンバとしては、int型やdouble型だけでなく、 配列や構造体、あるいはそれらのポインタであっても構わない。 例えば、(x,y)で点(point)を表し、3点で三角形を 表すことを考えると struct point { double x, y; }; struct triangle { struct point a, b, c; struct triangle A = {{0.0,0.0},{1.0, 0.0}, {0.0, 1.0}}; 3点a,b,cで三角形 を表す

自分自身をメンバに持つ構造体 構造体のメンバとして、既に宣言されている変数型 や構造体以外のものは許されない。特に、自分自身を メンバとして持つことはゆるされない。 しかし、ポインタ型としてのメンバであれば、許される。 struct cell { int data; struct cell next_cell; }; これは許されない struct cell { int data; struct cell *next_cell; }; これなら許される

関数への構造体の受け渡し 構造体を関数の引数として渡すには、値渡しと参照渡しの二つの方法がある。 複素数の絶対値を計算する関数 (値渡し) struct complex { double real, imag; }; typedef struct complex Complex; double cabs(Complex a) { double d; d = a.real * a.real + a.imag*a.imag; d = sqrt(d); return d; } Complex conjugate(Complex a) { Complex b; b.real = a.real; b.imag = -a.imag; return b;

参照渡し v->x は (*v).x と書くのと同じ。 構造体のポインタはよく使われる ので、要素を参照する為に特別な struct vector { double x,y,z; }; typedef struct vector Vector; main() { Vector v={0.0, 1.1, 3.2} double sum; sum = fn(&v); printf("%f\n", sum); } double fn(Vector *v) double sum=0; sum = v->x + v->y + v->z; return sum; ベクトル要素の合計を返却する関数             v->x    は    (*v).x と書くのと同じ。 構造体のポインタはよく使われる ので、要素を参照する為に特別な 記号を用意してある。

ファイルへの入出力 これまでは、データの入出力は標準入出力(キーボード及びコンソール)を用いてきた(シェルのリダイレクションを含む)が、入出力はファイル経由でも可能。 外部記憶装置(ハードディスクなど)に格納されたデータの集合をファイルという。ファイルを扱うために FILE という型が stdio.h に定義されている。 テキストファイル(text file)とバイナリファイル(binary file) 内容が文字と改行文字などそのまま表示可能なファイルをテキストファイル、データがバイト列となっているものをバイナリファイルという。ここではテキストファイルのみを取り扱う。 ファイルの入出力を行うには、事前、事後の操作が必要。 1) ファイルのオープン(ファイルを開く) 2) ファイルへの入出力 3) ファイルのクローズ(ファイルを閉じる)

ファイルのオープン ファイル型へのポインタ変数宣言 FILE *fp; ファイルのオープンには、ライブラリ関数 fopen() を用いる。 宣言しただけでは具体的なファイルを指していない。 ファイルのオープンには、ライブラリ関数 fopen() を用いる。 fp = fopen("data", "w"); ファイル名 モード "w" 書き込みモード(ファイルを新規作成) "r" 読み込みモード(ファイルが存在しないとエラー) 上記の例では、新規に data という名前のファイルが作成され、ファイルへのポインタ fp はこのファイルを指す。以後は fp を介してファイル入出力等を行う。ファイル data が既存の場合、内容は消去されて新規ファイル が作成される。

ファイル名の指定 文字列リテラルとしてファイル名を指定し、ファイルをオープンする例 FILE *fp; char file_name[]="data"; fp = fopen(file_name, "w"); 文字型配列として宣言と同時に初期化 FILE *fp; char file_name[100]; scanf("%s", file_name); fp = fopen(file_name, "w"); 文字型配列として宣言 scanf() で文字列として入力 文字列=文字型配列の操作により、ファイル名をプログラム内で変更可能。

ファイルオープン時のエラー処理 ファイルオープンに失敗すると fopen() は特殊な値 NULL を返す。下例のように、オープンに失敗した場合、ライブラリ関数 exit() でプログラムを正常に終了。 if( (fp = fopen("data", "w")) == NULL ){ printf("Can’t open file!\n"); exit(); } 書き込みモードでファイルを開く場合、ディスク容量が一杯、もしくは、読み込みモードで指定したファイルが存在しない等の理由でファイルオープンが失敗する場合がある。エラー処理を行わないと、ファイルへの入出力の時点で Bus error 等のプログラムの異常終了が起こる。 ライブラリ関数 exit() はプログラムの実行を終了する。

ファイルへの入出力 ライブラリ関数 fprintf(), fscanf() を用いた入出力 それぞれ、標準入出力に関する printf(), scanf() に相当する fprintf(fp, "socre is %d\n", i); ファイルポインタ fp が指すファイルへ書式付き出力 fscanf(fp, "%d", &data); ファイルポインタ fp が指すファイルからデータを整数値として読み込み、変数 data に格納。 使い方は printf(), scanf() と同じ。

ファイルのクローズ オープンしたファイルはプログラム終了時にクローズする必要がある ファイルのクローズ fclose(fp); 一般にプログラムが終了する直前でファイルを閉じる

具体例 1 main() { FILE *fp; fp = fopen("data", "w"); fprintf(fp, "Hello!\n"); fclose(fp); } data という名前のファイルを新規作成し、 文字列 “Hello!\n” を書き込む。 開いたファイルはちゃんと閉じること。 実行結果は左の通り。data というファイルが出来ていて、内容は Hello!\n である。 % ./a.out % cat data Hello! %

具体例 2 main() { FILE *fp; int score; fp = fopen("data", "r"); while( fscanf(fp, "%d", &score)!=EOF ){ printf("%d\n", score); } fclose(fp); 既存のファイル data を読み込みモード "r" で開き、ファイルに書かれているデータを整数値として読み込んで表示。 書き込みモード "w" でファイルを開くと、ファイルが新規作成されて中身が消えてしまうので注意! ファイルオープンの際、エラー処理をしていないので、data というファイルが存在しないと実行時エラーで異常終了する。

ファイル入出力続き ファイルから 1 文字ずつ文字を読み込むライブラリ関数 getc(), putc() 標準入出力に関する getchar(), putchar() に相当 int getc(FILE *fp); fp が指すファイルから 1 文字読み込み、文字コード(int)を返す int putc(int c, FILE *fp); fp が指すファイルへ文字コード c に対応する文字を書き込む。書き込み時にエラーが起こると EOF を返す

具体例 3 テキストファイル data_original の内容を別のファイル data_copy にコピーするプログラム main() { FILE *fp_in, *fp_out; int code; fp_in =fopen("data_original", "r"); fp_out=fopen("data_copy", "w"); while( (code=getc(fp_in)) != EOF )} putc(code, fp_out); } fclose(fp_in); fclose(fp_out); コピー元のファイルは読込みモードでオープンすること。 コピー元のファイルから 1 文字ずつ読み込んで、コピー先のファイルへ書きだす。

記憶クラス 変数をどのような記憶領域に割り当てるかを指定するのが記憶クラス 記憶クラスには、自動変数、静的変数、外部変数などがある。 今までの変数宣言はすべて、変数を自動変数( auto型)として宣言 auto int a; を省略して int a; と宣言 記憶クラス指定子 auto で自動変数を指定(省略可) 自動変数は、それが宣言されている関数が呼び出された時点で生成され、 関数の呼び出しが終了した時点で消滅する。 自動変数はメモリ上のスタックに格納される。

変数の生成と消滅 y y x x j j i i main() { int i,j; func_1(); ... func_2(); } void func_1(void) int x, y; .... void func_2(void) 局所変数 x, y の生成 局所変数 x, y の生成 消滅 消滅 y y x x メモリ空間 func_1 呼出 func_1 終了 func_2 呼出 func_2 終了 j j i i 時間 変数 i, j の寿命 変数 x, y の寿命 変数 x, y の寿命 main 文の開始 局所変数 i, j の生成 main 文の終了 局所変数 i, j の消滅

静的変数 static int x; 記憶クラス指定子 static を用いて、変数が静的変数であることを指定する 静的変数はメモリの静的領域に格納される。 静的変数は初期化の指定がない場合、ゼロで初期化される。 静的変数の初期化はコンパイル時に一度だけ行われる。 静的変数に対して、自動変数は関数の終了で消滅するため、プログラムの 実行中、値を保持することが出来ない。 プログラムの実行中常に値を保持するには静的変数を用いる。

静的変数の寿命 main() { int i; func_1(); func_2(); } void func_1(void) 自動変数 static int a; int b; .... void func_2(void) static int x; int y; main 文の局所変数 i main 文開始 生成 main 文終了 消滅 自動変数 func_1 の局所変数 b func_2 の局所変数 y func_1 呼び出し func_1 終了 func_2 呼び出し func_2 終了 func_1 の局所変数 a func_2 の局所変数 x 静的変数 main 文開始 生成 main 文終了 消滅

静的変数を用いた例 void count(void) % ./a.out main() 1 { 2 count(); % } static int x=0; printf(“%d\n”, x); x++; % ./a.out 1 2 % 関数 count 中の静的変数 x の値は、関数呼び出しが終わっても消滅しないで保持されている。 従って、呼び出されるごとに x の値は 1 増えるが変数 x は消滅しない。 静的変数の初期化はコンパイル時に一度だけ実行される % ./a.out % x を自動変数として宣言すると、関数呼びだし毎に x は生成され値は 0 に初期化される。実行結果は、

外部変数 関数の外で定義される変数を外部変数(大域変数)と呼ぶ。 外部変数のスコープは広域的(プログラム全体)。全ての関数で参照可能。 int a=10; double x=3.14159; main() { printf(“a = %d\n”, a); sample(); printf(“%f\n”, x); } void sample(void) x += 1.0; 外部変数 a, x の宣言。変数 a, x はどの関数(main 文を含む)からも利用可能。 外部変数はどの関数からも参照可能なの で、関数を独立したブラックボックスと して定義することが困難になる。 変数の隠ぺいができないため、外部変数の乱用は避けるべき。

問題 1 複素数を構造体として定義し、二つの複素数の積(結果は複素数)を返す 関数 cmult を定義せよ。 #include <stdio.h> struct complex { double real, imag; }; typedef struct complex Complex; Complex cmult(Complex, Complex); main() { Complex x = {1.0, 1.0}; Complex y = {1.414, 1.414}; Complex z; z = cmult(x,y); printf(“z = %f + %fi\n”, z.real, z.imag); }

問題 2 学生のデータを扱う為の構造体を定義せよ。この構造体の値を設定する ための入力関数read_dataと出力する関数write_dataを作れ。 データは各自適当に決めてよい。名前は、文字型の配列とし、サイズは20文字 にする。 ./a.out 名前は: kako-fujio 学籍番号は: 999999 年齢:20 身長:163.4 体重:72.5 登録データ  kako-fujio 999999 20歳  163.4 cm 72.5kg #include <stdio.h> struct student { 必要な変数を定義; }; main() { struct student s; read_data(&s); printf(“登録データ\n”); write_data(s); }

問題 3 出力の関数 void print(struct time a) { printf(“%d時%d分%d秒”, a.hour, 問題 3  時(hour)、分(minute)、秒(second)からなる構造体(time)を定義し、 時刻の和と差をそれぞれ求める関数 struct time timeadd(struct time a, struct time b); struct time timesub(struct time a, struct time b); を作成せよ。 時は、0から23までで、24時は0、また−1時は23時とする。 12時23分10秒+5時40分15秒=18時3分25秒 12時23分10秒−5時40分15秒=6時42分55秒 出力の関数 void print(struct time a) { printf(“%d時%d分%d秒”, a.hour, a.minute, a.second); }

問題 4 ヘロンの公式 平面での座標ベクトル(x、y)を構造体として定義し、 2点間の距離を求める関数をプログラムせよ。 また、この関数を用いて与えられた3点を頂点とする 三角形の面積を求めるプログラムを作成せよ。 ヘロンの公式

問題 5 キーボードから文字を 1 文字ずつ読み込み、Ctrl-D で中断する。読み込んだ文字をファイルに書き出すプログラムを作れ。なお書きだすファイルの名前もキーボードから入力するとする。 % ./a.out ファイル名:text_file 文字を入力せよ(Ctrl-Dで終了) Summer vacation is close at hand! Ctrl-D % % cat text_file 書き出したファイルの内容を確認せよ。