アプリ版:「スタンプのみでお礼する」機能のリリースについて

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);
}

A 回答 (6件)

とりあえず根本的な問題は中ほどにある


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);
}

これで一応君が望む動きはするはずだ。できればソース掲載以外の方法でアドバイスをしたかった(そのまんま課題の答えになってしまうし、方法論に広がりが無くなる)のだが、ちょっと今時間が足りないので勘弁していただきたい。後はゆっくりそれぞれの行が何の意味でどんな動きをしているのか吟味すべし。特に、「文字と文字列の違い」、「ポインタと++」に注目しよう。
    • good
    • 1

主だった問題は既に回答が出ているようなので、それ以外について書きます。



> ただし配列は使用しないものとする。

> printf("%s", *(str));

"%s" も立派な「文字配列」です。
    • good
    • 0

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. コメントを入れるより、プログラムの可読性をあげるほうが良い。
    • good
    • 0

ANo.2です。


もたもたしてたら、anmochiさんが全て解決してくれてました。
ソース掲載前の事柄をよく理解されるよう・・・

がんばってください。
    • good
    • 0

#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);
}
    • good
    • 0

とりあえず、無理やり直してみた。


ポイントだけ整理して、どこがいけないのかよく検討してみてください。

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);
}

上記のものでも、かなりおかしいですよ。
バッファのオーバーフロー等は、演習用の仕様だとしてもアロケータポインタをシフトするのは
あまりよろしくないですね。
今回の場合であれば、インデクス(添え字)をしようすればよいのでは・・・・

そのあたりを考慮して、作り直せば勉強になると思います。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!