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を探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・人生のプチ美学を教えてください!!
- ・10秒目をつむったら…
- ・あなたの習慣について教えてください!!
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・【大喜利】【投稿~9/18】 おとぎ話『桃太郎』の知られざるエピソード
- ・街中で見かけて「グッときた人」の思い出
- ・「一気に最後まで読んだ」本、教えて下さい!
- ・幼稚園時代「何組」でしたか?
- ・激凹みから立ち直る方法
- ・1つだけ過去を変えられるとしたら?
- ・【あるあるbot連動企画】あるあるbotに投稿したけど採用されなかったあるある募集
- ・【あるあるbot連動企画】フォロワー20万人のアカウントであなたのあるあるを披露してみませんか?
- ・映画のエンドロール観る派?観ない派?
- ・海外旅行から帰ってきたら、まず何を食べる?
- ・誕生日にもらった意外なもの
- ・天使と悪魔選手権
- ・ちょっと先の未来クイズ第2問
- ・【大喜利】【投稿~9/7】 ロボットの住む世界で流行ってる罰ゲームとは?
- ・推しミネラルウォーターはありますか?
- ・都道府県穴埋めゲーム
- ・この人頭いいなと思ったエピソード
- ・準・究極の選択
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
CStringからchar*への型変換に...
-
C言語のintとcharの違いってな...
-
char*を初期化したいのですが
-
C言語にて構造体のメンバがNULL...
-
DWORDとcharの変換
-
fstream型オブジェクトを関数の...
-
小数点入りの文字列をfloat型に...
-
new charとnew char[N]の違いは?
-
char 文字列型 の表現範囲が-12...
-
文字型配列に格納した空白の切捨て
-
続ポインタによる関数への配列渡し
-
SetWindowTextについて。
-
char[]をDWORDに格納するには
-
char *name1[4] とchar name2[]...
-
カンマで区切った文字の抽出に...
-
VC++ char[10]へのCString値の代入
-
動的メモリの初期化方法について。
-
C#に取り組んでいる初心者です.
-
固定長char型のvector配列を作...
-
2次元配列の文字"列"の初期化方法
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
CStringからchar*への型変換に...
-
char*を初期化したいのですが
-
C言語のintとcharの違いってな...
-
C言語にて構造体のメンバがNULL...
-
DWORDとcharの変換
-
小数点入りの文字列をfloat型に...
-
new charとnew char[N]の違いは?
-
char 文字列型 の表現範囲が-12...
-
C言語の文字リテラル中の16進文...
-
C++17で、unsigned char * 配列...
-
fstream型オブジェクトを関数の...
-
char型にint型の数値を代入する。
-
2次元配列の文字"列"の初期化方法
-
strcat関数を自作したいです
-
C言語で文字列をかえす正しい書...
-
C++Builder 2009 テキスト...
-
動的メモリの初期化方法について。
-
C言語のプログラムについてです
-
入力された文字列の中の数字を...
-
エクセルのMID関数は、C言語では?
おすすめ情報