malloc関数を使ってじゃんけんプログラムの履歴を表示する方法

このQ&Aのポイント
  • malloc関数を使ってじゃんけんプログラムの履歴を表示する方法について教えてください。
  • 現在のプログラムでは、malloc関数を使用してユーザーとコンピュータが出した全ての手を履歴として表示しようとしていますが、うまくいかないようです。
  • また、malloc関数を使わずに履歴表示をする方法もあるのでしょうか?
回答を見る
  • ベストアンサー

malloc関数の使い方

このじゃんけんプログラムに、ゲーム終了時ユーザーとコンピュータが出した全ての手を履歴として表示したいのですがmalloc()関数を使って表示させるとしたらどういったプログラムになりますか。 *aと*b、int stage=0を最初に宣言して void rireki(void) { a = (int *)malloc(sizeof(int) * (draw_no+lose_no+win_no)); a[stage++] = comp; b = (int *)malloc(sizeof(int) * (draw_no+lose_no+win_no)); b[stage++] = user; } 関数を作り、最後にprintf文で書いたのですがうまくいきませんでした。 hd[a[i]]という風に配列に配列を入れたのが駄目だったかもしれません。 for(i=0; i<draw_no+lose_no+win_no; i++){ printf("%d回目 ユーザ:%c コンピュータ:%c\n", i+1, hd[b[i]], hd[a[i]]); } また本の課題なのですが、malloc関数は後に出てくるのでmalloc関数を使わずに履歴表示できるかもしれないのですが その場合配列をあらかじめ宣言して格納する手段になるのでしょうか。 よろしくお願いします。 /* 課題3-6 */ #include <time.h> #include <stdio.h> #include <stdlib.h> int user; /* プレーヤの手 */ int comp; /* コンピュータの手 */ int win_no; /* 勝った回数 */ int lose_no; /* 負けた回数 */ int draw_no; /* 引き分けた回数 */ char *hd[] = {"グー", "チョキ", "パー"}; /* 手 */ /* initialize関数の宣言 */ void initialize(void); /* jyanken関数の宣言 */ void jyanken(void); /* count_no関数の宣言 */ void count_no(int result); /* disp_result関数の宣言 */ void disp_result(int result); /* confirm_retry関数の宣言 */ int confirm_retry(void); /* メイン関数 */ int main(void) { int judge; /* 勝敗 */ int retry; /* もう一度 */ initialize(); /* 初期処理 */ do{ jyanken(); /* じゃんけん実行 */ /* コンピュータとプレーヤの手を表示 */ printf("私は%sで、あなたは%sです。\n", hd[comp], hd[user]); judge = (user - comp + 3) % 3; /* 勝敗を判定 */ count_no(judge); /* 勝/負/引分け回数を更新 */ disp_result(judge); /* 判定結果を表示 */ retry = confirm_retry(); }while(retry == 1); printf("%d勝%d敗%d分けでした。\n", win_no, lose_no, draw_no); return (0); } /*--- 初期処理 ---*/ /* initialize関数の定義 */ void initialize(void) { win_no = 0; /* 勝った回数 */ lose_no = 0; /* 負けた回数 */ draw_no = 0; /* 引き分けた回数 */ srand(time(NULL)); /* 乱数の種を初期化 */ printf("じゃんけんゲーム開始!!\n"); } /*--- じゃんけん実行(手の読み込み/生成) ---*/ /* jyanken関数の定義 */ void jyanken(void) { int i; comp = rand() % 3; /* コンピュータの手 (0~2) を乱数で生成 */ printf("\n\aじゃんけんポン …"); for(i=0; i<3; i++) printf(" (%d)%s", i, hd[i]); printf(":"); scanf("%d", &user); /* プレーヤの手を読み込む */ } /*--- 勝/負/引き分回数を更新 ---*/ /* count_no関数の定義 */ void count_no(int result) { switch(result){ case 0: draw_no++; break; case 1: lose_no++; break; case 2: win_no++; break; } } /*--- 判定結果を表示 ---*/ /* disp_result関数の定義 */ void disp_result(int result) { switch(result){ case 0: puts("引き分けです。"); break; /* 引き分け */ case 1: puts("あなたの負けです。"); break; /* 負け */ case 2: puts("あなたの勝ちです。"); break; /* 勝ち */ } } /*--- 再挑戦するか確認 ---*/ /* confirm_result関数の定義 */ int confirm_retry(void) { int x; printf("もう一度しますか … (0)いいえ (1)はい:"); scanf("%d", &x); return (x); }

質問者が選んだベストアンサー

  • ベストアンサー
  • Wr5
  • ベストアンサー率53% (2177/4070)
回答No.1

>void rireki(void) >{ >a = (int *)malloc(sizeof(int) * (draw_no+lose_no+win_no)); >a[stage++] = comp; > >b = (int *)malloc(sizeof(int) * (draw_no+lose_no+win_no)); >b[stage++] = user; >} コールされるのはどのタイミングでしょう? そして、2回目以降にコールされた時、前回確保したaとbはどうなるでしょう。 # さらにその時のcompとuserには何が入っているでしょう? そして、rireki()がコールされるとstageがコール前より「2つ」増えています。 # 上記の場合だと、b[]への代入はバッファオーバーランしている可能性が高いです。 >hd[a[i]]という風に配列に配列を入れたのが駄目だったかもしれません。 1回目はa[0]には正しい履歴が入ります。 2回目にrireki()をコールした時に前回確保したaとbがメモリリーク(free()で開放する為のポインタが上書きにより失われる)し、a[]への登録がバッファオーバーラン(a[2]に書き込みしようとします。stageが2ですから)、a[0]とa[1]は「不定値」が残っています。 # malloc()は確保したメモリ内容に関してなにもしません。(calloc()なら0クリアしてくれますが…) 繰り返していって…… hd[a[0]]を参照した時に不定値になっていますので、高確率でhd[]の範囲外をアクセスしprintf()内で吹っ飛びます。 単純な方法だと… malloc()ではなくrealloc()で領域を「拡張」する。 以前記録した履歴(メモリ内容)は拡張後も保証されます。 stageの加算は履歴に「追加後」に行う必要があります。 ただし、realloc()は失敗する可能性もあるのでその辺りの対処が必要。 # a = (int *)realloc(a, sizeof(int) * (draw_no+lose_no+win_no)); # という使い方は正しくない。 メモリのフラグメントが発生しやすくなる。 realloc()を繰り返すのはコストが高い。 手間を掛けるのであれば、リスト構造体にして追加していく。 というのもありますな。 # 片方向リストで十分でしょう。

tanosiiC
質問者

お礼

mallocではなくreallocというものを使ってやってみれば良いんですね。 勘違いしていました。ありがとうございます。

tanosiiC
質問者

補足

回答ありがとうございます。 言われてみると酷い間違えばっかでした。 do~while文の前に a = (int *)malloc(sizeof(int) * (draw_no+lose_no+win_no)); b = (int *)malloc(sizeof(int) * (draw_no+lose_no+win_no)); rireki関数の呼び出しはjyanken関数の後に宣言してみました。 /* rireki関数 */ void rireki(void) { a[stage] = comp; b[stage] = user; stage++; } この場合履歴の表示まで実行できたのですが、グーなどの文字ではなく違う結果になってしまいました。

その他の回答 (4)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.5

まずは「こうすればもっと (私の主観に基づいて) いいプログラムになる」という指摘: ・「双方の出した手」を構造体で覚えておくと楽. ・グー/チョキ/パーを表す数値や勝敗を表す数値はいわゆる「マジックナンバー」なので enum なり #define なりする. ・勝敗を決めるときに judge = (user - comp + 3) % 3 のような謎の式を直接書かない. 配列を参照するか, せめて関数にする. ・勝敗のカウンタは配列にして count_no を捨てる. メッセージも配列にすれば disp_result も消せる. ・再挑戦するかどうかを確認する関数がコメントでは confirm_result なのに実際の定義が confirm_retry なのはおかしい. そもそも「~関数の定義」などというコメントは百害あって一利なし. ・過去の手を覚えておく変数にはそれなりな名前を与える. 「ふつ~の人」が a とか b とかいう変数名を見て「これが過去の手を覚えているんだ」と思えるでしょうか? で realloc を使うとすれば, 「1回やるたびに realloc」はさすがにコストが馬鹿にならないので #2 で触れられているように「大きめの領域を確保しておき, 足りなくなったら realloc」の方が速度は稼げる. 連結リストにするかどうかは.... まあ今回の場合は好みだな. ランダムアクセスしたいなら連結リストは外すだろうけど, シーケンシャルアクセスしかしないっぽいので連結リストもないわけじゃない. あと恒例の突込み (苦笑) ですがメモリ確保系の関数で「0バイトを確保」したときに何が返るかは処理系依存>#3. NULL が返っても文句は言えないです.

tanosiiC
質問者

お礼

アドバイスありがとうございます。 細かいところまで目が利くようになれるように頑張りたいです。 judge = (user - comp + 3) % 3は今やっている参考書からの引用です。 resultとretryは単純にタイプミスです。すいません。 ~関数の定義は、入門書の方で毎回書かれていたので一応書いてました。 aとかの変数名は適当になってました。

  • Wr5
  • ベストアンサー率53% (2177/4070)
回答No.4

>draw_no+lose_no+win_noの値に気付き、do~while文の後に >a = (int *)malloc(sizeof(int) * (draw_no+lose_no+win_no)); >b = (int *)malloc(sizeof(int) * (draw_no+lose_no+win_no)); ループ抜けた後では無意味です。 # それまでの履歴がないでしょう? ループ中でmalloc()すれば、最初と同じくメモリリークする上に以前のデータが引き継がれません。 別にmalloc()して、以前のデータを新しい領域にコピーして、以前の領域をfree()して……とかやるくらいならrealloc()を使えば済むハナシです。 実装によって異なりますが…realloc()で拡張する場合にやっているのは…… ・元のメモリブロックの後ろに要求された空きがあればメモリブロックを拡張して終わり。 ・空きがない場合は拡張後のサイズでメモリ確保し直して、元の領域のデータをコピーし、以前の領域はfree()。 ・拡張後のサイズでメモリ確保できない場合はなにもしないでNULLを返却し、失敗を通知。 って感じの処理を行っています。 # 縮小する場合は、たいてい成功するのでメモリブロック縮めて終わり。(溢れたデータについては保証しない) よって…勝負回数が不明である以上、malloc()1回で履歴を残す。というのは無理です。 # 大きい領域を確保すれば…にしてもそれを越えればアウトですし。(何度も書かれてますけど)

tanosiiC
質問者

お礼

ありがとうございます。 realloc()でやってみようと思います。 reallocの動作の説明までしてもらいありがとうございます。

  • Wr5
  • ベストアンサー率53% (2177/4070)
回答No.3

>do~while文の前に >a = (int *)malloc(sizeof(int) * (draw_no+lose_no+win_no)); >b = (int *)malloc(sizeof(int) * (draw_no+lose_no+win_no)); メインループの前に(draw_no+lose_no+win_no)はいくつになりますか? >rireki関数の呼び出しはjyanken関数の後に宣言してみました。 「宣言してみました」は正しくないのですが… rireki()をコールすると、a[]とb[]への書き込みでヒープを破壊していきます。 勝負の回数が増えたところで、メインループの前にmalloc()で確保した領域が(サイズにsizeof(int) * (draw_no+lose_no+win_no)を指定しているから)自動的に増えて行くなんてコトはありません。 malloc()した時点での各変数の値を参照するだけですから、 その後でdraw_noやlose_no、win_noを増やしてもmalloc()で確保した領域のサイズは増えません。 メインループの前では(draw_no+lose_no+win_no)は0でしょうから、「サイズ0のメモリブロック」を確保しただけになります。 そこにデータを追加していけば待っているのは領域破壊(バッファオーバーラン)だけです。 固定的にサイズを取得しても、それを越えればやはりバッファオーバーランです。 >この場合履歴の表示まで実行できたのですが、グーなどの文字ではなく違う結果になってしまいました。 バッファオーバーランで破壊していますから、原理的には何が起こっても不思議ではない…です。 あと…この程度のプログラムならmalloc()等での動的確保を開放せずにプログラム終了しても問題ないでしょうが、free()でちゃんと開放するように習慣づけた方がいいでしょう。

tanosiiC
質問者

補足

なんども入門書を読み直してるうちに自動的に増えるということに、勘違いしていたことに気付きました。 draw_no+lose_no+win_noの値に気付き、do~while文の後に a = (int *)malloc(sizeof(int) * (draw_no+lose_no+win_no)); b = (int *)malloc(sizeof(int) * (draw_no+lose_no+win_no)); としてみましたが、rireki関数には格納できないのでたぶんどこで宣言してもmalloc関数ではできないのでしょうか。 free()関数は終わりに書いてあります。 free(a); free(b); と記述しています。

  • vector01
  • ベストアンサー率50% (1/2)
回答No.2

こんにちは。業界でメインプログラマしている者です。 ソース拝見しました。rireki()を具体的にどこで呼んでいるのかが不明確ですが、恐らく1勝負毎ですね。 (do whileの最後辺り) 1回戦う毎に結果をメモリに残しておいて最後にprintfと言う事なんでしょうが、これは設計が分かれる所です。 大別すると、 1.初めに固定長バッファ(主さんの言う所の配列)を用意して決まった回数勝負する。 2.じゃんけんの回数を決めたくないから勝負する度にメモリ領域を確保してそこにしまっておく。 主さんの理想は2かと思います。 rireki()の呼び方が上述の通りと仮定すると、まずrireki()内でmallocしているのが問題です。 1勝負毎に別の空きメモリ空間に、その時の勝負回数分のサイズを別途用意してしまいます。 (1勝負する度にどんどんメモリリークする) この場合、2を実現するだけならmallocではなくreallocを使うのが現実的ですが、私はあまりお勧めしません。 (物によっては処理負荷が大きく、メモリの断片化も懸念されるので、業務の性質上、部下がやっていたらやめさせます) ただこの位の規模のじゃんけんゲームなら負荷も気にする必要は無いでしょうし、練習と言う意味では有りです。 reallocを使っての2の設計をもう少し細かくした別のアプローチとして、 ・1戦する度にreallocでは無く、予めmallocでN回分の領域を取っておいて、N回を超えたらもうN回分realloc と言う形でリアロケートの頻度を少なくする事で処理負荷と断片化を抑制する方法が考えられます。 実装効率、動的性と負荷を考えるとバランスはこう取るのが妥協ラインかと思います。後はNをどの位にするかでしょう。 もう少し進んでC++になるとVectorやlistにプッシュしていく。と言う技術をその内目にすると思います。 C++標準のSTLで実現する場合、内部ロジックはコンパイラによりますが、大体N回おきにreallocです。 reallocが嫌なら別途mallocした領域(Nより大きい)に今の内容をコピーして次の受け皿とする。 その後今まで使っていた領域をfreeで解放する。 とも出来ますが、結局断片化等の理由でアロケートサイズが足りなければmallocは失敗します。 私がもし、業務でやるならこうしますと言う案で言うと、 メモリアロケータやlistから自作出来るのであれば、list内部にアロケート機構を一切絡ませずに動的生成部分は外に出す。 その後、じゃんけんの度に勝負1回分をmallocしてlistに繋いでいく。と言う方法が取れます。 このじゃんけんの結果を格納するメモリをページ分けしておけば、その部分の断片化は起こらなく出来ます。 その為メモリをかなり有効に活用でき、延々と履歴を取れる様なイメージには近づけられると思います。 ただメモリは有限なので、どんなに頑張ってもNの回数を無限には出来ません。 無限にしたいならキュー構造等を使って規定回数を超えたら古い物から削除して新しい物を上書き。 とする事で妥協する位です。 少し話が難しくなったかも知れませんが、まずはreallocを使って実装してみるのが良いのでは無いでしょうか。 それが出来る事で、reallocの問題点を理解・認識して次に進めれば良いと思います。

tanosiiC
質問者

お礼

reallocについては入門書には全く触れられていなかったので今の段階ではよくわかりませんが、今やっている本に載っているみたいなのでこれからみてみようと思います。 ありがとうございます。

関連するQ&A

  • realloc関数の使い方

    前回のmalloc関数の使い方の続きみたいな感じです。 参考書にはmalloc関数とcalloc関数については載っていましたがrealloc関数については記述はありませんでした。 realloc関数はメモリの拡張や縮小ができるというみたいなのでdo~while文の中に入れています。 どこが間違っているのでしょうか。 /* 課題3-6 */ #include <time.h> #include <stdio.h> #include <stdlib.h> int user; /* プレーヤの手 */ int comp; /* コンピュータの手 */ int win_no; /* 勝った回数 */ int lose_no; /* 負けた回数 */ int draw_no; /* 引き分けた回数 */ int *a; int *b; int *a1; int *b1; int i; int stage = 0; char *hd[] = {"グー", "チョキ", "パー"}; /* 手 */ /* initialize関数の宣言 */ void initialize(void); /* jyanken関数の宣言 */ void jyanken(void); /* count_no関数の宣言 */ void count_no(int result); /* disp_result関数の宣言 */ void disp_result(int result); /* confirm_retry関数の宣言 */ int confirm_retry(void); /* rireki関数の宣言 */ void rireki(void); /* メイン関数 */ int main(void) { int judge; /* 勝敗 */ int retry; /* もう一度 */ initialize(); /* 初期処理 */ a = (int *)calloc(5, sizeof(int)); b = (int *)calloc(5, sizeof(int)); do{ jyanken(); /* じゃんけん実行 */ /* コンピュータとプレーヤの手を表示 */ printf("私は%sで、あなたは%sです。\n", hd[comp], hd[user]); judge = (user - comp + 3) % 3; /* 勝敗を判定 */ count_no(judge); /* 勝/負/引分け回数を更新 */ disp_result(judge); /* 判定結果を表示 */ retry = confirm_retry(); a1 = (int *)realloc(a, sizeof(int) * (draw_no+lose_no+win_no+1)); b1 = (int *)realloc(b, sizeof(int) * (draw_no+lose_no+win_no+1)); rireki(); }while(retry == 1); for(i=0; i<draw_no+lose_no+win_no; i++){ printf("%d回目 ユーザ%c コンピュータ%c\n", i+1, hd[b1[i]], hd[a1[i]]); } printf("%d勝%d敗%d分けでした。\n", win_no, lose_no, draw_no); free(a); free(b); return (0); } /*--- 初期処理 ---*/ /* initialize関数の定義 */ void initialize(void) { win_no = 0; /* 勝った回数 */ lose_no = 0; /* 負けた回数 */ draw_no = 0; /* 引き分けた回数 */ srand(time(NULL)); /* 乱数の種を初期化 */ printf("じゃんけんゲーム開始!!\n"); } /*--- じゃんけん実行(手の読み込み/生成) ---*/ /* jyanken関数の定義 */ void jyanken(void) { int i; comp = rand() % 3; /* コンピュータの手 (0~2) を乱数で生成 */ printf("\n\aじゃんけんポン …"); for(i=0; i<3; i++) printf(" (%d)%s", i, hd[i]); printf(":"); scanf("%d", &user); /* プレーヤの手を読み込む */ } /*--- 勝/負/引き分回数を更新 ---*/ /* count_no関数の定義 */ void count_no(int result) { switch(result){ case 0: draw_no++; break; case 1: lose_no++; break; case 2: win_no++; break; } } /*--- 判定結果を表示 ---*/ /* disp_result関数の定義 */ void disp_result(int result) { switch(result){ case 0: puts("引き分けです。"); break; /* 引き分け */ case 1: puts("あなたの負けです。"); break; /* 負け */ case 2: puts("あなたの勝ちです。"); break; /* 勝ち */ } } /*--- 再挑戦するか確認 ---*/ /* confirm_result関数の定義 */ int confirm_retry(void) { int x; printf("もう一度しますか … (0)いいえ (1)はい:"); scanf("%d", &x); return (x); } /*--- 履歴の表示 ---*/ /* rireki関数 */ void rireki(void) { a1[stage] = comp; b1[stage] = user; stage++; }

  • malloc関数の使い方について(初心者)

    膨大なデータ数を扱うためにmalloc関数を用いて配列にデータを格納し、それを表示するプログラムを考えました。コンパイルはできるのですが、実行するとエラーが起こります。freeの開放の仕方が間違っているのでしょうか?よろしくお願いします。 /*ソース*/ #include <stdio.h> #include <stdlib.h> #define N 130000 int main(void) { double *p; int i; p = malloc(N); if(!p){ printf("割り当てエラー"); exit(1); } for(i=0;i<N;i++){ p[i]=i; printf("%f\n",p[i]); } free(p); }

  • malloc関数について。

    typedef struct a{ char *simei; int nenrei; }MEIBO; void main(void) { MEIBO a[5]; int wa; for(i = 0; i <= 4; i++ ) { a[i].simei = malloc(100 * sizeof(char) ); if( a[i].simei == NULL ) /* 領域確保に失敗したか */ { printf( "%dバイトの領域確保に失敗", 100 * sizeof(char) ); return 1; } printf("%d番目を入力してください\n",i+1); scanf("%s",&a[i].simei); printf("%s\n",a[i].simei); } C言語初心者です。これで入力したa[i].simeiが表示されないのですが、間違いを指摘していただけますでしょうか。宜しくお願いします。

  • C言語 じゃんけんswicth case 関数化

    急いでいて大変困っています じゃんけんゲームを作成したのですが コンピュータの出す手、5つのパターン (完全ランダム コンピュータが絶対勝つ コンピュータが絶対負け 絶対引き分け 絶対引き分け無い) を作成して その5つのパターンを関数化して呼び出すようにする。 というのを作成したいのですが 一応下記のプログラムまでは進められましたが switch() case を関数化してint main(void)の外に書き込み それぞれ呼び出せるようにしたいです。 PCの調子が悪く調べてもあまり出ず本当に困っています 私ならこう、というプログラムをご教授お願いします 作成したプログラム #include <stdio.h> #include <stdlib.h> #include <time.h> char *table[]={"グー","パー","チョキ"}; int main(void) ★int mainここから { int i,j; int continue_f; int player,computer; int p_win,p_lose,p_draw; int t_win,t_lose,t_draw; t_win=t_lose=t_draw=0; srand(time(NULL)); do{ p_win=p_lose=p_draw=0; for(i=0;i<5;i++){ while(1){ printf("あなたが出した手は・・・\n"); printf("グー:0 パー:1 チョキ:2\n"); scanf("%d",&player); if(0<=player&&player<=2){ break; }else{ printf("もう一度入力してください。\n"); } } switch(0){//()の中を変更して下記のcaseを呼び出す★switch caseここから case 0: computer=rand()%3; break; case 1: computer=(player+1)%3; break; case 2: computer=(player+2)%3; break; case 3: computer=player; break; case 4: do{ computer=rand()%2; }while(computer==player); break; }               ★swicth caseここまで if((player+1)%3==computer){ printf("あなたは%sでわたしは%sです・・・わたしの勝利です。\n", table[player],table[computer]); p_lose++; }else if(player==computer){ printf("あなたもわたしも%sです・・・引き分けです。\n",table[player]); p_draw++; }else{ printf("あなたは%sでわたしは%sです・・・あなたの勝利です。\n", table[player],table[computer]); p_win++; } } printf("%d勝 %d敗 %d引き分けでした。\n",p_win,p_lose,p_draw); printf("このまま続けますか?続ける場合は何か数字を入力し、" "続けない場合は-1を入力してください。\n"); scanf("%d",&continue_f); t_win+=p_win; t_lose+=p_lose; t_draw+=p_draw; }while(continue_f!=-1); printf("トータルで %d勝 %d敗 %d引き分けでした。\n",t_win,t_lose,t_draw); return 0; }    ★int main ここまで

  • ポインタのポインタの関数受け渡しについて

    現在ポインタのポインタを利用したプログラムを作成しています。 main関数で int **dt; と宣言したとして、配列のセットにはset関数を、 表示に関する処理をpt関数で行いたいと思っています。 void set(int ??); void pt(int ???); int main(void){ int **dt; set(??); pt(???); } void set(int ??){ dt = (int**)malloc(sizeof(int*) * k); for(i = 0; i < k; i++){ dt[i] = (int*)malloc(sizeof(int*) * k); } のように配列サイズの動的確保が目的 } void pt(int ???){ 二重forループ{ printf(dt[i][j]); } } ??、???には何を入れるべきかが理解できません。 ご教示のほどよろしくお願いいたします。

  • malloc関数(strtok関数の自作版)につきまして分からないこと

    malloc関数(strtok関数の自作版)につきまして分からないことがあります。 以下のプログラムにmallo関数がフリーする最適な位置を明示しなさいといわれました。 文字が分離した時にfreeすると助言されたのですが いまいち理解できません・・・。 条件式の中で使用するともいわれていました。(おそらくif文・・・。) 色んな意見を参考にしたいので詳しい方助言のほうよろしくおねがいします。 なおプログラムはほかの箇所を変更したり、他の場所でもmalloc関数を使用することが認められています。 またfreeする場所はメイン関数ではなくあくまでもstrtok関数の中で宣言するようです。 よろしくお願いします。 #include <string.h> #include <stdio.h> #include <stdlib.h> main(){ char* s2 = ",/"; char* result; char* r1; char* r2; char* r3; char* r4; result = strtok("//123//,45/,678,9/","/,"); r1 = strtok(NULL, s2); r2 = strtok(NULL, s2); r3 = strtok(NULL, s2); r4 = strtok(NULL, s2); printf("%s\n",result); printf("%s\n",r1); printf("%s\n",r2); printf("%s\n",r3); printf("%s\n",r4); return; } char *strtok(char *s1, const char *s2) { int i,len; char *str1, *str2 , *str3; static char *tok ; static char* mstr; if(s1 != NULL) { str1 = s1; } else { str1 = tok; } str2 = str1 + strspn(str1, s2); /* strspnを利用 */ if (*str2 == '\0') { return (NULL); } len = 1; i = 0; while(*(str2 + i) != '\0'){ len++; i++; } mstr = (char*)malloc(sizeof(char)*len); if(mstr == (NULL)) { return 0; } i = 0; while(*(str2 + i) != '\0'){ *(mstr + i) = *(str2 + i ); i++; } *(mstr + i ) = '\0'; str3 = mstr + strcspn(mstr, s2); /* strcspnを利用 */ if (*str3 != '\0'){ *str3 = '\0'; str3 = str3 + 1; } tok = str3; return (mstr); }

  • 関数について

    C言語の関数プログラムで、月を入力すると季節を表示する関数を作っているのですが、上手くいきません・・・。何か違うのでしょうか。 #include <stdio.h> void month(int a) { switch(a) { case '3': case '4': case '5': printf("春です。"); break; case '6': case '7': case '8': printf("夏です。"); break; case '9': case '10': case '11': printf("秋です。"); break; case '12': case '1': case '2': printf("冬です。"); break; default: printf("そのような月はありません。"); break; } } int main(void) { int m; printf("月を表示してください:"); scanf("%d",&m); month(m); return 0; }

  • c言語のmalloc関数と2次元配列について

    ・mallocとreallocのAPPを作成しています、下記は単純化しました。 「質問-1」 ・while(1){...以下を無効にした場合、正常に終了します。 ・有効にして、最初に999を入力した場合、エラー表示されます。 ・この理由が分かりません。 「質問-2」 ・有効にして、初期数値(例えば11)を入力の場合、正常表示されます ・続けて数値(例えば15)を入力した場合、エラー表示されます。 ・この理由が分かりません。 ***************************************************************  #include <stdio.h>  #include <stdlib.h>  void MylnOut(void);  int **map;  int X=10,Y=10,i,j; //************************************************************** // MAIN //************************************************************** int main() {  char str[64]={""};  char *s="変更数値を入力(999で終了).... "; /* 2次元配列確保と初期表示 */  map=(int **)malloc(sizeof(int *)*X);  for(i=0;i<X;i++)   map[i]=(int *)malloc(sizeof(int)*Y);  MylnOut(); /* 変更数値入力 */ // while(1){ //  printf(s); //  gets(str); //  X=atoi(str); //  if(X==999) break; /* 領域変更と表示 */ //  map=(int **)realloc(map,sizeof(int *)*X); //  for(i=0;i<X;i++) //   map[i]=(int *)realloc(map[i],sizeof(Y)); //  MylnOut(); // } /* 領域開放 */   for(i=0;i<X;i++) free(map[i]);   free(map);   return 0; } //************************************************************** // 入力・表示 //************************************************************** void MylnOut(void) {  for(j=0;j<Y;j++)   for(i=0;i<X;i++) map[i][j]=55;   for(j=0;j<Y;j++){    for(i=0;i<X;i++) printf("%3d",map[i][j]);    printf("\n");   } }

  • mallocのプログラム

    以下の問いの回答を教えてください。 1.malloc関数を用いて巨大な配列を確保して、 その配列に書き込みを行うようなプログラムを作成しなさい。 int *buf; buf = (int*)malloc(SZ*sizeof(int)); if (buf == NULL) { perror("malloc"); exit(1); } for (i = 0; i < SZ; i++) buf[i] = i; 2.プログラムで、配列のサイズをさまざまに変えた時、 major pagefault と minor pagefault の回数がどうなるか調べなさい。 2. で調べた最大サイズの他、3~4通りのサイズで試すこと。 困ってますよろしくお願いします。

  • C言語 じゃんけんゲーム 非常に困っています

    じゃんけんゲームを作成したのですが コンピュータの出す手を5つのパターンを作成して その5つのパターンを関数化して呼び出すようにする。 というのを作成したいのですが検索などしましたが コンピュータの調子が悪く、それらしい物が見つからず、 何方か無礼ですが自分ならこうするという模範解答お願いします… 一応下記のプログラムまでは進められましたが switch()case を関数化してゲーム前に入力して5つのパターンを それぞれ呼び出せるようにしたいです… (例:ゲーム前に「パターンを選択:5でランダム、6でコンピュータ絶対勝つ…(省略)」 を表示させて入力数字でパターンを呼び出し、開始する) 5つのパターン(コンピュータの手) 完全ランダム コンピュータが絶対勝つ コンピュータが絶対負け 絶対引き分け 絶対引き分け無い 作成したプログラム #include <stdio.h> #include <stdlib.h> #include <time.h> char *table[]={"グー","パー","チョキ"}; int main(void) { int i,j; int continue_f; int player,computer; int p_win,p_lose,p_draw; int t_win,t_lose,t_draw; t_win=t_lose=t_draw=0; srand(time(NULL)); do{ p_win=p_lose=p_draw=0; for(i=0;i<5;i++){ while(1){ printf("あなたが出した手は・・・\n"); printf("グー:0 パー:1 チョキ:2\n"); scanf("%d",&player); if(0<=player&&player<=2){ break; }else{ printf("もう一度入力してください。\n"); } } switch(i){ case 0: computer=rand()%3; break; case 1: computer=(player+1)%3; break; case 2: computer=(player+2)%3; break; case 3: computer=player; break; case 4: do{ computer=rand()%2; }while(computer==player); break; } if((player+1)%3==computer){ printf("あなたは%sでわたしは%sです・・・わたしの勝利です。\n", table[player],table[computer]); p_lose++; }else if(player==computer){ printf("あなたもわたしも%sです・・・引き分けです。\n",table[player]); p_draw++; }else{ printf("あなたは%sでわたしは%sです・・・あなたの勝利です。\n", table[player],table[computer]); p_win++; } } printf("%d勝 %d敗 %d引き分けでした。\n",p_win,p_lose,p_draw); printf("このまま続けますか?続ける場合は何か数字を入力し、" "続けない場合は-1を入力してください。\n"); scanf("%d",&continue_f); t_win+=p_win; t_lose+=p_lose; t_draw+=p_draw; }while(continue_f!=-1); printf("トータルで %d勝 %d敗 %d引き分けでした。\n",t_win,t_lose,t_draw); return 0; }

専門家に質問してみよう