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

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

ただいまの
回答率

90.62%

  • C

    3558questions

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

printfを挿入すると無限ループに陥る何てことありますか?

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 730

Teppay

score 171

文字列処理のプログラムを書いていて、
ある関数を呼び出す前にprintfを入れると無限ループに陥ります。
ちなみに
printf("\n");
これです。

どんな理由が考えられますか?

#include <stdio.h>
#include <string.h>
#include <ctype.h>

void shift(char str[], int n){
    int i = 0, j;
    for(j = 0; j < n; j++){
        while(str[i] != '\0'){
            str[i] = str[i+1];
            i++;
        }
        i = 0;
    }
}


void devideStr(char str[], char ret[], int s, int e){
    int i;
    for(i = s; i <= e; i++){
        ret[i - s] = str[i];
    }
    ret[i] = '\0';
}

int chemsymToMolWeight(char chemsym[]){
    int ret;
    if(strcmp(chemsym,"A") == 0){
        ret = 2;
    }
    if(strcmp(chemsym,"Bc") == 0){
        ret = 3;
    }
    if(strcmp(chemsym,"De") == 0){
        ret = 4;
    }
    if(strcmp(chemsym,"F") == 0){
        ret = 5;
    }
    return ret;
}

int strToMolWeight(char chemform[]){
    int MW=0, tmp, p = 0, c, n = 1;
    char chemsym[3], ret[50], num[50];
    while(chemform[0] != '\0'){
        if(isupper(chemform[0])){
            chemsym[p++] = chemform[0];
            shift(chemform, 1);
            if(islower(chemform[0])){
                chemsym[p++] = chemform[0];
                shift(chemform, 1);
            }
            chemsym[p] = '\0';
            p = 0;
            tmp = chemsymToMolWeight(chemsym);
        }else if(chemform[0] == '('){
            for(c = 1; chemform[c] != ')'; c++);
            devideStr(chemform, ret, 1, c-1);
            tmp = strToMolWeight(ret);
            shift(chemform, c+1);
        }
        if(isdigit(chemform[0])){
            while(isdigit(chemform[0])){
                num[p++] = chemform[0];
                shift(chemform, 1);
            }
            num[p] = '\0';
            n = atoi(num);
            p = 0;
        }
        tmp *= n;
        MW += tmp;
        n = 1;
        
        
    }
    return MW;
}

int main(){
    char chemform[] = "A2(A23Bc)2";
    printf("\n"); //ここです!
    int MW;
    MW = strToMolWeight(chemform);
    printf("MolWeight = %d\n",MW);
    return 0;
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • tkow

    2015/10/11 15:39 編集

    コードがなければ原因は特定出来ません。少なくとも関数がどういう処理をするものかくらいは書いてください。ループに入る前に書いてるのであればバッファオーバーフローでも起きてない限り無限ループにハマるようなことはまずないです。 ループの中で書いていた場合。コンソール上の文字列をreadlineで取得していた場合無限ループにハマる可能性はあります。

    キャンセル

  • Teppay

    2015/10/11 15:45

    すみません コード載せるの忘れてました。。

    キャンセル

  • tkow

    2015/10/11 17:18 編集

    確かにプログラム自体は問題なさそうですね。確認ですが\nを抜いたら動くんですよね? 開発環境としてOS,エディタまたはIDE,コンパイラは何を使っているか教えていただければと思います。 追記:解決したようで何よりです。

    キャンセル

回答 3

checkベストアンサー

+1

devideStrでNULLを代入していますが、位置が間違っているようです。

ret[i] = '\0'; → ret[i - s] = '\0'; ★★★

iの位置に代入すると、1文字多くなってしまいます。
ローカル変数であるため、ここを参照すると不定の値になっていまします。
このために、strToMolWeightでの英数字、数字、かっこにヒットせず。無限ループになっているのでは!?

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/10/11 16:36

    本当だ!!!!
    上のstr[i]に引っ張られていました。。
    ありがとうございます!

    ただ、printfを挿入しなければ、正常(笑)に動くというのが謎です。。。。

    キャンセル

  • 2015/10/11 16:43

    ローカル変数が初期化されていないため、何が入るかわかりません。
    printfを挿入することによって、ローカル変数を確保するスタックの位置が違ってくることが考えられます。
    printfの挿入によってたまたま、変な値に(笑)なってしまったのでは。
    では、頑張ってください。

    キャンセル

  • 2015/10/11 18:36 編集

    akiruno-oneoneさんの言う通りです。あんなに細かいバグをみつけるなんてすごいですね。printfで使われ解放されたスタック領域にretのバッファが作られていることが原因だと思います。バッファを確保した領域が事前に使われていなければnull値が入りますが使われたメモリ上にバッファを確保した場合上書き形式で格納されるので初期化をしないとバッファにデータが残ったまま参照されることになり、アルファベットでも数字でもnullでもないbyteデータに解釈されている可能性があります。このプログラムでは余裕を持って50文字でバッファを取っていますので使われなかった文字分の余分な領域を参照するとprintfの断片データにアクセスしてしまい、retの値が代入されていない\0を末尾としてそこから2番目の要素が条件に引っかからない記号や解釈出来ないbyteコードを読み込みループにはまっているということだと思います。
    どんなバイナリデータが入っていたかわからないので配列を作る時に初期化はしておかないといけないとよく言われるのはこういうことが原因です。
    printfがなければretのバッファが確保されるまで確保した変数が解放されることがなかったのでたまたま大丈夫だったって感じですね。

    キャンセル

  • 2015/10/11 23:40

    C言語での領域の確保なんかの仕組みまでわからないといけないんですね...
    奥が深い
    頑張ります...

    キャンセル

  • 2015/10/11 23:45

    もしこのコメントを見ていただけたら教えていただきたいのですが、
    自分が持っているC言語の本では、printfがスタック領域を使って...みたいな、メモリも絡んだような処理の仕組み?って全くのっていないんですけど、みなさんはどうやって勉強したんでしょうか?
    本が違いますか?

    キャンセル

  • 2015/10/12 09:16

    Teppayさん
    そのあたりの話はC言語の詳しい書籍にも載っていますが,アセンブラの話が食い込んできます。なのでアセンブラについての本の方が詳しく載っています。
    僕の場合は大学の講義で詳しく習いましたが値型のデータ(数値,アドレス値)等はスタックメモリ,参照型の実データの格納先はヒープ領域のメモリに保存され,メモリはスタック構造になっていて値を開放するときは(速度が遅くなるので)いちいち値を0に書き変えているわけではなくスタックポインタのアドレスをずらすのみで後から値が入った時に上書きするような設計になっています。スタックの説明は値が取りだされると図で空白にして表現されることが多いので誤解されがちなのですが実際は値が上書きされるまで残ったままになります。

    キャンセル

0

デバッガでブレイクポイントを張るなどして、どこで無限ループしているか調べるとよいかもしれません。
またint tmpや、int retが初期化されていないのも一つの要因かもしれません。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/10/11 16:21

    回答ありがとうございます!
    int tmpやint ret初期化して実行してみましたが、変化ありませんでした( ノД`)シクシク…

    キャンセル

0

chemformの配列サイズを指定してはどうでしょう?
確認してないので違ってるかもしれませんが。
配列の領域関係のバグは厄介なのが多いので良い方向だと思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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

関連した質問

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

  • C

    3558questions

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