2017-03-20 15 views
3

私は会社から2つの質問に直面しました。 2つの質問が私を混乱させた。 誰でも回答の理由を説明できますか?文字列と文字に関するインタビュー質問

  1. 結果を書いてください。

    void Test(void){ 
        char *str = (char *) malloc(100); 
        strcpy(str, “hello”); 
        free(str); 
        if(str != NULL){ 
        strcpy(str, “world”); 
        printf(str); 
        } 
    } 
    

    回答:それは出力 "世界"

  2. が結果を書き込みます。

    char *GetMemory(void){ 
        char p[] = "hello world"; 
        return p; 
    } 
    
    void Test(void){ 
        char *str = NULL; 
        str = GetMemory(); 
        printf(str); 
    } 
    

    回答:ポインタが無効なため、出力が不明です。

+5

1. UB 2. UB ..... – BLUEPIXY

+2

最初の質問に対する回答は正しくありません。制約違反を実行するときは何も予測できません。 – StoryTeller

+3

[*未定義の動作*](https://en.wikipedia.org/wiki/Undefined_behavior)についてお読みになることをお勧めします。 –

答えて

9

両方の結果がundefined behaviourです。

最初は、フリーズした後にポインタ(str)を使用しているためです(free()は、ブロックの後にNULLへのポインタをNULLに設定できません)。

2番目の理由は、別の関数のローカル変数へのポインタを使用しているためです。

+0

あなたは質問された質問に答えていません。質問者は、最初のケースでUBであるにもかかわらずコードが "world"という単語を印刷する理由を知りたいと考えています。 – JeremyP

+2

"質問者はなぜそれがUBであるのか知りたがっています" - それは暴言のように聞こえる(またはUBが何であるかを理解していない)。 – usr

+0

質問者にコード断片が与えられ、 "world"という単語が印刷されたことが伝えられました。私はCコンパイラ(OS Xのclang)で検証し、 "world"という単語も表示しました(ただし、最後に改行がないため、Testを呼び出した後にstdoutをフラッシュしなくてはなりませんでした)。 「未定義の振る舞い」は、「毎回ランダムなやり方をします」という意味ではなく、実装が好きなことを何でもできることを意味します。 – JeremyP

1

どちらの例も、「未定義の動作」の古典的なケースです。最初のケースでは、あなたがそれを解放した後、mallocされたメモリを使用しています。 2番目のケースでは、メモリがスタック上にある文字列へのポインタを返します。これは、GetMemoryの戻り値が実行されるとすぐに無効になります。

しかし、「未定義の動作」とは、コンパイラが好きなことを行うことができ、どちらの場合でも現実にはコンパイラがメモリを再利用できるようにすることです。

最初のケースでは、メモリを解放すると、ブロックはただちに空きリストに置かれ、後で再利用されます。あなたはそれにstrcpyを見つけることができますが、mallocのフリーリストを破損する可能性のあるコストで見つけることができます。しかし、mallocが新しいメモリを割り当てようとしない限り、これは問題になりません。また、strcpyがあれば、mallocが別の場所にメモリを再割り当てしない限り、そのまま置かれます。これは、一部の実装(おそらくほとんどの実装)では、最初の例が実際に "world"を印刷する理由です。ただし、この機能が大きなプログラムの一部である場合は、後で説明できないクラッシュが発生する可能性があります。

2番目のケースでは、文字列のメモリがスタックから割り当てられます。この場合、printfにはスタック領域が必要なため、文字列の文字を含むメモリを上書きする可能性があります。 printfはおそらくガベージ文字列を出力しますが、最初のパラメータとして渡すので、ガベージ文字列で見つかった書式指定子を置換しようとします。だからあなたはゴミを拾うかもしれないし、クラッシュするかもしれない。

+1

いいえ、どちらの場合でも 'str'の値は不定です。 'str == NULL'でも未定義の振る舞いをします。 –

+0

@AnttiHaapalaあなたは私の答えを完全に読まなかった。定義されていない動作は、特定の実装に対して完全に予測できない動作を意味するものではありません。コンパイラで最初の例を実行してみてください( 'Test'を呼び出した後にstdoutがフラッシュされるようにする必要があります)、おそらく" world "が出力されます。 – JeremyP

+0

@JeremyP、あなたの詳細と明確な説明のために多くのおかげで! – user2933783

4

両方の例には、オブジェクトの存続期間が終了した後で同じタイプのエラーが含まれています。

このようにすると、定義されていない動作が発生します。テキストは基本的にオブジェクトへのアクセスまたは前記オブジェクトの識別子の使用を意味する「被参照」という言葉を使用する。

両方の例で実際に未定義の動作が起こるのは、上記の規則ではありません。ですが、密接に関連しています。寿命が終了したオブジェクトを指すポインタの値は、不確定値です。このような値を読むと、未定義の動作が発生します。これは両方の例で起こります。

最初の例では、strの有効期間はfreeを呼び出すときに終了します。ポインタはifステートメントif(str != NULL){で使用され、未定義の動作が発生します。

第2の例では、pの有効期間は、関数が戻るときに終了します。返されたポインタは、ポインタstr:str = GetMemory();に割り当てられ、未定義の動作が発生します。


(より引用:ISO/IEC 9899:201Xオブジェクトの6.2.4保管期間2)
オブジェクトがその 寿命の外部参照される場合、動作は未定義です。

が(より引用:ISO/IEC 9899:オブジェクトの201X 6.2.4保管期間は、2)それが(または単に過去の)ポイントオブジェクトを ときポインタの
値は不定となる端部に到達しますその生涯の