为什么限制限定符仍然允许 memcpy 访问重叠内存?

Duy*_*ặng 1 c memcpy restrict-qualifier

我想看看是否restrict会阻止memcpy访问重叠内存。

memcpy函数将n个字节从内存区域src直接复制到内存区域dest 。内存区域不应重叠。

memmove使用缓冲区,因此没有重叠内存的风险。

restrict限定词表示,对指针的寿命,只有指针本身或直接从一个值(例如pointer + n)将具有访问该对象的数据。如果没有遵循意图声明并且对象被一个独立的指针访问,这将导致未定义的行为。

#include <stdio.h>
#include <string.h>

#define SIZE 30

int main ()
{
    char *restrict itself;
    itself = malloc(SIZE);
    strcpy(itself, "Does restrict stop undefined behavior?");
    printf("%d\n", &itself);
    memcpy(itself, itself, SIZE);
    puts(itself);
    printf("%d\n", &itself);

    memcpy(itself-14, itself, SIZE); //intentionally trying to access restricted memory
    puts(itself);
    printf("%d\n", &itself);

    return (0);
}
Run Code Online (Sandbox Code Playgroud)

输出 ()

自身地址:12345
限制是否停止未定义的行为?
自身地址:12345
停止未定义的 bop 未定义行为?
自身地址:12345

是否memcpy使用独立指针?因为输出肯定会显示未定义的行为,restrict并且不会阻止访问与memcpy.

我假设memcpy它具有性能优势,因为它在memmove使用缓冲区时直接复制数据。但是对于现代计算机,我是否应该忽略这种潜在的更好性能并始终使用memmove它,因为它保证没有重叠?

Gas*_*rdP 5

restrict关键字被提供给编译器允许的代码的生成,告诉编译它不应该与指针别名的可能性(两种不同的命名指针访问同一地址)困扰的提示。

在接受restrict指针的函数中,编译器理解写入一个不会影响另一个。当从位置 A 复制到位置 B 时,这意味着它可以安全地更改此代码:

  • 从 A[0] 读入寄存器 1
  • 从寄存器 1 写入 B[0]
  • 从 A[1] 读入寄存器 1
  • 从寄存器 1 写入 B[1]
  • 从 A[2] 读入寄存器 1
  • 从寄存器 1 写入 B[2]
  • 从 A[3] 读入寄存器 1
  • 从寄存器 1 写入 B[3]
  • ...

进入这个序列:

  • 从 A[0] 读入寄存器 1
  • 从 A[1] 读入寄存器 2
  • 从 A[2] 读入寄存器 3
  • 从 A[3] 读入寄存器 4
  • 从寄存器 1 写入 B[0]
  • 从寄存器 2 写入 B[1]
  • 从寄存器 3 写入 B[2]
  • 从寄存器 4 写入 B[3]
  • ...

只有当 A 和 B 不重叠时,这两个序列才相同,并且除非您使用restrict(或者除非它可以从上下文中猜测这样做是安全的),否则编译器不会优化到第二个序列。

如果您最终提供指向不期望它们的函数的别名指针,编译器可能能够在编译时警告您,但不能保证它会。但是您真的不应该期望在编译时出现错误 - 相反,您应该将其视为在编译器从您的代码生成程序集时授予编译器的权限。


回答有关性能的问题 - 如果您确定指针中没有重叠,请使用memcpy. 如果没有,请使用memmove. memmove通常会检查是否有重叠,memcpy如果没有,则最终使用,但您需要为检查付费。