0

私は1週間前にC++で冒険を始めました。私はC++についてたくさん読みました。私は次のことを実験していました:

 char * String1 = "abcdefgh";

次に、次の方法でその値を変更しようとしました。

 String1[2] = 'f';

これにより、手に負えない例外が発生しました。ただし、次の結果は適切に実行されます。

 char String2[9]="abcdefgh";

 String2[7]='s';

DUMPBINを使用して、上記のコードを使用して生成されたバイナリに関する情報を抽出しようとしました。DUMPBINはVisualStudioツールです。/ ALLオプションを使用して、バイナリに含まれるすべての情報を抽出しました。

RAWDATAセクションに「abcdefgh」の2つのインスタンスが表示されました。そして、私はその理由を理解しています。

私の質問は次のとおりです。

1)String1とString2はどちらも基本的に、同じ文字シーケンスの2つの異なるインスタンスへのポインターですが、String1の操作が正当な操作ではないのはなぜですか?

2)コンパイラが変数名とその値をマッピングするためのSYMBOLTABLEを生成することを知っています。Windows OSでシンボルテーブルを視覚化するツールはありますか?

3)文字シーケンスの代わりに整数の配列がある場合、それはRAWDATAで見つけることができますか?

RAWDATAでも次のことがわかります。

Unknown Runtime Check Error.........
 Stack memory around _alloca was corrupted.......
  ....A local variable was used before it was initialized.........
 ....Stack memory was corrupted..
  ........A cast to a smaller data type has caused a loss of data.
  If this was intentional, you should mask the source of the cast with the appropriate bitmask.

これらのものはどのようにしてバイナリ実行可能ファイルに入りますか?これらのメッセージをバイナリ(明らかに読み取り可能ではありません)に含める目的は何ですか?

編集:私の質問1)には、次のことを意味するために使用されるINSTANCESという単語があります。

文字シーケンス「abcdefgh」は、大文字でない英語のアルファベットのセット、つまり{a、b、...、y、z}から派生しています。このシーケンスは2回インスタンス化され、AとBなどの2つのメモリ位置に格納されます。String1はA(仮定)を指し、String2はBを指します。この質問には概念的な取り違えはありません。

私が理解したかったのは、メモリ位置AとBの属性の違い、つまり、それらの1つが不変である理由でした。

4

5 に答える 5

5

注: 以下のコードはすべて、関数内のスコープを参照しています。

以下のコードは、書き込み可能なバッファをデータで初期化します。string2コンパイラは、コンパイラが生成した読み取り専用の文字列からこのバッファにコピーするための初期化コードを生成します。

char string2[] = "abcdefgh";

以下のコードは、コンパイラが生成した読み取り専用の文字列へのポインターを に格納しますstring1。文字列の内容は、実行可能イメージの読み取り専用セクションにあります。そのため、変更は失敗します。

char * string1 = "abcdefgh";

string1書き込み可能なバッファを指すことで機能させることができます。これは、文字列をコピーすることで実現できます。

char * string1 = strdup("abcdefgh");
....
free(string1); // don't forget to free the buffer!
于 2012-06-08T12:52:27.773 に答える
4

char * String1 = "abcdefgh";
C(およびC ++)ではconstであり、コンパイラは固定のconstデータを好きなように格納できます。別のDATAセグメントを持つ場合があり、完全にconstプログラムストアを持つ場合があります(ハーバードアーキテクチャ)

char String2[9]="abcdefgh"; 文字の 9 つの要素の配列を割り当て、たまたま文字列で初期化します。配列でやりたいことができます。他のタイプの配列も同じ方法で格納されます。

一部の実行時エラーのエラー メッセージは、(元の char* 文字列と同じ方法で) プログラム データ セグメントに格納されます。「このプログラムにはウィンドウが必要です」などの一部は、OS ではなく、明らかにそこにある必要があります。しかし、これらの特定のランタイム エラーが OS によって作成されない理由がわかりません

于 2012-06-08T12:52:28.223 に答える
2

文字列リテラルは変更できません。文字列リテラルのタイプは char const[]であり、それを変更しようとする試みは未定義の動作です。そして、次のようなステートメントが与えられます:

char* s1 = "a litteral";

、コンパイラは実際に警告を生成する必要があります。ここでのnon-constへの暗黙の変換は非推奨であり、既存のコードを壊さないようにするためにのみ言語に導入されました(Cがなかった時代からの日付const)。

その場合:

char s2[] = "init";

、実際には文字列リテラルはありません。「文字列リテラル」は実際には初期化仕様であり、文字列リテラルとは異なり、メモリ内のどこにも表示されません。これは、初期化の方法を決定するためにコンパイラーによって使用され、s2 次のものとまったく同じです。

char s2[] = { 'i', 'n', 'i', 't', '\0' };

(書く方が少し便利です。)

-短い歴史的なサイドライト:初期のCにはありませんでしたconst。文字列リテラルのタイプはでありchar[]、それを変更すること合法でした。これはいくつかの非常に恐ろしいコードにつながります:

char* f() { return "abcd"; }

/* ... */
f()[1] = 'x';

次に電話をかけたときにf、が返され"axcd"ました。ソースリストに表示される値を持たない文字は、コードを読み取る方法ではあり ません。C標準委員会は、これは保持しない方がよい機能の1つであると判断しました。

于 2012-06-08T13:28:50.640 に答える
1
char string[] = "foo"

これは char 配列を割り当て、値 {'f', 'o', 'o', '\0'} で初期化します。文字用の「独自の」ストレージを取得し、配列を変更できます。

char strptr* = "foo"

これはポインタを割り当て、そのポインタの値を {'f', 'o', 'o', '\0'} を含む char 配列のアドレスに設定します。ポインターはあなたが望むように処理できますが、char 配列はそうではありません。実際、配列の型はchar[]ではなくconst char[]であり、誤って const 配列を変更しようとしないように、strptr実際には として宣言する必要があります。const char*

最初のケースで"foo"は、配列初期化子です。2 番目"foo"は、文字列リテラルです。

各状況のメモリが配置されている正確な場所に関するより具体的な詳細は、標準では指定されていない傾向があります。ただし、一般的に言えば、スタックに配列を割り当て、スタックにchar string[] = "foo"ポインターを割り当て、(静的に)実行可能ファイルのデータ セクションに配列を割り当てます。charchar strptr* = "foo"charconst char

于 2012-06-08T13:06:54.780 に答える
1

1) C++ 標準 (2003) ( http://www.iso.org/iso/catalogue_detail.htm?csnumber=38110 )で指摘されているように


1 文字列リテラルは、 "..." または L"..." のように、必要に応じて文字 L で始まる、二重引用符で囲まれた一連の文字です。L で始まらない文字列リテラルは通常の文字列リテラルであり、ナロー文字列リテラルとも呼ばれます。通常の文字列リテラルは、「n const
char の配列」型と静的ストレージ期間 ( basic.stc ) を持ちます。ここで、n は以下で定義される文字列のサイズであり、指定された文字で初期化されます。L"asdf" などの L で始まる文字列リテラルは、ワイド文字列リテラルです。ワイド文字列リテラルは、「n の配列 const wchar_t」型を持ち、静的な保存期間を持ちます。ここで、n は以下で定義される文字列のサイズです。

2 すべての文字列リテラルが異なる (つまり、重複しないオブジェクトに格納されている) かどうかは、実装によって定義されます。文字列リテラルを変更しようとした場合の影響は未定義です。

上記のように、それは違法ではなく、未定義の動作であるため、VS では Windows で例外が発生し、g++ では Linux でセグメンテーション違反が発生します (基本的には似ていますが)。

2) 逆アセンブリ プログラムを使用して、exe ファイルのデータ セクションを確認できます (いくつかの exe ファイル構造x86 Disassembly/Windows Executable Filesの詳細については、この wiki を確認してください) 。

3) はい、exe ファイルの .data セクションにあるはずです。

于 2012-06-08T13:10:14.607 に答える