計算機プログラミング基礎
(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); }
ファイルの書き出し
#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);
}
#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)ポインタを更新する
#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() の呼び出しが(メモリ確保が) 成功したかどうかを確認する必要があります。構造体に対する動的メモリ
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を消滅させるList構造 解説
mylist.c
まずは リスト構造の定義
struct mylist
{
int data;
struct mylist *nextptr;
};
/
data
nextptr
mylistmain関数
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); } }
リストへの挿入
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
4ptr
ちなみに
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;