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

Original : Hello World! (0x0xbfab85e0) Copy : Hello World! (0x0x804a050) fgets mstrcpy malloc mstrcpy (main ) mstrcpy malloc free fgets stream 1 ( \n

N/A
N/A
Protected

Academic year: 2021

シェア "Original : Hello World! (0x0xbfab85e0) Copy : Hello World! (0x0x804a050) fgets mstrcpy malloc mstrcpy (main ) mstrcpy malloc free fgets stream 1 ( \n"

Copied!
19
0
0

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

全文

(1)

2008

年度プログラミング第

3

同演習

10

回演習問題

問題

1

文字列をコピーする関数を以下のプロタイプ宣言を用いて作成しなさい.そして,標準入力から文字列を 入力し,標準入力から入力された文字列とその先頭アドレス,および mstrcpy 関数を用いてコピーした文 字列とその文字列の先頭アドレスを出力するプログラムを作りなさい.

char *mstrcpy(const char *src);

この時,mstrcpy 関数においてコピー先のメモリ領域は malloc 関数を用いて確保するものする.(main 関 数においてコピーした文字列の出力終了後,free 関数を用いて確保したメモリ領域を開放するのを忘れない こと.)

また,標準入力からの文字列の入力は stdio.h に宣言されている fgets 関数を使用すること. char *fgets(char *s, int size, FILE *stream);

sは入力された文字列を格納するための領域を指すポインタ,size はこの領域のサイズ (バイト数,最大の 文字列の長さにあたる),stream は FILE 構造体へのポインタとなる.(詳しくは man ページ参照).

入力される文字列の長さは 40 文字を越えないものと仮定して良い.

実行結果サンプル

以下の実行結果において,括弧内の値 (アドレス) は異なっても構わない. % ./a.out String : test Original : test

(2)

Original : Hello World! (0x0xbfab85e0)

Copy : Hello World! (0x0x804a050)

チェックポイント

• 実行結果サンプルと同様の実行結果が得られること. • 標準入力からの文字列の入力に,fgets 関数を使用していること. • mstrcpy 関数内で malloc 関数を使用して文字列のコピー先領域を確保し,この領域に文字列をコピー していること. • コピー元文字列とそのアドレスおよびコピーした文字列とそのアドレスを両方出力していること. • mstrcpy 関数の呼び出し元 (main 関数) で,mstrcpy 関数内の malloc 関数で確保した領域を free 関数

を用いて開放していること.

ヒント

• fgets 関数では stream で与えられた入力から 1 行 (改行コード’\n’ まで.) が文字列として読み込ま れる.返される文字列には,改行コード’\n’ が含まれることに注意.

(一方,scanf 関数で"%s"フォーマットを用いた場合には基本的には 1 単語 (スペース’ ’ や,改行’\n’, タブ’\t’ などまで) が読み込まれる.例えば,入力として “Hello world!” を与えた場合,scanf("%s",str) 等として読み取ろうとすると,“Hello” のみが受け取られる.)

• stdio.h には stdin,stdout,stderr という FILE 構造体へのポインタが定義されており,各々標準入力, 標準出力,標準エラー出力に対応している.(fgets の第 3 引数の値として使用することができる.) • アドレスを出力する場合は,printf 関数のフォーマットに"%p"を指定する.例えば, char *p; : printf("%p\n", p); とする. • string.h に宣言されている,strlen で文字列の長さ取得することができる.プロトタイプ宣言は, size_t strlen(const char *s);

であり,s に文字列の先頭アドレスを指定する.size_t は,ここでは int 型と考えてよい.(詳しくは manページ参照)

• malloc 関数のプロトタイプ宣言は, void *malloc(size_t size);

であり,size に確保する領域のサイズ (バイト数) を与える.文字列の場合は文字の数+1 の値 (終端 記号’\0’ の分) を与える必要があることに注意.また,返される値は void *型なので,キャストして char *型に変換する必要があることに注意.

(3)

• mstrcpy 関数は,コピーした文字列の先頭アドレスを返す必要があることに注意.文字列コピーの過 程で,malloc 関数により返されたアドレス (確保された領域の先頭アドレス) を保存しておく必要が ある.

解答例

#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_STR 40

char *mstrcpy(const char *str);

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

char str[MAX_STR]; char *p;

printf("String : ");

fgets(&str[0], MAX_STR, stdin);

p = mstrcpy(&str[0]);

printf("Original : %s(0x%p)\n", &str[0], &str[0]); printf("Copy : %s(0x%p)\n", p, p);

free(p); exit(0); }

char *mstrcpy(const char *str) {

(4)

問題

2

コマンドライン引数に関数 (’sin’,’cos’,’tan’,’log’,’exp’) と値 (実数) を与え,計算結果を表示するプログラ ムを書きなさい.コマンドライン引数の数が 2 個以外の場合は,“Please specify a function and a value” と標準エラー出力に出力して終了しなさい.また,コマンドライン引数の第 1 引数が上記関数以外の場合 は,標準エラー出力に “Undefined function” と出力して終了しなさい.コマンドライン引数の第 2 引数は 実数を表す文字列であることを仮定して良い. 尚,sin,cos,tan,log,exp の各関数は libm に存在するため,コンパイル時に % gcc (ソースファイル名) -lm というように-lm オプションを付ける必要があることに注意 (libm).

実行結果サンプル

(sin,cos,tan,各関数の引数の単位はラジアン [rad] である.また,log,exp 関数の底は e(=2.71828...; ネイピア数) である.)

% ./a.out

Please specify a function and a value % ./a.out cos

Please specify a function and a value % ./a.out sin 1 2

Please specify a function and a value % ./a.out sin 1 Ans : 0.841471 % ./a.out cos 1.570796325 Ans : 0.000000 % ./a.out tan 2 Ans : -2.185004 % ./a.out exp 1 Ans : 2.718282 % ./a.out log 2.718282 Ans : 1.000000 % ./a.out hoge 10

Undefined function : hoge

チェックポイント

• 実行結果サンプルと同様の実行結果が得られること.

• 各エラーメッセージが問題文の通り,標準エラー出力に出力されること.

ヒント

(5)

int fprintf(FILE *stream, const char *format, ...);

を用いると良い.stream に標準エラー出力を指定する.format や... 部分は printf 関数と同様. • 文字列の比較は string.h に宣言されている,

int strcmp(const char *s1, const char *s2);

を用いると良い.strcmp 関数は s1 と s2 で渡される文字列の内容が等しければ 0 を返す.(詳しくは manページ参照)

• 文字列から実数への変換は stdlib.h に宣言されている double atof(const char *nptr);

を用いると良い.atof 関数は nptr で渡される文字列を double 型の数値に変換して返す.(詳しくは manページ参照)

(6)

解答例

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

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

double v, ans; if(argc != 3){

fprintf(stderr, "Please specify a function and a value\n"); exit(-1);

}

v = atof(argv[2]);

if(strcmp(argv[1], "sin") == 0){ ans = sin(v);

}else if(strcmp(argv[1], "cos") == 0){ ans = cos(v);

}else if(strcmp(argv[1], "tan") == 0){ ans = tan(v);

}else if(strcmp(argv[1], "log") == 0){ ans = log(v);

}else if(strcmp(argv[1], "exp") == 0){ ans = exp(v);

}else{

fprintf(stderr, "Undefined function : %s\n",argv[1]); exit(-1); } printf("Ans : %f\n", ans); exit(0); } 構造体および関数へのポインタを用いた例 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> struct { char *str; double (*func)(double); } funcMap[] = {

(7)

{"sin", sin}, {"cos", cos}, {"tan", tan}, {"log", log}, {"exp", exp}, {NULL, NULL} };

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

double v, ans; int i;

if (argc != 3) {

fprintf(stderr, "Please specify a function and a value\n"); exit(1);

}

v = atof(argv[2]);

for(i = 0; funcMap[i].str != NULL; i++){ if(strcmp(argv[1], funcMap[i].str) == 0){ ans = (*funcMap[i].func)(v); break; } } if(funcMap[i].str == NULL){

fprintf(stderr, "Undefined function : %s\n", argv[1]); exit(1); }else{ printf("Ans : %f\n", ans); exit(0); } }

(8)

問題

3

コマンドライン引数にファイル名を与え,ファイル内の大文字 (’A’∼’Z’) の数,小文字 (’a’∼’z’) の数,数 字の数 (’0’∼’9’),記号 (それら以外) の数,および総文字数 (バイト数) を標準出力に表示するプログラムを 書きなさい. 尚,コマンドライン引数にファイル名が指定されなかった場合には,標準エラー出力に “Usage : (実行 ファイル名) <file name>” と出力して終了しなさい.コマンドラインに 2 つ以上引数が与えられた場合に は,2 つめ以降のファイルは無視して構わない.また,指定されたファイルが開けなかった場合には,標準 エラー出力に “Cannot open file : (ファイル名)” と表示して終了しなさい.

オープンしたファイルはクローズしてから終了すること.

実行結果サンプル

sample10-3_1.txtおよび sample10-3_2.txt は~ren/Pro3_2008_Quiz/以下からコピーして使用する こと.

% ./a.out

Usage ./a.out <file name>. % ./a.out sample10-3_1.txt Upper-case alphabets : 11 Lower-case alphabets : 147 Numbers : 0 Marks : 39 Total : 197 % ./a.out sample10-3_2.txt Upper-case alphabets : 76 Lower-case alphabets : 801 Numbers : 0 Marks : 348 Total : 1225 % ./a.out hoge.txt

Cannot open file : hoge.txt

チェックポイント

• 実行結果サンプルと同様の実行結果が得られること. • 各エラーメッセージが問題文の通り,標準エラー出力に出力されること. • fopen を用いてファイルをオープンしていること. • fopen を用いてオープンしたファイルを,fclose を用いてクローズしていること.

ヒント

• “Usage ...” の出力において,実行ファイル名は argv[0] より取得することができる.

(9)

• ファイルのオープンは

FILE *fopen(const char *path, const char *mode);

を用いる.path にはファイルのパスを示す文字列を与える.mode にはオープン時のモードを示す文 字列を与え,代表的なものに読み込みを表す “r” や,書き込みを表す “w” がある.fopen 関数はファ イルのオープンに失敗すると,NULL を返す.(詳しくは man ページ参照)

• オープンしたファイルは, int *fclose(FILE *fp);

を用いてクローズする.fp には fopen で返された FILE 構造体へのポインタを与える.(詳しくは man ページ参照.)

• ファイルから一文字読み込むには, int *fgetc(FILE *fp);

を用いる.fp には fopen で返された FILE 構造体へのポインタを与える.戻り値は getchar 関数と同 様,int 型で文字が返される.(詳しくは man ページ参照.)

(10)

解答例

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

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

int c;

int lower, upper, number, mark; int total;

FILE *fp;

if (argc < 2) {

fprintf(stderr, "Usage %s <file name>.\n", argv[0]); exit(1);

}

if ((fp = fopen(argv[1], "r")) == NULL) {

fprintf(stderr, "Cannot open file : %s\n", argv[1]); exit(1);

}

total = 0;

lower = upper = number = mark = 0; while ((c = fgetc(fp)) != EOF) {

total++;

if(’a’ <= c && c <= ’z’){ lower++;

}else if(’A’ <= c && c <= ’Z’){ upper++;

}else if(’0’ <= c && c <= ’9’){ number++; }else{ mark++; } } fclose(fp);

printf("Upper-case alphabets : %d\n", upper); printf("Lower-case alphabets : %d\n", lower); printf("Numbers : %d\n", number); printf("Marks : %d\n", mark); printf("Total : %d\n", total); exit(0);

(11)

問題

4

コマンドライン引数に入力ファイル名 (0 個以上,複数可) と出力ファイル名 (0 個か 1 個) を与え,入力 に含まれる小文字 (’a’∼’z’) を大文字 (’A’∼’Z’) に変換して出力するプログラムを書きなさい. コマンドライン引数が 1 つも与えられない場合には,標準入力から文字列を読み込み,標準出力へ結果 を出力しなさい.コマンドライン引数が 1 つ与えられた場合には,出力ファイル名が与えられたものとみ なし,標準入力から文字列を読み込んで結果をファイルに出力しなさい.コマンドライン引数が 2 つ与え られた場合には,第 1 引数を入力ファイル,第 2 引数を出力ファイルとみなし,入力ファイルを読み込ん で結果を出力ファイルに出力しなさい.コマンドライン引数が 3 つ以上与えられた場合には,最後の引数 を出力ファイル,それ以外のファイルを入力ファイルとみなし,各々の入力ファイルからコメント範囲を削 除した結果を連結して,出力ファイルに出力しなさい. このとき,小文字から大文字へ変換する作業は以下の関数を定義して行いなさい. void toUpper(FILE *in, FILE *out);

(ファイルや標準入力からの読み込み,ならびにファイルや標準出力への出力ものこの関数内で行うこと.) 尚,入力ファイル,出力ファイルが各々オープンできない場合には,標準エラー出力に “Cannot open file : (ファイル名)” と出力して終了しなさい.

実行結果サンプル

sample10-4_1.txt,sample10-4_2.txt,sample10-4_3.txt は~ren/Pro3_2008_Quiz/以下からコピー して使用すること.

% echo ’test’ | ./a.out TEST

% echo ’Hello World!’ | ./a.out test.txt % cat test.txt

HELLO WORLD!

% ./a.out sample10-4_1.txt sample10-4_2.txt test2.txt % cat test2.txt

PETER PIPER PICKED A PECK OF PICKLED PEPPERS

LONDON BRIDGE IS FALLING DOWN, FALLING DOWN, FALLING DOWN.

% ./a.out sample10-4_3.txt sample10-4_2.txt sample10-4_1.txt test3.txt % cat test3.txt

(12)

• コマンドライン引数の数に応じて以下の動作をすること. 指定されない場合 標準入力から入力し,小文字を大文字に変換して,標準出力へ出力する. 1つの場合 指定されたファイルは出力ファイルとし,標準入力から読み込んで小文字を大文字に変換し,出 力ファイルに出力する. 2つ以上の場合 最後に指定されたファイルを出力ファイルとし,それ以外のファイルを入力ファイルとして,小 文字を大文字に変換した後,入力ファイルが指定された順に結果を連結して出力ファイルに出力 する.

ヒント

• ファイルへの出力は stdio.h に宣言されている fputc 関数を使用すると良い.fputc 関数のプロトタイ プ宣言は

int fputc(int c, FILE *stream);

であり,c には文字 (int 型),stream には fopen でオープンしたファイルの FILE 構造体へのポイン タを与える.

• 問題 1 のヒントに示したように,stdio.h には stdin,stdout,stderr という FILE 構造体へのポイン タが定義されており,各々標準入力,標準出力,標準エラー出力に対応している.つまり,fputc 関 数や fgetc 関数では,stream に与える引数によって,標準入力や標準出力,標準エラー出力や fopen 関数でオープンしたファイルを統一的に扱うことができる.

このため,toUpper 関数内で fgetc 関数,fputc 関数を使用して入力と出力を行えば,呼出側 (main 関 数) でコマンドライン引数の数に応じて toUpper 関数に与える引数 (stdin や stdout,もしくは fopen したファイルの FILE 構造体へのポインタ) を切替えることで,1 つの関数で標準入出力からの入力・ 出力とファイルからの入力・出力どちらにも対応させることができる.

(13)

解答例

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

void toUpper(FILE *in, FILE *out);

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

int i;

FILE *in, *out;

if(argc < 2){

toUpper(stdin, stdout); }else if(argc == 2){

if((out = fopen(argv[1], "w")) == NULL){

fprintf(stderr, "Cannot open file : %s\n", argv[1]); exit(1);

}

toUpper(stdin, out); fclose(out);

}else if(argc == 3){

if((in = fopen(argv[1], "r")) == NULL){

fprintf(stderr, "Cannot open file : %s\n", argv[1]); exit(1);

}

if((out = fopen(argv[2], "w")) == NULL){

fprintf(stderr, "Cannot open file : %s\n", argv[1]); exit(1); } toUpper(in, out); fclose(in); fclose(out); }else{

(14)

}

fclose(out); }

exit(0); }

void toUpper(FILE *in, FILE *out) {

int c;

while((c = fgetc(in)) != EOF){ if(’a’ <= c && c <= ’z’){ c = c - ’a’ + ’A’; } fputc(c, out); } }

(15)

問題

5

コマンドラインから与えられたファイル名から ID,名前,身長,体重をを読み込んで読み込んだ順と逆 順に出力するプログラムを作りなさい. 与えられるファイルの内容は, 1 Yamasaki 170 64 2 Ohmura 165 68 : のように,1 行づつ ID,名前,身長,体重がスペース区切りで順に並んでいる.また,データの終了はファ イルの終了 (EOF) によって表される.各データ (ID や名前,身長,体重それぞれの文字列) の長さは 40 文 字を越えないものと仮定して良い. プログラム内では,各人のデータは以下の構造体で表し,線形リストを用いてデータを管理すること.ま た,各データの領域は malloc 関数を用いて確保し,プログラム終了時,free 関数を用いて開放すること. 下記の構造体で,next はリスト構造での次のデータ (person 構造体) へのポインタとする.また,name は 名前を格納する文字列のためのポインタとする.

struct person_list{ strcut person *next; int id; char *name; int height; int weight; }; コマンドライン引数に何も与えられない場合には,標準エラー出力に “Uasage : (実行ファイル名) <file name>”と表示して終了しなさい.コマンドライン引数に与えられたファイルがオープンできない場合に は,標準エラー出力に “Cannot open file : (ファイル名)” と出力して終了しなさい.コマンドライン引数 が 2 つ以上の指定された場合には 2 つめ以降の引数は無視して構わない.

実行結果サンプル

sample10-5.txtは~ren/Pro3_2008_Quiz/以下からコピーして使用すること. % ./a.out sample10-5.txt

(16)

ID : 3 Name : Chishiro Height : 182 Weight : 65 ID : 2 Name : Ohmura Height : 165 Weight : 68 ID : 1 Name : Yamasaki Height : 170 Weight : 62

チェックポイント

• 実行結果サンプルと同様の実行結果が得られること. • 各エラーメッセージが問題文の通り,標準エラー出力に出力されること. • データをファイルから読み込んでいること. • プログラム内で,問題文にある person list 構造体を用いてデータを管理していること.また,線形リ ストを用いてデータを管理していること.

• person list の各要素は malloc 関数を用いてその領域を確保していること.

• また,malloc 関数により確保された領域を全て free 関数によって開放していること.

ヒント

• ファイルの読み込みは,fgets 関数や fgetc 関数を用いて行うことができるが,このように,空白区切 りやタブ区切りで複数のデータが並ぶ場合,fscanf 関数を用いると良い.fscanf 関数は stdio.h に

int fscanf(FILE *stream, const char *format, ...):

で宣言されており,stream に対象となるファイルの FILE 構造体へのポインタを指定する以外は scanf と同様である.また,ファイルの終了に到達した場合、EOF を返す.(詳しくは man ページ参照) 問題 1 のヒントにあるように,"%s"は 1 単語を対象とし,また,"%d"や"%f"などのフォーマットも 同様にスペース等の文字で区切られる. 例えば、 <数字> <数字> <数字> のようにスペース区切りで 3 つの数値 (整数とする) が並ぶ場合, int a,b,c; :

fscanf(fp, "%d%d%d", &a, &b, &c);

(17)

<数字> <文字列> <数字>

のような場合, int a,b; char str[40];

:

fscanf(fp, "%d%s%d", &a, &str[0], &c);

とすればそれぞれの数値と文字列を読み込むことができる.

• person list 構造体内の name は char 型へのポインタとなっているので,ここに直接文字列を格納する ことはできないため,別途文字列を格納するための領域を設ける必要がある.name へはこの領域へ のポインタを設定する.このような目的で,問題 1 で作成した mstrcpy を使用することができる.(終 了時,free 関数によって mstrcpy 関数内の malloc で確保した領域を開放することを忘れないこと.) • この問題では,読み込んだ順とは逆順に出力するため,読み込んだ順にノード (person list のデータ) を作成し,リストの先頭から追加していけば良い.例えば、リストの先頭のアドレスを保持する変数が

struct person_list *head = NULL;

として確保されているとすれば, struct person_list *p;

:

p = (strcut person_list *)malloc(sizeof(struct person_list)); /* pへのデータの設定 */ として領域確保およびデータを設定した後, p->next = head; head = p; のようにすれば良い. またリストの全てのノードをたどる時は、 for (p = head; p != NULL; p = p->next) {

(18)

解答例

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

#define MAX_STR 40

char *mstrcpy(const char *str);

struct person_list {

struct person_list *next; int id;

char *name; int height; int weight; };

struct person_list *list_head = NULL;

int main(int argc, char *argv[]) { FILE *fp; int id; char name[MAX_STR]; int height; int weight; struct person_list *p; if(argc < 2){

fprintf(stderr, "Usage : %s <file name>\n", argv[0]); exit(1);

}

if((fp = fopen(argv[1], "r")) == NULL){

fprintf(stderr, "Cannot open file : %s\n", argv[1]); exit(1);

}

while(fscanf(fp, "%d%s%d%d\n", &id, &name[0], &height, &weight) != EOF){ p = (struct person_list *)malloc(sizeof(struct person_list));

if(p == NULL){

fprintf(stderr, "Cannot allocate enough memory\n"); exit(1);

(19)

p->id = id; p->name = mstrcpy(&name[0]); p->height = height; p->weight = weight; p->next = list_head; list_head = p; } fclose(fp);

for(p = list_head; p != NULL; p = p->next){ printf("ID : %d\n", p->id);

printf("Name : %s\n", p->name); printf("Height : %d\n", p->height); printf("Weight : %d\n", p->weight); }

for(p = list_head; p != NULL; p = p->next){ list_head = p->next; free(p->name); free(p); } exit(0); }

char *mstrcpy(const char *str) {

char *p, *q;

int len = strlen(str);

p = q = (char *)malloc(len + 1); while(*str != ’\0’){

*p++ = *str++; }

参照

関連したドキュメント

各情報システムでは, Oracle , MySQL , PostgreSQL , Microsoft SQL Server , SQLite

が省略された第二の型は第一の型と形態・構

「文字詞」の定義というわけにはゆかないとこ ろがあるわけである。いま,仮りに上記の如く

7IEC で定義されていない出力で 575V 、 50Hz

(実被害,構造物最大応答)との検討に用いられている。一般に地震動の破壊力を示す指標として,入

デスクトップまたはスタートボタンの“プログラム”に 標準宅地鑑定評価システム 2023 のショートカ

当初申請時において計画されている(又は基準年度より後の年度において既に実施さ

長期入院されている方など、病院という枠組みにいること自体が適切な治療とはいえないと思う。福祉サービスが整備されていれば