0

実際、memcpy は、文字へのポインタを使用すると問題なく動作しますが、文字へのポインタへのポインタを使用すると動作しなくなります。

ここで memcpy が失敗する理由、またはさらに良いことに、自分でそれを理解する方法を誰かが理解するのを手伝ってくれませんか。私の c/c++ コードで発生する問題を理解するのは非常に難しいと感じています。

char *pc = "abcd";
char **ppc = &pc;
char **ppc2 = &pc;
setStaticAndDynamicPointers(ppc, ppc2);

char c;
c = (*ppc)[1];  
assert(c == 'b');                     // assertion doesn't fail.

memcpy(&c,&(*ppc[1]),1);

if(c!='b')
  puts("memcpy didn't work.");  // this gets printed out.

c = (*ppc2)[3];
assert(c=='d');                      // assertion doesn't fail.
memcpy(&c, &(*ppc2[3]), 1);

if(c != 'd')
  puts("memcpy didn't work again.");

memcpy(&c, pc, 1);
assert(c == 'a');   // assertion doesn't fail, even though used memcpy

void setStaticAndDynamicPointers(char **charIn, char **charIn2)
{
  // sets the first arg to a pointer to static memory.
  // sets the second arg to a pointer to dynamic memory.
  char stat[5];
  memcpy(stat, "abcd", 5);
  *charIn = stat;

  char *dyn = new char[5];
  memcpy(dyn, "abcd", 5);
  *charIn2 = dyn;
}
4

7 に答える 7

4

あなたのコメントchar stat[5]はそれが静的であるべきであることを意味しますが、そうではありません。結果としてcharIn、スタックに割り当てられたブロックを指し、関数から戻るとスコープ外になります。ということstatic char stat[5]ですか?

于 2010-11-01T20:38:35.263 に答える
0

割り当てステートメントの式の括弧は、memcpy 式の括弧とは異なる場所にあることに注意してください。したがって、彼らが異なることをしていても、それほど驚くべきことではありません。

于 2010-11-01T21:05:15.330 に答える
0

ポインターを扱うときは、次の 2 点をしっかりと頭に入れておく必要があります。

#1ポインタ自体は、それが指すデータとは別のものです。ポインタは単なる数字です。この数字は、メモリ内で他のデータのチャンクの始まりを見つけることができる場所を示しています。ポインターを使用して、それが指すデータにアクセスできますが、ポインター自体の値を操作することもできます。ポインター自体の値を増加 (または減少) させると、ポインターの「宛先」が最初に指していた場所から前方 (または後方) に移動します。これにより、2 番目のポイントが表示されます...

注※2ポインタ変数には、どのようなデータを指すかを示すがあります。Aは;を指します。aは;を指します。等々。ポインターは、別のポインターを指すこともできます ( )。コンパイラが算術演算をポインタ値に適用するとき、指しているデータ型のサイズが自動的に考慮されるため、型は重要です。これにより、単純なポインター演算を使用して配列を処理できます。char *charint *intchar **

int *ip = {1,2,3,4};
assert( *ip == 1 );    // true

ip += 2;   // adds 4-bytes to the original value of ip
           // (2*sizeof(int)) => (2*2) => (4 bytes)

assert(*ip == 3);      // true

これが機能するのは、配列が同一の要素 (この場合はints) の単なるリストであり、単一の連続したメモリ ブロックに順番に配置されているためです。ポインターは、配列の最初の要素を指し始めます。次に、ポインター演算により、ポインターを配列内で要素ごとに進めることができます。これは、任意のタイプのポインターで機能します (算術は では許可されていませんvoid *)。

実際、これはまさにコンパイラが配列インデクサー operator の使用を変換する方法[]です。これは文字通り、逆参照演算子を使用したポインター追加の省略形です。

assert( ip[2] == *(ip+2) );  // true

では、これらすべてがあなたの質問にどのように関連していますか?

これがあなたのセットアップです...

char *pc = "abcd";
char **ppc = &pc;
char **ppc2 = &pc;

今のところ、への呼び出しを削除して単純化しましたsetStaticAndDynamicPointers。(その関数にも問題があります。関数の詳細については、@Nim の回答とそこにある私のコメントを参照してください)。

char c;
c = (*ppc)[1];  
assert(c == 'b');     // assertion doesn't fail.

これは機能します。「ポイントを(*ppc)教えてください」と言うからです。ppcこれは、 に相当しppc[0]ます。それはすべて完全に有効です。

memcpy(&c,&(*ppc[1]),1);

if(c!='b')
    puts("memcpy didn't work.");  // this gets printed out.

他の人が指摘したように、問題のある部分は です&(*ppc[1])。これは文字通り「ppc[1] が指すものへのポインタをください」という意味です。

まず、単純化しましょう... 演算子の優先順位によると、:&(*ppc[1])は と同じ&*ppc[1]です。次に&*は逆​​であり、互いに打ち消し合います。に簡略&(*ppc[1])化しppc[1]ます。

さて、上記の議論を考えると、これが機能しない理由を理解する準備が整いました。要するに、ppc実際には単一のポインターのみを指しているのに、ポインターの配列を指しているかのように扱っています。

コンパイラは に遭遇するとppc[1]、上記のポインタ演算を適用し、変数の直後にあるメモリへのポインタを作成しpcます。そのメモリに含まれるものは何でも。(ここでの動作は常に未定義です)。

したがって、問題はまったくありませんmemcopy()。への呼び出しmemcpy(&c,&(*ppc[1]),1)は、偽のポインターが指すメモリから 1 バイトを (要求に応じて) 忠実にコピーし、ppc[1]それを文字変数に書き込みますc

他の人が指摘したように、括弧を移動することでこれを修正できます。

memcpy(&c,&((*ppc)[1]),1)

説明がお役に立てば幸いです。幸運を!

于 2010-11-02T05:33:20.350 に答える
0

char stat[5];

はスコープ外のスタック変数ですが、そうではありません // sets the first arg to a pointer to static memory.。abcd を入れるメモリを malloc/new する必要があります。あなたがするようにcharIn2

于 2010-11-01T20:37:03.820 に答える
0

Preet が言ったように、問題は memcpy にあるとは思いません。関数「setStaticAndDynamicPointers」では、その関数呼び出しのスタックに作成された自動変数へのポインターを設定しています。関数が終了するまでに、「stat」変数が指すメモリは存在しなくなります。その結果、最初の引数 **charIn は存在しないものを指します。スタック フレーム (またはアクティベーション レコード) の詳細については、リンク テキストを参照してください。

そのコードで、スタック変数へのダングリング ポインターを効果的に作成しました。スタック変数への値のコピーをテストする場合は、呼び出された関数内ではなく、呼び出し元関数で作成されていることを確認してください。

于 2010-11-01T20:55:13.263 に答える
0

「stat」の定義に加えて、私の目の主な問題は、それ*ppc[3]が と同じではないということです(*ppc)[3]。必要なのは後者 (ppc が指す文字列の 4 番目の文字) ですが、memcpy() では前者、「文字列配列」 ppc の 4 番目の文字列の最初の文字を使用します (明らかに ppc は違います)。の配列ですchar*が、コンパイラにそのように扱わせます)。

このような問題をデバッグするときは、通常、関連するメモリ アドレスと内容を出力すると便利です。

于 2010-11-01T20:57:54.487 に答える
0

以前の回答は有効な点を提起しましたが、他に確認する必要があるのは、次の場合の演算子の優先順位規則だと思いますmemcpy

memcpy(&c, &(*ppc2[3]), 1);

そこで何が起こるの?それはあなたが意図しているものではないかもしれません。配列表記は逆参照演算子よりも優先されるため、最初に と同等のポインター演算を実行しようとしppc2++ます。次に、その値を逆参照して、アドレスを に渡しますmemcpyこれは と同じではありません (*ppc2)[1]。私のマシンでの結果はアクセス違反エラー (XP/VS2005) ですが、一般的にこれは未定義の動作です。ただし、以前と同じ方法で逆参照すると、次のようになります。

memcpy(&c, &((*ppc2)[3]), 1);

その後、そのアクセス違反は解消され、適切な結果が得られます。

于 2010-11-01T20:58:36.697 に答える