質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.32%

  • C

    3997questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C言語でメモリ解放について

解決済

回答 5

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 721

Alyn

score 15

 Cで領域を超えた文字列を処理したい。

領域を超えた場合に勝手に文字列を出力させなようにして、while文の繰り返し処理をさせる方法を教えてください。
free関数では溢れたものまでは処理できなくて困ってます。

 該当のソースコード

#include<stdio.h>
#include<stdlib.h>

int OverFlow(char *str)
{
    if (*str == NULL)
    {
        return -1;
    }
    return 1;
}
int main(void)
{
    char *mojiretu;
    int jdg = 0;

    while (jdg != 1)
    {
        mojiretu = (char *)malloc(5);
        if (mojiretu == NULL)
        {
            printf("メモリ確保失敗。\n");
            break;
        }

        printf("文字列を入力:");
        scanf_s("%s", mojiretu, 5);
        jdg = OverFlow(mojiretu);
        if (jdg == -1)
        {
            printf("領域を超えています。\n");
            free(mojiretu);
        }
    }
    if (jdg == 1)
    {
        printf("入力された文字列は%sです。\n", mojiretu);
    }

    return 0;

}

 追記

文字列を入力:abcdefgh
領域を超えています。
文字列を入力:123
入力された文字列は123です。

となるようなプログラムを作りたいです。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • cateye

    2018/07/19 23:42

    main()の終了で、プログラムが終了するので開放はされるのですが、returnの前にfree()で開放するほうがいいと思いますが?

    キャンセル

  • Alyn

    2018/07/19 23:43

    入力をやり直しさせたいのですが、どうしたらいいですか?

    キャンセル

  • asm

    2018/07/20 01:23 編集

    期待する動作・実際の動作を提示してほしいです。 abcdefと6文字入力すると"領域を超えています\nf" と出力されるのがイヤって事でしょうか?

    キャンセル

回答 5

+2

溢れたらそれでおしまい、です。
freeでもなにを使ったところで回復できません
溢れる前に、それを検出して止めるようにしないとダメです

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/17 23:02

    どうしたらよいでしょうか?

    キャンセル

  • 2018/07/18 08:26

    自分のコードの中で、溢れないようにしっかりデバッグする、しかないですね

    キャンセル

  • 2018/07/20 13:21

    scanfじゃなくてscanf_sなんだよなぁ

    キャンセル

checkベストアンサー

+1

y_waiwaiさんの回答からの流れでの回答を欲ししてるようなので便乗して。

C言語の場合、メモリを溢れさせないように常に気をつけないといけません。
mallocで5バイトだけ確保していても、余裕で領域オーバーして数百バイトでも書き込めます。
その代わり他のプロセスが使っているかもしれない領域を上書きしているのですから、何が起きてもおかしくはありません。
そのためにも予防は必要です。
例えば厳格な業務アプリなんかでは、今回の例のようにscanfで文字入力するようなことはまずありません。
なのでscanfを使うのはちょっとしたお試しプログラムを書く場合などと思っててください。
という前提で予防の話をしますと、最初に5バイトmallocしていますが、それとは別に大きな領域を用意して、scanfでの入力はそちらに取り込むようにします。
大きな領域とは今回の例では自分で入力するであろう最大の文字数です。10でも20でも100でもよいです。
そしてその入力されたものの文字数を数えて5バイトを超えていたら警告するような作りになります。

mojiretu = (char *)malloc(5);
work = (char *)malloc(100);
while(1){
    printf("文字列を入力:");
    scanf_s("%s", work, 100);
    if(strlen(work) < 5){
        break;
    }
    printf("領域を超えます。\n");
}
strcpy(mojiretu, work);


厳密には文字列の終端にはNULL文字が必要なので、mallocで5バイト確保していても4文字しか格納できません。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/18 11:07

    細かいことで申し訳ありませんが、
    一般的なメモリ保護を行うOS上であればプロセスは独立した論理空間を持ちますから、
    未確保の領域にアクセスしただけで他所のプロセスのメモリを上書きしてしまう可能性を危惧する必要はないかと思われます。

    キャンセル

  • 2018/07/18 11:09

    「プロセス」って書いちゃうとダメですね。
    指摘ありがとうございます。

    キャンセル

  • 2018/07/18 11:27

    Win9x系のカーネル領域は共有されてると噂には聞いた事がありますね

    キャンセル

  • 2018/07/18 14:43

    まー、Cが走る環境はWindowsとも限らないし、ましてやインテルCPU上とも限りませんし

    キャンセル

  • 2018/07/19 23:00

    これは領域を超えてしまうと終了ですか?
    入力をやり直すことはできないのですか?

    キャンセル

  • 2018/07/19 23:08

    終了です。そうなるとなにが起こっても文句言えません
    まあ、malloc(5)のときにホンマはもうちょっとたくさん確保しているってこともあるので、大丈夫なときもあるでしょうけど、それは保証できない世界になります

    キャンセル

  • 2018/07/20 08:30

    shibu-mさん
    >これは領域を超えてしまうと終了ですか?
    これってどれを指しているのですか?
    私が提示したコードでしょうか。
    であるなら単なるサンプルなので、もう一度入力させたいのであれば、mallocから下の処理をループで回せばよいかと。

    キャンセル

  • 2018/07/20 08:50

    もう一度入力をやり直そうとすると溢れた文字列が勝手に入力されて出力されてしまうので、自分で再入力させたいです。

    キャンセル

  • 2018/07/20 08:54

    コードを修正してみたのでご確認ください。

    キャンセル

+1

何が問題なのか良くわかりませんが、

printf("文字列を入力:");
mojiretsu[0] = '\0';//これを入れないといけないのでは?
scanf_s("%s", mojiretu, 5);
jdg = OverFlow(mojiretu);
if (jdg == -1)

としないと、
OverFlow(mojiretu);
で、-1 が帰ってこない可能性が多々あるように思いますが。

ついでに、
if (jdg == -1)
{
printf("領域を超えています。\n");
free(mojiretu);
}
の部分で読み込みきれなかった文字列を読み捨てる必要が無いでしょうか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/19 22:54

    mojiretuが4文字を超えてしまうと中身が\0となっていましたので、-1は返ってきますよ

    キャンセル

0

OverFlow()内 if(*str == NULL)は、文字の比較になっていませんか?

繰り返し入力させるなら、確保→入力→解放の繰り返し while()の条件見直し。
システム(OS)の空きメモリ開放は時間がかかるのでfree()推奨d^^。
あと・・・メモリが確保できなというのは考えづらい。
ちなみにうちの機械(linux:mem16GB)では、8GBぐらいなら余裕で確保できます。

「追記」
繰り返し入力&メモリ確保失敗と入力長異常に対応(題意にそぐわないなら無視希望)

usr~/test/c % cat ct.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define Forever for(;;)

int main(void)
{
    char *mojiretu;
    char buf[100];

    Forever{
        mojiretu = (char *)malloc(5);
        if (mojiretu == NULL)
        {
            puts("メモリ確保失敗。");
            break;
        }

        printf("文字列を入力:");
        fgets(buf,sizeof buf - 1, stdin);
        size_t len= strlen(buf);
        if( len > 5 ){
            puts("領域を超えています。");
            free(mojiretu);
            continue; // break→continue
        }
        buf[len-1]= '\0';   // 改行を削除
        strcpy(mojiretu,buf);
        printf("入力された文字列は%sです。\n", mojiretu);
        free(mojiretu);
    }

    return 0;
}

usr~/test/c % ./a.out 
文字列を入力:1234567
領域を超えています。
文字列を入力:abc
入力された文字列はabcです。
文字列を入力:1234
入力された文字列は1234です。
文字列を入力:12345
領域を超えています。
文字列を入力:0
入力された文字列は0です。
文字列を入力:^C
usr~/test/c % 


一部修正しました、ただ、これだとメモリ確保に失敗しないと終了しません、

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/20 00:26

    すみません、初心者なので全然わからなくて…

    どんなコードですか?

    キャンセル

  • 2018/07/20 00:32

    まず、if(*str == NULL)をif(str == NULL)にして試してみて下さい。

    キャンセル

  • 2018/07/20 00:55

    環境がわからないのでなんとも言えませんが、デバッガないですか?・・・ 変数のプリントを仕込むこともできますよd^^

    キャンセル

0

C言語の標準入力の仕様です。

対応策は

  • scanf/scanf_sを使用したらgetcで改行もしくはEOFを検知するまで読み取る
  • %*[^\n]を用いて読み飛ばしを行う
  • fflush(stdin);でバッファクリアできる環境もある(規格上定義された動作ではない)
  • fgetsを用いて1行入力した後にsscanf/sscanf_sを用いる

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.32%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C

    3997questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。