这是一项学习练习。我试图通过通知用户复制操作在开始之前是通过还是失败来增加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;
}
答案 0 :(得分:9)
确定两个内存范围是否重叠的唯一便携式方法是:
int overlap_p(void *a, void *b, size_t n)
{
char *x = a, *y = b;
for (i=0; i<n; i++) if (a+i==b || b+i==a) return 1;
return 0;
}
这是因为指针与关系运算符的比较是未定义的,除非它们指向同一个数组。实际上,这种比较确实适用于大多数现实世界的实现,因此您可以执行以下操作:
int overlap_p(void *a, void *b, size_t n)
{
char *x = a, *y = b;
return (a<=b && a+n>b) || (b<=a && b+n>a);
}
我希望我的逻辑正确;你应该检查一下。如果你想假设你可以采用任意指针的差异,你可以进一步简化它。
答案 1 :(得分:2)
您要检查的是源相对于目的地的内存位置:
如果源位于目的地之前(即源&lt; destination),那么您应该从最后开始。如果源是之后,则从头开始。如果他们是平等的,你不必做任何事情(无关紧要)。
以下是一些粗略的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原型会阻止您对此进行任何形式的检查,并且根据上面给出的规则来决定如何复制,操作将成功(除了任何其他考虑因素之外,如先前的内存损坏或无效指针)。
答案 2 :(得分:0)
我不相信“通过通知用户复制操作在开始之前是通过还是失败来尝试增加memcpy”。是一个很好的概念。
首先,memcpy()在正常意义上不成功或失败。它只是复制数据,如果它在源数组外部读取或在目标数组外部写入,则可能导致错误/异常,并且它也可能在其中一个数组之外读取或写入而不会导致任何错误/异常并且只是静默地破坏数据。当我说“memcpy做到这一点”时,我不只是谈论C stdlib memcpy的实现,而是关于任何具有相同签名的函数 - 它没有足够的信息来做其他事情。
其次,如果您对“成功”的定义是“假设缓冲区足够大但可能重叠,则将数据从源复制到dst而不会在复制时绊倒自己” - 这确实是memmove()的作用,它总是可能的。同样,没有“返回失败”的情况。如果缓冲区没有重叠,那么很容易,如果源与目标的末尾重叠,那么你只需从头开始逐字节复制;如果源与目标的开头重叠,那么您只需从末尾逐字节复制。这是memmove()的作用。
第三,在编写这种代码时,必须非常小心指针算法的溢出情况(包括加法,减法和数组索引)。在val = abs(ptr1 - ptr2)
中,ptr1 - ptr2
可能是一个非常大的数字,并且它将是无符号的,因此abs()
将不对其执行任何操作,并且int
是错误的类型将它存入。只是你知道。