Transcript C言語

C言語
情報理工学部 システム工学科 3年
H107035 亀窪祐太
H107042 纐纈琢真
H107034 神谷真輝
H107087
西村智
目的
 バグの無いプログラムを書く
 入力時のエラーを無くそう
 汎用的なプログラムを書く
 型にとらわれないプログラムを書こう
 効率の良いプログラムを書く
 動的配列を使おう
目次
1.
入力時の問題点
3.
動的配列
 scanfを使わない入力
 利点
 問題点の改善策
 動的メモリ管理関数
 入力関数
 プログラム例
 バッファ削除
 フォーマット関数
 改善プログラムの解説
2.
型にとらわれないプログラム
 関数ポインタ
 void*とキャスト
 qsortを使った実例
4.
課題
プログラム
これを実行
すると…
問題のあるプログラム
2番目の文字が
入力できない
文字がcのサイズより
多くても入力できる
プログラムの問題点
1. バッファサイズのチェックを行っていない
2. 空白、改行も入力文字として取っている
3. 型をチェックしていない
scanfを使わない入力

CRT(C Runtime)には、ストリームが用意されている
ストリームとは
プログラム、入出力先間のデータの流れの経路

ストリームの種類
ポインタ
ストリーム
機能
FILE* stdin
標準入力
キーボードから読み込み
FILE* stdout
標準出力
画面へ出力
FILE* stderror
標準エラー出力
画面へ出力
1の改善策:入力関数

ファイルから一行読み込む関数
char* fgets(char* str, int str_size, FILE* fp);
ヘッダ <stdio.h>
[返り値]char* str
fpにストリームをセットすることで
ストリームから文字列を読み取りstrが指す配列に格納する
なお、格納されるのは (str_size – 1) バイト目まで
2の改善策:バッファ削除

標準入力(stdin)から1文字入力する関数
int getchar();
ヘッダ <stdio.h>
[返り値] その文字コードを返す
入力ストリームに残っている場合は、
入力を行うのではなく、入力ストリームから1文字取り出す
※入力が改行で終わるとき、次の方法でできる
char str[20];
fgets(str, sizeof(str) stdin);
int strLength = strlen(str); //strlen() : 文字列の長さを取得する
if (str[strLength - 1] != ‘\n’){
// 改行がでるまで標準入力から1文字ずつ取り出す
while(getchar() != '\n');
}
2の改善策:バッファ削除

バッファのフラッシュ(消去)を行う

Borland C++ Compiler(bcc)のみ使える方法(※)
int fflush(FILE* stream);
ヘッダ <stdio.h>
[返り値] 0
streamが出力ストリーム、または
直前の操作が入力でない入出力ストリームなら
全ての内容を出力し、消去する
※ Borland C++ Compiler(bcc)では、上記の機能に加えて
streamが入力ストリームなら全て内容を消去するという機能がある
[ 注意 ]
標準規格(ANSI-C)では、入力ストリームに対しての動作は未定義である
3の改善策:フォーマット関数

文字列から書式指定に従い入力を行う関数
int sscanf(const char* str, const char* format, [argument]);
ヘッダ <stdio.h>
[返り値]代入された入力項目の個数
str の文字列を format の形式に変換し、
指定した変数のアドレスに代入
改善したプログラム
プログラムの解説
fgets(d,sizeof(d),stdin);
[0] [1] [2] [3] [4]
d
\0
stdin
a
b
c
d
ヌル文字を入れる
d – 1 バイト目まで文字列を入れていく
e
f
\n
プログラムの解説
if(d[strlen(d) - 1] != ‘\n’)
[0] [1] [2] [3] [4]
d
a
b
c
d
stdin
\0
strlen = 4
strlen(d) – 1 番目が \n か判定
dの文字列の長さを求める(\0は含まない)
e
f
\n
プログラムの解説
while(getchar() != ‘\n’);
[0] [1] [2] [3] [4]
d
a
b
c
d
stdin
\0
getchar
取り出した文字が
stdinから1文字取り出す
\n を取り出すまで繰り返す
\n か判定
e
f
\n
プログラムの解説
if(sscanf(d,”%s”,c) == 0) exit(-1);
[0] [1] [2] [3] [4]
d
a
b
c
d
\0
[0] [1] [2] [3] [4]
c
a
cd を文字列型に変換
に文字列型に変換した d をコピー
b
c
d
\0
型にとらわれないプログラム

ソート処理の場合
 比較関数を作る
 ソート関数に比較関数を渡す
C言語の場合
関数ポインタを使う
 voidポインタとキャストを使う

関数ポインタ
関数のアドレスを保持するポインタ
 以下のように宣言する
戻り値の型 (*ポインタ名) (引数のリスト);
 以下のように使う
int func(int x){
int (*p)(int x);
return x * 2;
p = func;
}

ans = (*p)(10); // または ans = p(10);
voidポインタとキャスト

voidポインタ (汎用ポインタとも呼ばれる)
あらゆるポインタ型に変換できる
 どのような型も受け取れる関数が作れる
 使用する際に使いたい型にキャストする

例
void *v;
int i;
v = &i;
// 受け取る場合
int *p = (int *)v; // 使用する場合
voidポインタの使用例

qsort : クイックソートを行う関数
void qsort(void* base, size_t n, size_t size, int(*fnc)(const void*, const void*))
// base
ソートする配列のアドレス
// n
配列の要素数
// size
配列の要素の大きさ
// fnc
比較関数のアドレス
比較関数
並べ替えるための基準を指定する関数
 2つの引数を持ち、int型を返り値とする
 返り値は

第1引数を先頭側にする場合、負の値
第2引数を先頭側にする場合、正の値
両者が同じなら0
Cによる動的配列
動的に連続領域を確保する
リスト構造を使用する
構造体とポインタを使用して実現
配列を動的に確保する
malloc, calloc, realloc, free関数を使用して実現
動的確保の利点
必要な場所で必要な量だけ確保する事が可能
好きな時に、解放する事が出来る
使用範囲が自由に決められる
その結果、効率の良いリソース(メモリ)の
管理が可能になる
動的メモリ管理関数
動的メモリ領域の確保
void* malloc(size_t size)
void* calloc(size_t n, size_t size)
動的メモリ領域の再定義
void* realloc(void *ptr, size_t size)
動的メモリ領域の解放
void free(void *ptr)
必要なヘッダーファイル
<stdlib.h>
※ size_t とは
引数として与えるsizeの型
void* malloc(size_t size)
メモリを確保する関数
引数には確保したいメモリサイズ(バイト)を渡す
任意の領域を確保するために、
voidポインタ型が使われている
確保領域に合わせてキャストする必要あり
返り値
成功 :確保領域の先頭アドレス
失敗 :NULL
void* calloc(size_t n, size_t size)
mallocと同じく、メモリを確保する関数
キャストする必要あり
引数には配列の要素数と要素の
メモリサイズ(バイト)を渡す
返り値
成功 :確保領域の先頭アドレス
失敗 :NULL
確保した領域は
全てのビットを0で初期化されている
void* realloc(void *ptr, size_t size)
動的配列のサイズを再定義する
キャストする必要あり
前に確保した要素は最大で
sizeバイト分だけコピーされる
引数には再定義したい配列のアドレス、
再定義後のメモリサイズ(バイト)を指定する
返り値
成功 :確保領域の先頭アドレス
失敗 :NULL
reallocの注意点
引数がおかしくても文法エラーにならない
第一引数をNULLに
realloc(NULL, size_t size)→malloc(size_t size)
第二引数を0に
realloc(void *ptr, 0)→free(void *ptr)
void* free(void *ptr)
確保された領域を解放する関数
引数には動的に確保した領域のアドレスを渡す
動的に確保した領域以外のアドレスや既に解放した
アドレスを渡してはいけない
NULLを受け取った場合何もしない
解放し忘れていてもエラーにはならないので注意
解放し忘れる → メモリリークが起こる
malloc ・ calloc : プログラム例
freeで解放
忘れずに
realloc : プログラム例
課題
課題1
5個の変数にそれぞれ整数を入力し、引数が
1つの関数ポインタを利用してその値を加算
せよ
scanfは使わない
課題2
名前とGPAを持つ構造体を使った配列を10
人分保管し、qsortを使って並び変えよ
 mallocまたはcallocを使うこと
 ソート前とソート後のデータを表示すること
 並び変えの基準
 GPAの降順
 GPAが同じ場合は名前の昇順(辞書順)
課題3
課題2のプログラムを任意の数扱えるように
せよ
ユーザに任意の人数分のデータを入力させよ
名前入力時にendと入力すると
入力が終わるようにする