
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で質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
- C言語・C++・C# c言語配列の結合についてです。 なぜうまくいかないのでしょうか。 #include <stdio.h 4 2022/05/30 22:42
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
- C言語・C++・C# str[j++]の意味 2 2022/08/30 16:20
- Excel(エクセル) 2つのVBAを一緒にしたら機能しなくなりました(エクセル) 7 2022/06/02 12:41
- C言語・C++・C# sprintf()の使い方について 1 2022/08/17 16:16
- C言語・C++・C# c言語でユーザ関数を利用して入力された文字列を反転させるプログラムを作りたいです。 3 2023/01/29 19:47
- C言語・C++・C# C言語で再起関数とポインタを用いて文字列反転をする方法がわかりません。 4 2023/04/29 20:32
- C言語・C++・C# カードシャッフルのブログラムを使ってc言語でブラックジャックをしたい 2 2022/04/12 15:13
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
CStringからchar*への型変換に...
-
C言語のintとcharの違いってな...
-
fstream型オブジェクトを関数の...
-
C言語にて構造体のメンバがNULL...
-
new charとnew char[N]の違いは?
-
全体をNULLクリアするより、最...
-
小数点入りの文字列をfloat型に...
-
文字列置換のアルゴリズムを教...
-
fread()関数とfwrite()関数につ...
-
csvファイルを構造体に格納した...
-
コマンドライン引数 *argv[]は...
-
動的メモリの初期化方法について。
-
C++17で、unsigned char * 配列...
-
ポインタを使って回文かどうか...
-
char 文字列型 の表現範囲が-12...
-
C言語でポインターで詰まってい...
-
'\\0'とはなんですか?
-
入力された文字列の中の数字を...
-
関数から配列を返すには?
-
C言語 配列の長さの上限
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
CStringからchar*への型変換に...
-
char*を初期化したいのですが
-
C言語のintとcharの違いってな...
-
char型にint型の数値を代入する。
-
C言語にて構造体のメンバがNULL...
-
fgetc( )の戻り値はなぜ整数??
-
char 文字列型 の表現範囲が-12...
-
fstream型オブジェクトを関数の...
-
C++17で、unsigned char * 配列...
-
DWORDとcharの変換
-
文字列内の数字削除
-
new charとnew char[N]の違いは?
-
文字型配列に格納した空白の切捨て
-
C++Builder 2009 テキスト...
-
csvファイルをfscanfで読み込む...
-
文字列の途中から途中までを抽出
-
[C] 構造体メンバーのカンマ区...
-
エクセルのMID関数は、C言語では?
-
小数点入りの文字列をfloat型に...
-
wsprintf( ポインタ , "%d" , "...
おすすめ情報