Comments
Description
Transcript
Report#6(A)
プログラミングⅠ Report#6 提出期限:6月12日(木) 所属:情報工学科 学籍番号:085762K 氏名:谷津 和平 目次 ※ はじめに… …………P1 P2 ①変数とアドレスについて ②ポインタとは? ③配列とアドレスについて ④ポインタと文字列について ⑤ 授業内容 1. 大文字と小文字、小文字と大文字を置換するプログラム …………P3 P5 2. 文字列を反転して表示するプログラム …………P6 P9 感想&参考文献 …………P9 はじめに… ①変数とアドレスについて int a = 828; は、「メモリ上のある番地([例]1000番地)に変数aとしての領域を確保し、その領域に828を格納す る」ということ。 メモリ<---------------2byte---------------> aに割り振られたアドレス … 1000 ↑ 828を代入 a &a 変数aの値(828)を示す。 変数aのアドレス(1000)を示す。 ②ポインタとは? ポインタとはアドレス変数、すなわち変数のアドレスを記憶する変数のことです。 メモリ <------------------------2byte------------------------> aに割り振られたアドレス 1000 ¦ 記憶 --------------> ポインタ ③配列とアドレスについて char a[4]="ABC";のとき、 a メモリ <---------1byte---------> a → a[0] : 1000 番地 'A' a[1] : 1001番地 'B' a[2] : 1002番地 'C' 1[3] : 1003番地 '\0' 配列aの先頭要素のアドレス (1000) a[0] a[1] a[2] a[3] a[0]の値 ('A') &a[0] &a[1] &a[2] &a[3] a[0]のアドレス (1000) a[1]の値 ('B') a[2]の値 ('C') a[3]の値 ('\0') a[1]のアドレス (1001) a[2]のアドレス (1002) a[3]のアドレス (1003) 1000 ④ポインタと文字列について 文字列は、配列を使わずにメモリ上に取られた文字列のアドレスを直接ポインタに指定することができる。 (ⅰ) 1つの文字列をポインタで表す 1. char *p = "ABC"; 2. char *p; p = "ABC"; メモリ上のどこかに文字列"ABC"がとられ、その先頭番地がポインタに設定される。 (ⅱ) 複数の文字列をポインタの配列で表す char *LArc[2] = { "hide", "tetsu", }; メモリ上のどこかに文字列"hide","tetsu"がとられ、それぞれの先頭番地がポインタの配列の LArc[0],LArc[1]に設定される。 1byt e 'h' 'i' 'd' ↓ 2byt e LArc[0] ~~~授業内容~~~ **P = 値 *P = アドレスの中身 P = アドレス **argv = *argv[ ]<--値を持つ *argv = argv[ ] <--アドレス argv = argv <--アドレス 'e' '\0' 't' 'e' 't' 's' ↓ LArc[1] 'u' '\0' 1. コマンドラインから受け取った文字列の大文字と小文字を変換するプログラムを作成せよ。 【ソースコード ; replace01】 01 /* 02 Program 03 Author 04 comment 05 : : replace01.c Kazuhei Yatsu : 小文字→ 大文字 大文字→ 小文字 */ 06 07 #include <stdio.h> 08 #include <ctype.h> 09 10 /* 関数のプロトタイプ宣言 */ 11 void replace(char *dest, char *str); 12 13 /* main関数 */ 14 int main(int argc, char *argv[]) 15 { 16 int i; 17 char dest[1000]; 18 19 printf("~~~~~~~~~~~~~~~~~~~~~~~~~実行結果~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); 20 21 for(i = 1;i < argc; i++) 22 { 23 replace(dest,argv[i]); /* 配列destに文字列argv[i]を変換された文字列を保存 */ 24 printf("変換前 : %s\t\t",argv[i]); 25 printf("変換後 : %s\n",dest); 26 } 27 printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); 28 return(0); 29 } 30 31 /* replace関数 */ 32 void replace(char *dest, char *str) 33 { 34 35 int i; 36 for(i = 0; i < str[i]; i++) 37 { 38 if(isalpha(str[i])) 39 /* 文字str[i]がアルファベットのとき */ { 40 if(islower(str[i])) 41 /* 小文字のとき */ dest[i] = toupper(str[i]); 42 else if(isupper(str[i])) 43 /* 大文字のとき */ dest[i] = tolower(str[i]); 44 /* 大文字に… */ /* 小文字に… */ } 45 else 46 /* アルファベット以外はそのまま */ dest[i] = str[i]; 47 } 48 dest[i] = 0; 49 50 51 return; } 【解析 ; replace01.c】 ※[引数]dest : 変換結果の複写先, str : 変換対象の文字列 {strを変換し、destに変換結果を代入する。} ◆7~8行目 : #include <stdio.h>を記述したのは、printf関数を使用するため。 また、#include <ctype.h>を記述したのは、文字処理関数を使用するため。 ◆17行目 : char dest[828]は、変換後の文字列の保存先。 ◆21行目 : for(i = 1;i < argc; i++)は、コマンド名を除くすべての引数にアクセスする。i = 0 にしな いのは、実行するときの ./a.out が変換してしまうので i = 1 にした。 ◆23行目 : replace(dest,argv[i]);は、配列destに文字列argv[i]を変換した文字列を保存して、32行 目のvoid replace(char *dest, char *str)に飛ばす。 ◆32行目 : 23行目のreplace(dest,argv[i]);から値を受ける。 ◆36行目 : for(i = 0; i < str[i]; i++)より、i = 0としてるのは、上で./a.outを省いてるから。 i = 1にすると文字が変換しなくなる。 ◆48行目 : dest[i] = 0;を記述。配列destの最後には 0 が入ってないので入れた。 ■処理の手順は処理ごとにコメントでソースファイルに書き込んだ。 【実行結果 ; replace01.c】 [yatsu-wahei-no-macbook:~] yatsukazuhei% ./a.out AbC LArc82 tetsu ~~~~~~~~~~~~~~~~~~~~~~~~~~~~実行結果~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 変換前 : AbC 変換後 : aBc 変換前 : LArc82 変換後 : laRC82 変換前 : tetsu 変換後 : TETSU ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 【考察 ; replace01.c】 ◆このプログラムはコマンドラインから文字を受け取り、あらかじめ作っておいた同じ型の配列に変換関 数をかけた後コピーする。そして、その文字を表示するようになっている。 ◆ポインタを引数にとることによって、他の関数のローカル変数に演算結果を保存するのではなく、呼び出す側の変数 を置き換えられて、保存させることができるようになった。 ~~~~~プログラムの流れ~~~~~ argv[]には、コマンドラインより打ち込んだ文字が1文字ずつメモリに保存されている。そして、それらの文字は 各々アドレスを持っている。*argvには、*argv[]のそれぞれの先頭アドレスが入っている。つまり、++argvとメ モリを共有しているため、argvもまた*argvのショートカットとして使用されている。argvは、**argvのショー トカットのショートカットである。 構造的に、argvは、*argvにアクセスし、そこからまた**argvにアクセスするようになっている。また、3つと も同じメモリの領域を使用しているのでどれかを書き換えるとすべてに影響する。 2. 文字列を反転して表示するプログラムを作成せよ。 【ソースコード ; replace02.c】 01 /* 02 program : replace02.c 03 Author : Kazuhei YATSU 04 comment : 文字列を反転 05 */ 06 07 #include <stdio.h> 08 #include <ctype.h> 09 10 /* 関数のプロトタイプ宣言 */ 11 void reverse(char *dest,char *str,int t); 12 int get_n(char *LArc); 13 14 /* main関数 */ 15 int main(int argc,char **argv){ 16 int c; 17 char dest[256]; /*変換後の文字列の保存先*/ 18 printf("~~~~~~~~~~~~~~~~~~~~~~~~~~実行結果~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); 19 20 for(c = 1,argv++; *argv != NULL; argv++) { 21 c = get_n(*argv); 22 reverse(dest,*argv,c); 23 24 printf("反転対象 : %s\t",*argv); 25 printf("反転結果 : %s\n",dest); 26 27 28 printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); } return(0); 29 } 30 31 /* get_n関数 */ 32 int get_n(char *LArc) 33 { 34 int i; 35 for(i = 0;*LArc != 0; i++,LArc++); 36 37 } return(i); 38 39 /* reverse関数 */ 40 void reverse(char *dest,char *str,int t) 41 { 42 while(--t > 0) str++; 43 44 45 for( ;*str != 0; dest++,str--) *dest = *str; 46 47 *dest = 0; 48 } 【解析 ; replace02.c】 ◆12行目 : int型get_n(char *LArc)関数を宣言しておく。 ◆各々、引数にはポインタを使用している。 ◆15行目からはmain関数。コマンドラインが使用されている。 ◆最初にargcでコマンドラインの文字列(スペースで区切る)の数を表示した後、for文により何番目の文字列か" と 打ち込んだ文字が表示されている。 ◆40行目 : reverse関数。while文を使用し、変数tを-1ずつ減らし、その分だけstr++するようにしている。 ◆45行目 : strを-1ずつ減らし、NULLにたどり着いたら終了する。 ◆46行目 : -1する度にdestにアドレスをコピーするようにしている。 ◆48行目: 最後にNULLを足してあげる。 【実行結果 ; replace02.c】 [yatsu-wahei-no-macbook:~] yatsukazuhei% cc replace02.c [yatsu-wahei-no-macbook:~] yatsukazuhei% ./a.out AbC wXyZ Hide3 ~~~~~~~~~~~~~~~~~~~~~~~~~~実行結果~~~~~~~~~~~~~~~~~~~~~~~~~~ 反転対象 : AbC 反転結果 : CbA ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 反転対象 : wXyZ 反転結果 : ZyXw ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 反転対象 : Hide3 反転結果 : 3ediH ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 【考察 ; replace02.c】 ●文字列を後ろから順にコピーしていくプログラムで、実行結果より、うまく成立している。 後ろからコピーするために、サンプルプログラムに載ってたget_n関数を使った。プログラムのメインである reverse(反転)関数の中に、while文を加えるだけのシンプルな構造である。 ●今回のプログラムでは、strを入力の引数、destを出力の引数をとっている。 ●このプログラムの流れは、まず、get_n関数で文字の数を数える。そして、その文字の数の分だけ、argvを引き ながらdestに文字をコピーし、最後に、変換前と変換後を表示させてプログラムが終了している。 ~~~~~プログラムの流れ~~~~~ ① A b C NU LL w X y Z NU LL H i d e 3 H i d e 3 NU LL ※get_n関数で各々の文字数を調べた。その後、reverse関数に入る。 ② A b C NU LL w X y Z NU LL NU LL ※文字数だけ str++でアドレスを1つずつ進んでいく。 ③ str A b C b A NU LL w X y Z Z y X w NU LL H i d e 3 3 e d i H i H NU LL dest C NU LL NU LL NU LL ※for文でstr--する度に、*strの文字を*destにコピー。これで反転した文字になる。 ※str--する度に *dest = *str ④あとは表示されるだけ。 C b A NU LL Z y X w NU LL 3 e d NU LL 【感想】 今回のレポートは今まで一番クセがあって理解しにくい所が多かったです。 課題をやる前にポインタの勉強が最優先でした。 ソースを作ろうにもエラーが15行も出て、ホント苦労です。しかし、きれいに導いたときはうれしい感情が れま した。 C言語のポインタと配列の関係が思っていたより、関係が深かったことです。 今回の課題,ポインタやアドレスを使用したプログラムは、難しいと言われているので、みんなに負けないように頑 張っていきたいです。 【参考文献】 ●C実践プログラミング 第3版 谷口 功(訳) ●猫でもわかるC言語プログラミング 第2版 ●初心者のためのポイント学習C言語 ●苦しんで覚えるC言語 望月 康司(監訳) Steve Oualline(著) 井 康孝(著) http://www9.plala.or.jp/sgwr-t/ http://homepage3.nifty.com/mmgames/c_guide/