8

这是一个学习练习。我试图通过在复制操作开始之前通知用户复制操作将通过还是失败来增加 memcpy。我最大的问题是以下。如果我分配两个每个 100 字节的 char 数组,并且有两个指针引用每个数组,我怎么知道我在复制哪个方向?如果我将所有内容从第一个数组复制到第二个数组,如何确保用户不会覆盖原始数组?

我当前的解决方案比较了指针与目标数组大小的距离。如果两者之间的大小小于我所说的将发生覆盖。但是,如果它向另一个方向复制呢?我只是有点困惑。

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

确定两个内存范围是否重叠的唯一可移植方法是:

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

在下面的非常准确的评论之后,我应该补充一点,您可以使用指针(目标 - 源)的差异,但为了安全起见,事先将这些指针转换为 char *。

在您当前的设置中,我认为您无法检查操作是否会失败。您的 memcpy 原型会阻止您进行任何形式的检查,并且使用上面给出的决定如何复制的规则,操作将成功(除了任何其他考虑因素之外,例如先前的内存损坏或无效指针)。

于 2012-10-26T23:00:00.313 回答
0

我不相信“试图通过在复制操作开始之前通知用户复制操作将通过还是失败来增加 memcpy”。是一个结构良好的概念。

首先, memcpy() 在正常意义上不会成功或失败。它只是复制数据,如果它在源数组之外读取或在目标数组之外写入,可能会导致错误/异常,并且它也可能在这些数组之一之外读取或写入,而不会导致任何错误/异常并且只是默默地破坏数据. 当我说“memcpy 执行此操作”时,我并不是在谈论 C stdlib memcpy 的实现,而是在谈论具有相同签名的任何函数——它没有足够的信息来做其他事情。

其次,如果您对“成功”的定义是“假设缓冲区足够大但可能重叠,则将数据从源复制到 dst,而不会在复制时绊倒自己”——这确实是 memmove() 所做的,而且总是可能的。同样,没有“退货失败”的情况。如果缓冲区不重叠很容易,如果源与目标的末尾重叠,那么您只需从头开始逐字节复制;如果源与目标的开头重叠,那么您只需从末尾逐字节复制。这就是 memmove() 的作用。

第三,在编写此类代码时,您必须非常小心指针运算(包括加法、减法和数组索引)的溢出情况。In val = abs(ptr1 - ptr2),ptr1 - ptr2可能是一个非常大的数字,它是无符号的,所以abs()不会对它做任何事情,并且int是错误的类型来存储它。只是让你知道。

于 2012-10-27T00:58:47.910 回答