1

更新 2:

さて、私が持っている回避策を別の関数にリファクタリングしました。このように、まだ理想的ではありませんが (特に、関数内で割り当てられたメモリを関数外で解放する必要があるため)、もう少し一般的に使用することができます。私はまだ、より最適でエレガントなソリューションを望んでいます...


更新:

さて、問題の原因は特定されましたが、まだ解決策がありません。

構造体の配列の数バイトを変更する (簡単で効果的な) 方法を見つけようとしています。同じサイズのバッファーを動的に割り当て、配列をコピーし、バッファーを変更し、配列の代わりにバッファーを使用してから、バッファーを解放するという私の現在の回避策は、過剰であり、最適ではないようです。このようにする必要がある場合は、構造体に 2 つの配列を配置し、両方を同じデータに初期化して、2 番目の配列を変更することもできます。私の目標は、メモリ フットプリント (元の配列と変更された配列の違いだけを格納する) と手作業の量 (配列に自動的にパッチを適用する) の両方を削減することです。


元の投稿:

昨夜、問題なく動作するプログラムを書きましたが、今日それをリファクタリングして拡張性を高めたところ、問題が発生しました。

元のバージョンには、ハードコードされたバイト配列がありました。いくつかの処理の後、いくつかのバイトが配列に書き込まれ、さらにいくつかの処理が行われました。

パターンのハードコーディングを避けるために、配列を構造体に入れ、関連するデータを追加してそれらの配列を作成できるようにしました。しかし、今、構造体の配列に書き込むことができません。疑似コードの例を次に示します。

main() {
  char pattern[]="\x32\x33\x12\x13\xba\xbb";
  PrintData(pattern);
  pattern[2]='\x65';
  PrintData(pattern);
}

それは機能しますが、これは機能しません:

struct ENTRY {
  char* pattern;
  int   somenum;
};

main() {
  ENTRY Entries[] = {
      {"\x32\x33\x12\x13\xba\xbb\x9a\xbc", 44}
    , {"\x12\x34\x56\x78", 555}
  };
  PrintData(Entries[0].pattern);
  Entries[0].pattern[2]='\x65';   //0xC0000005 exception!!! :(
  PrintData(Entries[0].pattern);
}

2 番目のバージョンでは、割り当てでアクセス違反の例外が発生します。2番目のバージョンではメモリの割り当てが異なるためだと思いますが、何が何であるか、またはこれを修正する方法を理解しようとして頭が痛くなり始めています。(私は現在、パターン配列と同じサイズのバッファーを動的に割り当て、パターンを新しいバッファーにコピーし、バッファーに変更を加え、パターン配列の代わりにバッファーを使用して、それから(一時的な) バッファを解放することを忘れないようにします。)

(具体的には、元のバージョンはパターン配列 +offset を DWORD* にキャストし、それに DWORD 定数を割り当てて 4 つのターゲット バイトを上書きします。ソースの長さが不明であるため、新しいバージョンではそれができません。 memcpy の代わりに memcpy を使用します.memcpy へのポインターが正しいことを確認しましたが、まだアクセス違反が発生します. Unicode char ではなくプレーン char (バイトの配列として) を使用し、null ターミネータを無視します。上記の代入を使用すると、同じ問題が発生します。)

何か案は?

4

4 に答える 4

7

文字列リテラルを変更しようとするのは違法です。君の

Entries[0].pattern[2]='\x65'; 

行はまさにそれを試みます。2 番目の例では、文字列にメモリを割り当てていません。代わりに、(構造体オブジェクト内の) ポインターが文字列リテラルを直接指すようにしています。また、文字列リテラルは変更できません。

この質問は毎日数回聞かれます。この文字列反転 C コードがセグメンテーション違反を引き起こしているのはなぜですか? 詳細については。

于 2010-04-10T00:07:38.440 に答える
3

他の回答がエラーの理由に対処しています:許可されていない文字列リテラルを変更しています。

この質問にはC++のタグが付いているため、問題を解決する簡単な方法はを使用することstd::stringです。

struct ENTRY {
  std::string pattern;
  int         somenum;
};
于 2010-07-22T20:27:37.303 に答える
3

問題は、 が式で a とよく似ていても、 achar[]が ではないという事実に要約されます。char*char[]char*

于 2010-04-10T00:08:54.363 に答える
1

更新に基づいて、実際の問題は次のとおりです。構造体の配列内の文字列を編集可能な方法で初期化する方法を知りたいです。(この問題は、構造体の配列が作成された後に起こることとは何の関係もありません。コード例で示したように、文字列が正しく初期化されていれば、文字列の編集は簡単です。)

次のコード サンプルは、これを行う方法を示しています。

// Allocate the memory for the strings, on the stack so they'll be editable, and
// initialize them:
char ptn1[] = "\x32\x33\x12\x13\xba\xbb\x9a\xbc";
char ptn2[] = "\x12\x34\x56\x78";

// Now, initialize the structs with their char* pointers pointing at the editable
// strings:
ENTRY Entries[] = {
  {ptn1, 44}
, {ptn2, 555}
};

それはうまくいくはずです。ただし、文字列のメモリはスタック上にあるため、現在のスコープを離れると消えてしまうことに注意してください。Entries も (この例のように) スタック上にある場合は問題ありません。もちろん、同時に消えてしまうからです。

これに関するいくつかの Q/A:

Q: 構造体の配列の初期化で文字列を初期化できないのはなぜですか? A: 文字列自体は構造体に含まれていないため、配列を初期化すると、配列が指すものではなく、配列自体にメモリが割り当てられるだけです。

Q: では、構造体に文字列を含めることはできますか? A:いいえ。構造体は一定のサイズである必要があり、文字列は一定のサイズではありません。

Q: これは、文字列リテラルを保持してからストレージを malloc して文字列リテラルをそこにコピーするよりもメモリを節約します。その結果、文字列の 2 つのコピーが作成されますよね? A: おそらく違います。あなたが書くとき

char pattern[] = "\x12\x34\x56\x78";

何が起こるかというと、そのリテラル値がコンパイルされたコードに埋め込まれ (基本的には文字列リテラルと同じように)、その行が実行されると、メモリがスタックに割り当てられ、コードからの値がそのメモリにコピーされます。つまり、ソース コード内の編集不可能なバージョン (初期値の取得元であるため、そこにある必要があります) と、メモリ内の他の場所にある編集可能なバージョンの 2 つのコピーが作成されます。これは、ほとんどがソース コードの単純さに関するものであり、コンパイラがコピーを行うために使用する命令を最適化するのを助けることに関するものです。

于 2010-04-10T05:48:48.233 に答える