![](http://oshiete.xgoo.jp/images/v2/pc/qa/question_title.png?e8efa67)
ccalloc関数を用いてメモリを確保し、15文字以下の名前をキーボードから入力を10人分繰り返し、その後にすべての名前を出力をしなさい、ただし配列は使用しないものとする。
という問題があるのですが、calloc関数を習ったことがなくうまくいかず、エラーかnullが出力されます。どなたか教えてください、お願いします。
*********ソース*********
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
int i;
char *str;
char *buf;
/*メモリの確保*/
str = (char *)calloc(110, sizeof(char ));
/*メモリが確保出来なかった時*/
if(str == NULL){
printf("メモリの確保ができません。");
exit(1);
}
/*キーボードからの入力*/
for(i = 0; i < 10 ;i++){
gets(buf);
*str = *buf;
str++;
}
/*名前の出力*/
for(i = 0; i < 10 ;i++){
printf("%s", *(str));
str++;
}
/* メモリの開放 */
free(str);
}
No.1ベストアンサー
- 回答日時:
とりあえず根本的な問題は中ほどにある
gets(buf);
*str = *buf;
str++;
だな。配列不許可という事だが、じゃあどこまでは許容されるんやろね。C言語においては、配列はメモリのオフセットに他ならない(←ちょっと難しい話)ので、解釈によっては難しいな。
さし当たっては配列っぽい表現を使わない方法、つまり、君のソースを部分的に修正する方法を検討してみようか。
まず、「15文字以下を10人分」という事で、メモリは160バイト必要だ。理由は、C言語の文字列は文字列+Null文字の1バイト分余計に必要だからだ。以下、「文字」は全て半角文字を意味する。
str = (char *)calloc(160, sizeof(char ));
で、最初に指摘した箇所がなぜいけないかというと、
gets(buf); /* bufを確保してない! 大変危険 */
*str = *buf; /* これでは、入力してもらった最初の1文字目だけをstrのの先頭にコピーするという意味になる */
str++; /* これでは、strが指し示す次の文字に移動する事になる */
さらに、これの最もいけない点は、「最初にcallocで確保してstrで指し示したメモリの場所が途中から分からなくなる」というものだ。
なんか説明しづらいな・・・・ソースをどんと載せようか。
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
int i;
char *str; /* こいつはずっとcalloc(160)を指し示す */
char *tmp; /* こちらを10人分保存用に使う */
char *buf; /* 入力バッファ */
char *buftmp; /* こっちはコピー用 */
tmp = str = (char *)calloc(160, sizeof(char));
buf = (char *)calloc(16, sizeof(char)); /* ちゃんとbufも確保してあげる */
if(str == NULL || buf == NULL){
printf("メモリの確保ができません。");
exit(1);
}
for(i = 0; i < 10 ;i++){
gets(buf);
buftmp = buf; /* 下2行と合わせて C言語で文字列のコピーは、自前ではこうしないといけない */
while(*buftmp) { *tmp++ = *buftmp++; }
*tmp++ = '\0';
}
tmp = str; /* 保存されている先頭に戻る */
for(i = 0; i < 10 ;i++){
printf("%s\n", tmp); /* 文字列として表示するために、*tmpではなくtmpを使う */
while(*tmp++); /* 下1行と合わせて 次のデータを読み出すために頭だし */
tmp++;
}
free(buf);
free(str);
}
これで一応君が望む動きはするはずだ。できればソース掲載以外の方法でアドバイスをしたかった(そのまんま課題の答えになってしまうし、方法論に広がりが無くなる)のだが、ちょっと今時間が足りないので勘弁していただきたい。後はゆっくりそれぞれの行が何の意味でどんな動きをしているのか吟味すべし。特に、「文字と文字列の違い」、「ポインタと++」に注目しよう。
No.6
- 回答日時:
主だった問題は既に回答が出ているようなので、それ以外について書きます。
> ただし配列は使用しないものとする。
> printf("%s", *(str));
"%s" も立派な「文字配列」です。
No.5
- 回答日時:
1. void main()ではなく、int main(void)
2. gets(3)は、バッファオーバーフローの危険があるので使ってはいけない関数です。fgets(3)を使うべき。
3. void *calloc(size_t nmemb, size_t size)は、 size バイトの要素 nmemb 個からなる配列にメモリを割り当てる関数です。この場合は16byteの要素を持つ配列10個を確保するのだから、calloc(10, 16)が本来の使い方。
4. コメントを入れるより、プログラムの可読性をあげるほうが良い。
No.3
- 回答日時:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int
main(void)
{
int i;
char *str;
char *buf, *p;
if ((buf = (char *) calloc(1, BUFSIZ)) == NULL
|| (str = (char *) calloc(10, 16)) == NULL) {
printf("メモリの確保ができません。");
exit(1);
}
for (i = 0, p = str; i < 10; i++, p += 16) {
printf("%2d:", i+1);
fgets(buf, BUFSIZ, stdin);
buf[strlen(buf) - 1] = '\0';
strncpy(p, buf, 15);
}
for (i = 0, p = str; i < 10; i++, p += 16) {
printf("%s\n", p);
}
free(buf);
free(str);
return(0);
}
No.2
- 回答日時:
とりあえず、無理やり直してみた。
ポイントだけ整理して、どこがいけないのかよく検討してみてください。
1.アロケートサイズ・・・110の根拠が不明( 15 + 1(ストッパ分)* 10)
2.gets()関数の仕様を見直そう。
3.ポインタシフト・・・・なぜ1Byteずつ?
4. アロケートポインタをシフトして、なぜそのポインタに対してFree()してしまっているの?
{
int i;
char *str;
char *work;
char buf[16];
/*メモリの確保*/
str = (char *)calloc(16 * 10, sizeof(char ));
/*メモリが確保出来なかった時*/
if(str == NULL){
printf("メモリの確保ができません。");
exit(1);
}
work = str;
/*キーボードからの入力*/
for(i = 0; i < 10 ;i++){
memset(buf, 0x00, sizeof(buf));
gets(buf);
strncpy(str, buf, strlen(buf));
str += 16;
}
str = work;
/*名前の出力*/
for(i = 0; i < 10 ;i++){
printf("%s\n", str);
str+=16;
}
/* メモリの開放 */
str = work;
free(str);
exit(0);
}
上記のものでも、かなりおかしいですよ。
バッファのオーバーフロー等は、演習用の仕様だとしてもアロケータポインタをシフトするのは
あまりよろしくないですね。
今回の場合であれば、インデクス(添え字)をしようすればよいのでは・・・・
そのあたりを考慮して、作り直せば勉強になると思います。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・ゆるやかでぃべーと タイムマシンを破壊すべきか。
- ・「I love you」 をかっこよく翻訳してみてください
- ・歩いた自慢大会
- ・許せない心理テスト
- ・字面がカッコいい英単語
- ・昔のあなたへのアドバイス
- ・かっこよく答えてください!!
- ・あなたが好きな本屋さんを教えてください
- ・スタッフと宿泊客が全員斜め上を行くホテルのレビュー
- ・【大喜利】【投稿~8/27】 こんなガソリンスタンド二度と来るか!なぜそう思った?
- ・これ何て呼びますか Part2
- ・人生で一番思い出に残ってる靴
- ・【お題】動物のキャッチフレーズ
- ・【お題】甲子園での思い出の残し方
- ・ゆるやかでぃべーと すべての高校生はアルバイトをするべきだ。
- ・「それ、メッセージ花火でわざわざ伝えること?」
- ・自分用のお土産
- ・人生で一番お金がなかったとき
- ・一番好きなみそ汁の具材は?
- ・泣きながら食べたご飯の思い出
- ・ちょっと先の未来クイズ第1問
- ・ゴリラ向け動画サイト「ウホウホ動画」にありがちなこと
- ・初めて自分の家と他人の家が違う、と意識した時
- ・単二電池
- ・チョコミントアイス
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C言語のintとcharの違いってな...
-
callocについて
-
char型にint型の数値を代入する。
-
CStringからchar*への型変換に...
-
苦しんでCを読んでchar型は文字...
-
SetWindowTextについて。
-
C言語でポインターで詰まってい...
-
char*を初期化したいのですが
-
文字列についていい方法はあり...
-
char 文字列型 の表現範囲が-12...
-
C言語で文字列をかえす正しい書...
-
C++17で、unsigned char * 配列...
-
固定長レコードから構造体につ...
-
小数点入りの文字列をfloat型に...
-
fstream型オブジェクトを関数の...
-
共用体について
-
DWORDとcharの変換
-
C言語にて構造体のメンバがNULL...
-
2次元配列の文字"列"の初期化方法
-
配列のアドレス渡し
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
char*を初期化したいのですが
-
C言語にて構造体のメンバがNULL...
-
C言語のintとcharの違いってな...
-
CStringからchar*への型変換に...
-
C言語のプログラムについてです
-
小数点入りの文字列をfloat型に...
-
new charとnew char[N]の違いは?
-
char型にint型の数値を代入する。
-
エクセルのMID関数は、C言語では?
-
fstream型オブジェクトを関数の...
-
動的メモリの初期化方法について。
-
const char* s1とただのchar s1...
-
char 文字列型 の表現範囲が-12...
-
文字列の途中から途中までを抽出
-
strcat関数を自作したいです
-
2次元配列の文字"列"の初期化方法
-
DWORDとcharの変換
-
C言語で文字列をかえす正しい書...
-
szとlpszの違い
-
fgetc( )の戻り値はなぜ整数??
おすすめ情報