8

これは学習演習です。コピー操作が開始する前に成功するか失敗するかをユーザーに通知することで、memcpyを拡張しようとしています。私の最大の質問は次のとおりです。それぞれ100バイトのchar配列を2つ割り当て、各配列を参照する2つのポインターがある場合、コピーしている方向をどのように知ることができますか?最初の配列から2番目の配列にすべてをコピーする場合、ユーザーが元の配列を上書きしないようにするにはどうすればよいですか?

私の現在のソリューションは、宛先配列のサイズからのポインターの距離を比較します。間のサイズが私が言うよりも小さい場合、上書きが発生します。しかし、他の方向にコピーした場合はどうなりますか?私はちょっと混乱しています。

int memcpy2(void *target, void *source, size_t nbytes) {
    char * ptr1 = (char *)target;
    char * ptr2 = (char *)source;


    int i, val;

    val = abs(ptr1 - ptr2);
    printf("%d, %d\n", val, nbytes + 0);
    if (val > nbytes) {
        for (i = 0; i < val; i++){
            ptr1[i] = ptr2[i];
        }
        return 0;  /*success */
    }
    return -1; /* error */
}



int main(int argc, char **argv){

  char src [100] = "Copy this string to dst1";
  char dst [20];
  int p;
  p = memcpy2(dst, src, sizeof(dst));

    if (p == 0)
        printf("The element\n'%s'\nwas copied to \n'%s'\nSuccesfully\n", src, dst);
    else
        printf("There was an error!!\n\nWhile attempting to copy the elements:\n '%s'\nto\n'%s', \n Memory was overlapping", src, dst);
    return 0;


}
4

3 に答える 3

13

2つのメモリ範囲が重複しているかどうかを判断する唯一のポータブルな方法は次のとおりです。

int overlap_p(void *a, void *b, size_t n)
{
    char *x = a, *y =  b;
    for (i=0; i<n; i++) if (x+i==y || y+i==x) return 1;
    return 0;
}

これは、ポインタが同じ配列を指していない限り、ポインタと関係演算子の比較が定義されていないためです。実際には、比較はほとんどの実際の実装で機能するため、次のようなことができます。

int overlap_p(void *a, void *b, size_t n)
{
    char *x = a, *y =  b;
    return (x<=y && x+n>y) || (y<=x && y+n>x);
}

私はその論理が正しいことを願っています。あなたはそれをチェックする必要があります。任意のポインターの違いをとることができると仮定したい場合は、さらに単純化できます。

于 2012-10-27T00:41:04.487 に答える
2

チェックしたいのは、宛先に対するソースのメモリ内の位置です。

ソースが宛先よりも進んでいる場合(つまり、ソース<宛先)、最後から開始する必要があります。ソースが後の場合は、最初から始めます。それらが等しい場合、何もする必要はありません(些細なケース)。

問題を視覚化するための大まかなASCII図面を次に示します。

|_;_;_;_;_;_|          (source)
      |_;_;_;_;_;_|    (destination)
            >-----^    start from the end to shift the values to the right

      |_;_;_;_;_;_|    (source)
|_;_;_;_;_;_|          (destination)
^-----<                 start from the beginning to shift the values to the left

以下の非常に正確なコメントに続いて、ポインタの違い(destination --source)を使用できることを追加する必要がありますが、安全のために、事前にそれらのポインタをchar*にキャストしてください。

現在の設定では、操作が失敗するかどうかは確認できないと思います。memcpyプロトタイプでは、これをチェックすることはできません。コピー方法を決定するための上記のルールを使用すると、操作は成功します(以前のメモリの破損や無効なポインタなど、他の考慮事項を除く)。

于 2012-10-26T23:00:00.313 に答える
0

「コピー操作が開始する前に成功するか失敗するかをユーザーに通知することでmemcpyを拡張しようとする」とは思いません。整形式の概念です。

まず、memcpy()は、通常の意味では成功または失敗しません。データをコピーするだけで、ソースアレイの外部で読み取りたり、宛先アレイの外部で書き込みを行ったりすると、フォールト/例外が発生する可能性があります。また、フォールト/例外を発生させず、データをサイレントに破損することなく、これらのアレイの1つで読み取りまたは書き込みを行う可能性もあります。 。「memcpyがこれを行う」と言うときは、C stdlib memcpyの実装についてだけでなく、同じシグネチャを持つ関数についても話します。それ以外の場合は、十分な情報がありません。

次に、「成功」の定義が「バッファが十分に大きいが重複している可能性があると想定し、コピー中に自分自身につまずくことなくデータをソースからdstにコピーする」である場合、これは確かにmemmove()が行うことであり、常に可能。繰り返しますが、「返品失敗」のケースはありません。バッファがオーバーラップしない場合、それは簡単です。ソースがデスティネーションの終わりとオーバーラップしている場合は、最初からバイトごとにコピーするだけです。ソースが宛先の先頭と重なっている場合は、末尾からバイトごとにコピーするだけです。これがmemmove()が行うことです。

第3に、この種のコードを作成するときは、ポインター演算(加算、減算、配列のインデックス付けなど)のオーバーフローのケースに十分注意する必要があります。でval = abs(ptr1 - ptr2)ptr1 - ptr2は非常に大きな数になる可能性があり、署名されていないためabs()、何も実行せず、それintを格納するのに間違ったタイプです。ご存知のとおりです。

于 2012-10-27T00:58:47.910 に答える