2012-02-27 10 views
14
#include<stdio.h> 

int main() 
{ 
    char *name = "Vikram"; 
    printf("%s",name); 
    name[1]='s'; 
    printf("%s",name); 
    return 0; 
} 

出力は出力されず、セグメント化エラーが発生します。私はGDBでそれを実行した場合でも、私は、次の取得 -printf()とセグメンテーションフォールトの実行

Program received signal SIGSEGV, Segmentation fault. 
0x0000000000400525 in main() at seg2.c:7 
7  name[1]='s'; 
(gdb) 

をこれは、プログラムが7行(明らかに私は一定のchar配列に書き込むことはできません)にSEG障害を受けることを意味します。では、なぜ行番号6のprintf()が実行されないのですか?

+0

私はあまりよく分かりません。 OSX Lionを実行している私のMac(LLVMに準拠、LLDBでデバッグ)で期待通りに動作します。 –

答えて

30

これは、ストリームバッファリングがstdoutであるためです。 fflush(stdout)を実行するか改行を印刷しない限り、"\n"出力はバッファリングされます。

この場合、バッファがフラッシュされて印刷される前にセグメンテーションが行われます。

あなたは代わりに、これを試すことができます。

printf("%s",name); 
fflush(stdout);  // Flush the stream. 
name[1]='s';   // Segfault here (undefined behavior) 

か:

printf("%s\n",name); // Flush the stream with '\n' 
name[1]='s';   // Segfault here (undefined behavior) 
+6

'fflush'が本当に正しい方法であることに注意してください - 改行はフラッシュを引き起こすことは保証されていません(前にその動作に噛まれています)。 –

4

を使用すると、セグメンテーションフォールトを取得している理由は、Cの文字列リテラルのみC標準に従って読んでいるということです、そしてあなたリテラル配列 "Vikram"の2番目の要素に "s"を書き込もうとしています。

出力が得られない理由は、プログラムが出力をバッファリングし、バッファをフラッシュする可能性があるためにクラッシュするためです。 stdioライブラリの目的は、printf(3)のようなフレンドリなフォーマット関数を提供することに加えて、メモリ内バッファにデータをバッファリングし、必要に応じて出力をフラッシングするだけで、時折入力を実行することによって、常に代わりに。実際の入出力は、通常はstdio関数を呼び出す時点ではなく、出力バッファがいっぱい(または入力バッファが空)のときにのみ発生します。

FILEオブジェクトが(stderrのように)常にフラッシュされるように設定されていても、一般的にはそれが要点です。

デバッグしている場合は、クラッシュする前にデバッグ出力がフラッシュされるようにstderrにfprintfを設定することをお勧めします。

9

まず、printfsを "\ n"(または少なくとも最後のもの)で終了する必要があります。しかしそれはセグメンテーションとは関係ありません。

コンパイラがコードをコンパイルすると、バイナリを複数のセクションに分割します。読み取り専用のものもあれば、書き込み可能なものもあります。 読み取り専用セクションに書き込むと、segfaultが発生することがあります。 文字列リテラルは、通常、読み取り専用セクションに配置されます(gccは ".rodata"に配置する必要があります)。 ポインタ名がそのroセクションを指しています。したがって、あなたが使用する必要があります

const char *name = "Vikram"; 

私はいくつかの "may" "should"を使用しました。動作は、OS、コンパイラ、およびコンパイル設定によって異なります(リンカスクリプトでセクションが定義されます)。 GCCのコマンドラインに

-Wa,-ahlms=myfile.lst 

を追加

は、生成されたアセンブラコードでmyfile.lstと呼ばれるファイルを生成します。 上部には、文字列はビクラムであることを示している

.section .rodata 
.LC0: 
    .string "Vikram" 

を見ることができます。

同じコードを使用(それは配列ではなくポインタで気づく、他のGCCはスタック上に格納することができる、グローバルスコープでなければならない)

char name[] = "Vikram"; 

.data 
    .type name, @object 
    .size name, 7 
name: 
    .string "Vikram" 

生成に構文は少し異なりますが、現在は.dataセクションにどのように読み書きされているかを見てください。 ところで、この例が動作します。

+1

あなたが気付いた場合、OPはsegfaultがなぜ発生するのかを尋ねるのではなく、文字列が最初に印刷されなかった理由を尋ねています。 –

+1

これは質問に正確には答えられないかもしれませんが、.rodataと.dataについてのヒントと説明が参考になります。 – vts

0

デフォルトでは、stdoutが端末に接続されている場合、ストリームはラインバッファされます。実際には、あなたの例では、'\n'(または明示的なストリームフラッシュ)がないと、文字が表示されません。

しかし、理論的には未定義の動作が制限されていない

(標準 から「挙動は[...]は、この国際規格には要件を課していない」)と、未定義の動作をする前に、たとえば、発生しても前に、セグメンテーションフォルトが発生する可能性があります最初の printfコール!

+0

だから、あなたはその行動があまりにも未定義であると言っているので、時間の中で後方に行動することはできますか? –

関連する問題