当源内存块和目的内存块重叠,并且如果您的循环从索引0
开始逐个复制元素,则对于dest < source
,它可以工作,但对于dest > source
则不行(因为您在复制之前覆盖了元素)反之亦然。
您的代码从索引0
开始复制,因此您可轻松测试哪些情况可行和哪些情况不可行。请参阅以下测试代码;它展示了如何向前移动测试字符串失败,而向后移动字符串却正常工作。此外,它还展示了从后向前复制时向前移动测试字符串是如何正常工作的:
#include <stdio.h>
#include <string.h>
void *mem_copy(void *dest, const void *src, size_t n) {
size_t i = 0;
char* newsrc = (char*)src;
char* newdest = (char*)dest;
while(i < n) {
newdest[i] = newsrc[i];
i++;
}
return newdest;
}
void *mem_copy_from_backward(void *dest, const void *src, size_t n) {
size_t i;
char* newsrc = (char*)src;
char* newdest = (char*)dest;
for (i = n; i-- > 0;) {
newdest[i] = newsrc[i];
}
return newdest;
}
int main() {
const char* testcontent = "Hello world!";
char teststr[100] = "";
printf("move teststring two places forward:\n");
strcpy(teststr, testcontent);
size_t length = strlen(teststr);
printf("teststr before mem_copy: %s\n", teststr);
mem_copy(teststr+2, teststr, length+1);
printf("teststr after mem_copy: %s\n", teststr);
printf("\nmove teststring two places backward:\n");
strcpy(teststr, testcontent);
length = strlen(teststr);
printf("teststr before mem_copy: %s\n", teststr);
mem_copy(teststr, teststr+2, length+1);
printf("teststr after mem_copy: %s\n", teststr);
printf("move teststring two places forward using copy_from_backward:\n");
strcpy(teststr, testcontent);
length = strlen(teststr);
printf("teststr before mem_copy: %s\n", teststr);
mem_copy_from_backward(teststr+2, teststr, length+1);
printf("teststr after mem_copy: %s\n", teststr);
}
输出:
move teststring two places forward:
teststr before mem_copy: Hello world!
teststr after mem_copy: HeHeHeHeHeHeHeH
move teststring two places backward:
teststr before mem_copy: Hello world!
teststr after mem_copy: llo world!
move teststring two places forward using copy_from_backward:
teststr before mem_copy: Hello world!
teststr after mem_copy: HeHello world!
因此,可以编写一个函数,根据调用者是要向前复制还是向后复制,决定从索引
0
或索引
n
开始复制。棘手的事情是找出调用者是否要向前或向后复制,因为对
src
和
dest
进行指针算术运算,例如
if (src < dest) copy_from_backward(...)
在每种情况下实际上并不被允许(参见标准,例如这个
草案):
6.5.9 相等运算符
当比较两个指针时,结果取决于所指对象在地址空间中的相对位置。如果两个指向对象或不完整类型的指针都指向同一对象,或都指向同一数组对象的最后一个元素之后,它们相等。如果所指对象是同一聚合对象的成员,则以后声明的结构成员的指针比先前声明的成员的指针更大,并且具有较大下标值的数组元素的指针比具有较小下标值的同一数组的元素的指针更大。所有指向同一联合对象的成员的指针都相等。如果表达式P指向数组对象的元素,并且表达式Q指向同一数组对象的最后一个元素,则指针表达式Q+1比P大。 在所有其他情况下,行为是未定义的。
虽然我从来没有遇到过
src < dest
不能给我想要的结果的情况,但是这种方式比较两个指针实际上是未定义行为,如果它们不属于同一数组。
因此,如果您问“如何防止它?”,我认为唯一正确的答案必须是:“这取决于调用者,因为函数
mem_copy
无法确定是否可以正确比较
src
和
dest
。”
newdest[i]='\0';
。 - BLUEPIXY[dest..dest+n-1]
中的任何地址都与范围[src..src+n-1]
中的任何地址不同。要以可移植的方式确定这一点实际上相当困难 - 正式地说,您只能比较单个数组中的地址,但通常当地址范围不重叠时,这些区域不是同一数组的一部分。 - Jonathan Lefflermemcpy
不是一个字符串函数。它不关心'\0'
。 - melpomene