• 検索結果がありません。

Microsoft PowerPoint - pro ppt [互換モード]

N/A
N/A
Protected

Academic year: 2021

シェア "Microsoft PowerPoint - pro ppt [互換モード]"

Copied!
10
0
0

読み込み中.... (全文を見る)

全文

(1)

計算機プログラミング基礎

(Cクラス)

河口 信夫

前回の復習

ファイル入出力

– 文字ストリームのコンセプト

– 標準入出力

ポインタ

– 数字・文字列との対応

ファイル入出力

ファイルのオープンとクローズ

ファイル入出力の手順 ファイルのオープン ファイルの読み書き ファイルのクローズ ファイル処理の前準備 データの読み書き ファイル処理の後始末

ファイルのオープンとクローズ

fopen 関数と fclose 関数

【一般的な書式】 FILE *fp; fp=fopen(<ファイル名文字列>,<アクセスモード文字列>); fclose(fp); r 既存ファイルの読み込み用(read) w 新規ファイルへの書き出し用 (ファイルが存在すれば上書き)(write) a 既存ファイルへの追加書き出し用(append) r+,w+,a+ それぞれのモードで読み込み/書き出しの両用 アクセスモード 文字列 t ASCIIファイル b binaryファイル ファイル種別 注) ASCIIファイルと binaryファイルを区別する OSと区別しないOSがある ファイルポインタ(オープンされた ファイルの管理に使用)

ファイルの読み込み

#include <stdio.h> #include <stdlib.h> int main(void) { FILE *fin; char infile[40]; printf("input file = "); scanf("%s", infile); if ( (fin=fopen(infile,"rt")) == NULL ) { fprintf(stderr,"Can't open %s.¥n",infile); exit(1); } /* ここでファイルの中身を読む */ fclose(fin); return(0); } 「 “infile” で指定したファイルを入力用にファイル ポインタ “fin” を使ってオープンしなさい。ファイル オープンに失敗したらNULL が返されるので、 その場合はエラーメッセージを表示しなさい。」

ファイルの中身の表示

#include <stdio.h> #include <stdlib.h>

int main(int argc, char *argv[]) { FILE *fin;

int c;

if ( (fin=fopen(argv[1],"rt")) == NULL ) { fprintf(stderr,"Can't open %s.¥n",infile); exit(1);

}

while( (c=fgetc(fin)) != EOF){ putchar(c);/* fputc(c,stdout);*/ }

fclose(fin); return(0); }

(2)

ファイルの書き出し

#include <stdio.h> #include <stdlib.h> int main(void) { FILE *fout; char outfile[40]; printf("output file = "); scanf("%s", outfile); if ( (fout=fopen(outfile,"wt")) == NULL ) { fprintf(stderr,"Can't open %s.¥n",outfile); exit(1); } /* ここでファイルに書き出す */ fclose(fout); return(0); } 「 “outfile” で指定したファイルを出力用にファイル ポインタ “fout” を使ってオープンしなさい。ファイル オープンに失敗したらNULL が返されるので、 その場合はエラーメッセージを表示しなさい。」 注) 書き出し用にファイルを オープンする際、すでに同名 のファイルが存在した場合は、 ファイルオープンを行った時点 で、そのファイルの中身は すべて消去されます。

標準入出力

stdin, stdout, stderr

– Cのプログラムが実行を開始すると、自動的に 利用できるようになるストリーム – ストリームとは、Cプログラムとファイルとを結ぶ、 データの流れのこと – C言語では、入出力デバイス(キーボードや画面)も 特別なファイルとして扱う stdin 標準入力ストリームポインタ(キーボード入力) stdout 標準出力ストリームポインタ(画面出力) stderr 標準エラーストリームポインタ(画面出力)

ファイル入出力関数

ファイル入出力関数 コンソール入出力関数

putc または fputc

putchar

getc または fgetc

getchar

fputs

puts

fgets

gets

fprintf

printf

fscanf

scanf

例) char s[256]; fgets(s, 256, fin); fputs(s, fout); 「fin が指すファイルから最大256文字まで、 文字列s に1行入力しなさい。 文字列s を、 fout が指すファイルへ 1行出力しなさい。」

ポインタとアドレス

変数には、値を格納するためのメモリが 割り当てられます。 int a; ポインタは、「コンピュータのメモリ上に 格納されているデータの場所を表すデータ」 です。データ a のポインタを p とすると、 「p は、実体 a を指し示すポインタ」 と表現することができます。 これをC言語では次のように表します。 int *p; p = &a; 「int 型のデータをポインタ p で参照します」 「ポインタ p が参照するのは、変数 a のアドレス」 13 12 11 220 10 9 8 7 6 5 10 4 内容 アドレス int 型変数 a の場所 ポインタ変数p の場所

ポインタ演算子

ポインタの示すアドレスにある値を取り出す

*

変数のアドレスを取り出す

&

意味

演算子

ポインタを通じて、変数にアクセスすることが

できます

ポインタの練習

#include <stdio.h>

int main(void){

int a,b;

int *p;

a=123; p=&a; b=*p;

printf("a=%d, b=%d, *p=%d¥n",a,b,*p);

a=200; b=300;

printf("a=%d, b=%d, *p=%d¥n",a,b,*p);

p=&b;

printf("a=%d, b=%d, *p=%d¥n",a,b,*p);

return(0);

}

(3)

#include <stdio.h>

int main(void){

int a,b;

int *p;

a=123; p=&a; b=*p;

printf("a=%d, b=%d, *p=%d¥n",a,b,*p);

a=200; b=300;

printf("a=%d, b=%d, *p=%d¥n",a,b,*p);

p=&b;

printf("a=%d, b=%d, *p=%d¥n",a,b,*p);

return(0);

}

※ 上記のアドレス値(1000,1004)は例です 1000 1004 1000 p b a アドレス変換表 1000番地 1004番地 123 123 実メモリ

#include <stdio.h>

int main(void){

int a,b;

int *p;

a=123; p=&a; b=*p;

printf("a=%d, b=%d, *p=%d¥n",a,b,*p);

a=200; b=300;

printf("a=%d, b=%d, *p=%d¥n",a,b,*p);

p=&b;

printf("a=%d, b=%d, *p=%d¥n",a,b,*p);

return(0);

}

※ 上記のアドレス値(1000,1004)は例です 1000 1004 1000 p b a アドレス変換表 1000番地 1004番地 300 200 実メモリ

#include <stdio.h>

int main(void){

int a,b;

int *p;

a=123; p=&a; b=*p;

printf("a=%d, b=%d, *p=%d¥n",a,b,*p);

a=200; b=300;

printf("a=%d, b=%d, *p=%d¥n",a,b,*p);

p=&b;

printf("a=%d, b=%d, *p=%d¥n",a,b,*p);

return(0);

}

※ 上記のアドレス値(1000,1004)は例です 1000 1004 1004 p b a アドレス変換表 1000番地 1004番地 300 200 実メモリ

文字列とポインタ

1)文字列先頭アドレスのコピー

#include <stdio.h> #include <string.h> int main(void){ char st[10]; char *p; strcpy(st,"NAGOYA"); p=st; printf("st=%s p=%s¥n",st,p); return(0); } 1000番地 1001番地 ’A’ ’N’ 実メモリ 1002番地 ’G’ 1003番地 ’O’ 1004番地 ’Y’ 1005番地 ’A’ 1006番地 ¥0 p 1000 1001 1002 st[2] st[1] st[0] アドレス変換表 1003 st[3] 1004 st[4] 1005 st[5] 1006 st[6] 1007 st[7] 1008 st[8] 1009 st[9] p 1000

文字列とポインタ

(2)ポインタで任意のアドレスを示す

#include <stdio.h> #include <string.h> int main(void){ char st[10]; char *p; strcpy(st,"NAGOYA"); p=st; putchar(*p); putchar(*(p+1)); putchar(*(p+2)); putchar('¥n'); return(0); } putchar(c) は、 文字変数c (1文字) を画面に表示する ための関数 1000番地 1001番地 ’A’ ’N’ 実メモリ 1002番地 ’G’ 1003番地 ’O’ 1004番地 ’Y’ 1005番地 ’A’ 1006番地 ¥0 p 1000 1001 1002 st[2] st[1] st[0] アドレス変換表 1003 st[3] 1004 st[4] 1005 st[5] 1006 st[6] 1007 st[7] 1008 st[8] 1009 st[9] p 1000 p+1 p+2

文字列とポインタ

(3)ポインタで値を書き換える

#include <stdio.h> #include <string.h> int main(void){ char st[10]; char *p; strcpy(st,"NAGOYA"); p=st; *p='T'; *(p+2)='K'; printf("st=%s¥n",st); return(0); } 1000番地 1001番地 ’A’ ’T’ 実メモリ 1002番地 ’K’ 1003番地 ’O’ 1004番地 ’Y’ 1005番地 ’A’ 1006番地 ¥0 p 1000 1001 1002 st[2] st[1] st[0] アドレス変換表 1003 st[3] 1004 st[4] 1005 st[5] 1006 st[6] 1007 st[7] 1008 st[8] 1009 st[9] p 1000 p+2

(4)

文字列とポインタ

(4)ポインタを更新する

#include <stdio.h> #include <string.h> int main(void){ char st[10]; char *p; strcpy(st,"NAGOYA"); printf("st=%s¥n",st); p=st; while(*p){ *p=*p+1; ++p; } printf("st=%s¥n",st); return(0); } *p=*p+1 1000 1001 1002 st[2] st[1] st[0] アドレス変換表 1003 st[3] 1004 st[4] 1005 st[5] 1006 st[6] 1007 st[7] 1008 st[8] 1009 st[9] p 1000 1000番地 1001番地 ’A’ ’N’ 実メモリ 1002番地 ’G’ 1003番地 ’O’ 1004番地 ’Y’ 1005番地 ’A’ 1006番地 ¥0 ’B’ ’O’ ’H’ ’P’ ’Z’ ’B’ ¥0 ++p で、ポインタ自身の値 を+1、すなわちアドレス値 に1 を加算

本日の目標

構造体

– リスト構造

動的メモリ確保

名簿管理システム

構造体

配列は、同種のデータをひとまとめに扱うためのデータ構造 構造体は、異種のデータをまとめて取り扱うためのデータ構造 – レコード : データ構造の単位 – メンバ : レコードを構成する個々のデータ struct タグ名 { 型 メンバ1; 型 メンバ2; 型 メンバ3; .... 型 メンバN; } 変数リスト; 一般的な形式 タグ名と変数リストは どちらか一方を 省略することができる 変数リストは 構造体の実体を表す

構造体の定義例

氏名

, 学籍番号, 年齢を含む構造体 member を

定義し、その変数として64個の要素を格納できる

配列 classC を宣言する

struct member { char name[40]; char ID[10]; int age; } classC[64]; struct member { char name[40]; char ID[10]; int age; }; int main(void) {

struct member classC[64]; ... どちらでも可

構造体メンバへのアクセス

ドット演算子(.)とアロー演算子(->)

– 構造体型変数で構造体メンバにアクセスする場合には ドット演算子を使い、ポインタで構造体メンバにアクセス する場合にはアロー演算子を使う ... struct member { char name[40]; char ID[10]; int age; }; int main(void) {

struct member classC[64], *p; classC[0].age = 18;

p = &classC[0];

printf("%d %d¥n", classC[0].age, p->age ); return 0; }

動的なメモリ割り当て

プログラムの実行中に必要に応じてメモリを

割り当てる処理(stdlib.h が必要)

–malloc 関数 ・・・ メモリを割り当てる –free 関数 ・・・ 割り当て済みのメモリを解放する

例) 「

80バイトまでの文字列を扱う配列を宣言する」

char *p; p = (char *)malloc(80); free(p); ※ 実際には、malloc() の呼び出しが(メモリ確保が) 成功したかどうかを確認する必要があります。

(5)

構造体に対する動的メモリ

sizeof 演算子 =型の大きさを決める

#include <stdio.h> struct member { char name[40]; char ID[10]; int age; }; int main(void) { struct member *c;

c = (struct member *)malloc(sizeof(struct member)); if( c == NULL ) exit(1);

c->age = 18; printf("%d¥n", c->age ); free(c); return 0; }

ソーティング・アルゴリズム

並べ替えを行う

対象によって様々な種類が

– バブルソート

– マージソート

– クイックソート

高度なソーティングは講義の範疇を超える

ので、単純なバブルソート、

マージソートのみを解説

配列のソーティング

単純なバブルソート

void main(){

int i,j, tmp, val[10] = {3,6,2,8,1,5,7,9,0,4}; for(i=0; i< 9; i++){

for(j = i+1; j < 10; j++){ if( val [i] > val [j] ) {

tmp = val[i]; val[i] = val[j]; val[j] = tmp; }}} for(i =0; i < 9; i++) printf(“val[%d]=%d ¥n”,i,val[i]); }

リストを使った挿入ソート

リストに値を入れる段階で

ソーティングして入れる

異なるソーティング法をとりたいときは、

新しいリストを作成するのが簡単

リスト構造

順序関係を持つ1次元的なデータ構造

– リストを構成するひとつのレコードをセルという

– セルは、データと次のセルへのポインタを要素

として持つ

– ポインタをたどることによって、リストを走査する

ことができる

A B C D

データ 次のセルへのポインタ

リストの挿入・削除

リストの挿入(セルAとBの間にセルEを挿入)

リストの削除(セルCを削除)

A B C D

E

×

1) セルEを作成 2) AのポインタをEに向ける 3) EのポインタをBに向ける A B

×

C

×

D

1) BのポインタをDに向ける 2) Cを消滅させる

(6)

List構造 解説

mylist.c

まずは リスト構造の定義

struct mylist

{

int data;

struct mylist *nextptr;

};

data

nextptr

mylist

main関数

int main(){ MYLIST *root=NULL; int newdata; while(1){ printf("data > "); scanf("%d",&newdata); if(newdata<0) break; insertList(&root,newdata); traverseList(root); } return 0; }

typedef

typedef struct mylist{

int id;

char name[40];

struct mylist *nextptr;

} MYLIST;

typedef struct mylist MYLIST;

typedef struct mylist *MYLISTP;

MYLISTP root;

main関数

int main(){ MYLIST *root=NULL; int newdata; while(1){ printf("data > "); scanf("%d",&newdata); if(newdata<0) break; insertList(&root,newdata); traverseList(root); } return 0; } LISTのroot LISTへの挿入 LISTの表示 ここに注目

リスト表示関数(TraverseList)

void traverseList(MYLIST *ptr)

{

while(ptr!=NULL)

{

printf("%d ",ptr->data);

ptr=ptr->nextptr;

}

putchar('¥n');

}

NULLになるまで ループ Listを順に たどって表示

リストへの挿入

void insertList(MYLIST **ptr, int newdata) {

if(*ptr==NULL || (*ptr)->data > newdata) { MYLIST *newcell; newcell=(MYLIST *)malloc(sizeof(MYLIST)); newcell->data=newdata; newcell->nextptr=*ptr; *ptr=newcell; }else{ insertList(&((*ptr)->nextptr), newdata); } }

(7)

リストへの挿入

void InsertList(struct LIST **ptr, int newdata) {

if(*ptr==NULL || (*ptr)->data > newdata) { struct LIST *newcell;

newcell=(struct LIST *)malloc(sizeof(struct LIST)); newcell->data=newdata; newcell->nextptr=*ptr; *ptr=newcell; }else{ InsertList(&(*ptr)->nextptr, newdata); } } ここに注目 ポインタへのポインタ 値が大きい セルが存在しない 新しいセルを 作成して挿入 ここに注目 ポインタの更新 セルが存在し、値が小さい場合は次に

ポインタへのポインタ?

struct LIST **ptr; とは何を意味しているか?

3 6

struct LIST *root

InsertList( &root, 4 ) を考える

if(*ptr==NULL || (*ptr)->data > newdata)

→ *ptr = root != NULL

→ (*ptr)->data = root->data = 3 < 4 より

条件成立せず。

if 文の else 節を実行し、

InsertList( &(root->nextptr), 4 ) が呼び出される

ポインタへのポインタ?

struct LIST **ptr; とは何を意味しているか?

3 6

struct LIST *root

if(*ptr==NULL || (*ptr)->data > newdata)

→ (*ptr)->data = 6 > 4 より条件成立

InsertList( &(root->nextptr), 4 ) を考える

newcell=(struct LIST *)malloc(sizeof(struct LIST)); newcell->data=newdata;

newcell->nextptr=*ptr; *ptr=newcell;

セルへの挿入

3 6

struct LIST *root

newcell=(struct LIST *)malloc(sizeof(struct LIST)); newcell->data=newdata ; /* (= 4) */ newcell->nextptr=*ptr; *ptr=newcell; ptr = &(root->nextptr) が前提

newcell

4

ptr

ここまでの状況(下図)

セルへの挿入

3 6

struct LIST *root

newcell=(struct LIST *)malloc(sizeof(struct LIST)); newcell->data=newdata ; /* (= 4) */ newcell->nextptr=*ptr; *ptr=newcell; ptr = &(root->nextptr) が前提

newcell

4

ptr

ちなみに

root = NULL の場合

(ptr = &root )

root = NULL

newcell

4

ptr

newcell=(struct LIST *)malloc(sizeof(struct LIST)); newcell->data=newdata ; /* (= 4) */ newcell->nextptr=*ptr; *ptr=newcell;

root

newcell

4

ptr

(8)

ではリストの削除は?

deleteList を作ろう

引数は

MYLIST **ptr となる

– (引数が指す先を delete したい場合もあるため)

関数宣言

void deleteList(MYLIST **ptr, int deldata);

やるべきこと

deldata を探して、そのセルを削除

前のセルからのポインタを次のセルに接続

演習問題

deleteList を作ろう

詳細は

Webページからダウンロード可能

deleteList の実際

まずは

delData までを探す

最初に

*ptr がNULLでないことを確認

NULLの場合は、return

(再帰で実現した場合の終了条件の一つ)

再帰で実現するのが簡単

if( (*ptr)->data != delData )

deleteList((*ptr)->nextptr,delData);

draverseList と同様にWhile文でもOK.

deleteList の実際

deldata =4 で*ptr が 以下の場合、

(*ptr)->data == deldata

3

×

4 6

struct LIST *root

ptr

やるべきことは

MYLIST *delptr = *ptr;

*ptr = delptr->nextptr;

free(delptr); // メモリ開放

ここからプログラミング本番

テキストベースの

名簿管理のシステムを作ってみよう

プログラムの構造

構造体を使ったデータ管理

動的なメモリの確保、ソーティング、削除

名簿管理をつくるには?

データ構造(構造体)

データ投入

– キーからの入力

– ファイルからの入力

表示

– ソーティング

– 追加・削除

保存

– ファイルへの保存

(9)

キーからのデータ入力

2種類の入力方法

– これまでは scanf を利用

– 1行(空白文字も含めて)を入力ためには

fgets(char *buf, int leng, FILE *fp);

を利用 (input.c を参考)

詳細はマニュアル

man を見てみよう

ファイルからの入力

ファイルも、文字入力も同じFILE *

(これをストリームと呼ぶ)

同じ扱いでOK

– fscanf (FILE *fp, char *format)

を使う

ファイルの最後は

EOF (end of file)

ファイルへの出力

出力も同じようにストリーム

– 画面表示も stdoutというストリーム

fopen で開いて fclose で閉じる

fprintf(FILE *fp, “format”, args…);

で 出力可能

一覧表示

いろいろなソーティングを行う

表示方法を切り替える仕組みも

あるとよい

(ソーティング関数で切り替え?)

動的なメモリ割り当て

プログラムの実行中に必要に応じてメモリを

割り当てる処理(stdlib.h が必要)

–malloc 関数 ・・・ メモリを割り当てる –free 関数 ・・・ 割り当て済みのメモリを解放する

例) 「

80バイトまでの文字列を扱う配列を宣言する」

char *p; p = (char *)malloc(80); free(p); ※ 実際には、malloc() の呼び出しが(メモリ確保が) 成功したかどうかを確認する必要があります。

キャスト(型変換)

型変換を行う

char b = 10;

int a = (int)b; // キャスト(型変換)

malloc で利用

struct LIST *newcell =

(10)

出席アンケート

今後の講義の参考にしますので

アンケートには、要望や良かった点なども

書いてください

大学では、問題点を「自分で」見つけ出して

「自分で」学習することが基本です。

講義は、学習のテーマやきっかけを与え、

学習の支援をすることが目的です。

課題

名簿管理システムの作成

– 以下のコマンドを実行可能な名簿管理システムを作成せよ メンバーの追加・削除 メンバーリストのアルファベット順での表示 メンバーリストのアルファベット順でのファイル出力 – メンバー情報として、学生番号と名前を含む – 引数として -f “ファイル名” を付与することによって、 初期メンバーリストをファイルから読み込む機能を持つこと – 名簿システムの機能は、各自自由に拡張して構わない

来週は演習中心

課題提出は2週間後

(12/ 8 17:00)

詳細は講義Webページ参照

次回(12/2)は演習

構造体をつかった名簿管理システムを

作成します。

構造体の配列を利用する手法(初級)

構造体のリストで実現(上級)

とりあえず、構造体+ポインタの簡単な

理解はできたかと思います。

参照

関連したドキュメント

これはつまり十進法ではなく、一進法を用いて自然数を表記するということである。とは いえ数が大きくなると見にくくなるので、.. 0, 1,

Sabbah, Equations diff´ ´ erentielles ` a points singuliers irr´ eguliers et ph´ enom` ene de Stokes en dimension 2, Ast´erisque, 263, Soci´et´e Math´ematique de France,

自分は超能力を持っていて他人の行動を左右で きると信じている。そして、例えば、たまたま

azuma Bumpyうるし ヒカルト

だけでなく, 「家賃だけでなくいろいろな面 に気をつけることが大切」など「生活全体を 考えて住居を選ぶ」ということに気づいた生

ERROR  -00002 認証失敗または 圏外   クラウドへの接続設定及びア ンテ ナ 接続を確認して ください。. ERROR  -00044 回線未登録または

○金本圭一朗氏

・ 11 日 17:30 , FP ポンプ室にある FP 制御盤の故障表示灯が点灯しているこ とを確認した。 FP 制御盤で故障復帰ボタンを押したところ, DDFP