15

だから私は次のプログラムを持っています:

int main(){
  char* one = "computer";
  char two[] = "another";
  two[1]='b';
  one[1]='b';
  return 0;
}

「one[1]='b'」の行でセグフォールトします。これは、ポインタ「one」が指すメモリが読み取り専用メモリにある必要があるため、意味があります。ただし、問題は、「two [1]='b'」という行がセグメンテーション違反ではないのはなぜかということです。gccからのアセンブリ出力を見てください。

.file   "one.c"
        .section        .rodata
.LC0:
        .string "computer"
.LC1:
        .string "another"
        .text
.globl main
        .type   main, @function
main:

両方の文字列がrodataセクションにあるため、読み取り専用であることがわかります。では、どうして「two [1]='b'」という行がセグメンテーション違反にならないのでしょうか。

4

3 に答える 3

38

one読み取り専用ページにある文字列を直接指します。一方、twoはスタックに割り当てられた配列であり、一定のデータで初期化されます。実行時に、実行可能ファイルの読み取り専用セクションの文字列がスタックにコピーされます。変更するのは、読み取り専用メモリページではなく、スタック上のその文字列のコピーです。

より高いレベルの観点では、言語の観点から"abcd"は、タイプの表現であり、ではconst char*ありませんchar*。したがって、そのような式が指す値を変更すると、未定義の動作が発生します。このステートメントchar* one = "something";は、文字列へのポインタを変数に格納するだけです(修飾子をキャストしているため、安全ではありませんconst)。はchar two[] = "something";全然違います。のように、実際には配列を宣言して初期化しますint a[] = {1,2,3};。ここで引用符で囲まれた文字列は、初期化式です。

于 2009-11-20T20:36:51.457 に答える
1

rodataセクションに表示される「別の」は、two初期化されるときにアレイにコピーされるものです。一方、文字列「computer」のアドレスが1つに割り当てられます。

したがって、oneは読み取り専用セグメント(したがって、書き込み時のセグメンテーション違反)を指しtwo、スタックに割り当てられてから、「別の」セグメントがそのセグメントにコピーされます。

于 2009-11-20T20:40:46.493 に答える
0

2番目の形式は、リテラル文字列をコピーして配列を作成します。

これは次と同等です。

char two[] = {'a', 'n', 'o', 't', 'h'. 'e', r', '\0'};

次のような変数を使用して文字配列を初期化できます。

char c = 'a';
char two[] = {'a', 'n', c, '\0'};
于 2009-11-20T21:02:41.763 に答える