提问人:marinsucks 提问时间:10/19/2023 最后编辑:chqrliemarinsucks 更新时间:10/22/2023 访问量:132
memcpy.c 应该如何对内存重叠做出反应?
How is memcpy.c supposed to react to memory overlap?
问:
我正在尝试重现 的行为。但是,当我尝试使用重叠内存测试时,我收到的不是跟踪陷阱,而是不同的结果。例如,使用以下函数,结果为 。memcpy
main
OveOveOveOveO�
#include <stdio.h>
#include <string.h>
int main() {
char buffer[] = "Overlap Test";
ft_memcpy(buffer + 3, buffer, strlen(buffer) + 1);
printf("%s\n", buffer + 3);
return 0;
}
我确实将关键字放在函数声明中,我不明白为什么编译器在这种情况下不将其作为跟踪陷阱。当然,chatgpt对此也找不到解释是没有用的。在内存重叠的情况下,会出现这样的结果吗?restrict
void *ft_memcpy(void *restrict dst, const void *restrict src, size_t n)
{
char *ptr;
char *d;
char *s;
d = dst;
s = (char *)src;
ptr = dst;
if (src == NULL || n == 0)
return (NULL);
while (n > 0)
{
*d = *s;
d++;
s++;
n--;
}
return (ptr);
}
答:
我正在尝试重现 memcpy 的行为。但是,当我尝试时 通过重叠的内存测试,我收到的不是跟踪陷阱 不同的结果。
在这种情况下(根据 C 标准),结果为 undefined。memcpy
在内存重叠的情况下,会出现这样的结果吗?
不,因为它是 UNDEFINED,你不应该期待任何事情。
我确实在函数声明中放置了限制关键字,并且我 不明白为什么编译器不将其作为跟踪陷阱放入 这种情况。
因为不是这样工作的。它不添加任何检查。你承诺你的函数将以某种方式运行,允许编译器进行更积极的优化。restrict
胡说八道的UB审议:
此外,许多 x86 glibc 实现不使用单字节复制,因此您的(朴素)函数将无法重现它们的行为。memcpy
评论
memmov
memcpy
我正在尝试重现 memcpy 的行为。
Bug:错误的返回值
当 时,返回 。OP 的代码也应该执行相同的操作。n == 0
memcpy()
dest
// if (src == NULL || n == 0)
// return (NULL);
if (src == NULL) // memcpy(..., NULL, ...) is not defined. Do whatever you want.
return NULL; // or maybe `n = 0;`
if (n == 0)
return dest;
Bug:空间不足
对于,是一个问题,因为没有足够的空间容纳字符。@chqrliechar buffer[] = "Overlap Test";
ft_memcpy(buffer + 3, buffer, strlen(buffer) + 1);
buffer + 3
strlen(buffer) + 1
memcpy.c 应该如何对内存重叠做出反应?
在内存重叠的情况下,会出现这样的结果吗?
给定函数签名中的 2,使用重叠缓冲区是未定义行为 (UB)。没有预期的结果。restrict
void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
即使 OP 的功能与今天的 OP 编译器一样,也不确定它明天或其他机器会运行。它是UB。ft_memcpy()
memcpy()
我建议不要使用重叠缓冲区测试等效功能 - 因为它是 UB。
请注意,当 .n == 0
相反,请考虑“memcpy.c 应该如何对内存重叠做出反应?memmove()
这更棘手。
复制的发生方式是,首先将 所指向的对象中的字符复制到与 和所指向的对象不重叠的临时字符数组中,然后将临时数组中的字符复制到 所指向的对象中。
C23dr § 7.26.2.3 2n
s2
n
s1
s2
n
s1
OP 的代码需要更改才能满足这一点。要做得好是一个挑战,因为通常的方法是比较地址。根据哪个更大,从源缓冲区的开头或结尾复制。然而,对于用户代码,没有定义的方式来比较任意地址的顺序。各种通常有效的技巧比比皆是。
下面的没有 UB,但可能无法像在所有机器上一样编译或运行。memmove()
#include <stdlib.h>
#include <stdint.h>
void *ft_memmove(void *dst, const void *src, size_t n) {
// Not needed as passing `NULL` for either pointer parameter is UB in memmove().
if (dst == NULL || src == NULL) {
n = 0;
}
// Weakness: uintptr_t is an optional type.
uintptr_t d = (uintptr_t) dst;
uintptr_t s = (uintptr_t) src;
// Weakness: `d < s` is not a defined order compare of the pointers.
if (d < s) {
for (size_t i = 0; i < n; i++) {
((char*)dst)[i] = ((const char*)src)[i];
}
} else {
while (n > 0) {
n--;
((char*)dst)[n] = ((const char*)src)[n];
}
}
return dst;
}
评论
while (n --> 0)
评论
memcpy()
对于重叠区域未定义,因此可以针对非重叠情况进行优化。 需要处理重叠区域。memmove()
cast
const char *s = src;