0

我想我生锈了,所以请和我一起裸露。我会尽量简短。

Q1。当尝试复制缓冲区时,比如buf2buf1,以下检查是否足以防止混叠?

if (buf2 >= buf1 && buf2 < buf1 + buf1size) {
    // aliasing
}

Q2。如果是这样,我们是否可以像这样选择性地使用其中一个 memcopy()memmove()视情况而定?

// use memcpy() by default
void *(*funcp)(void *restrict, const void *restrict, size_t) = &memcpy;

// switch to memmove() when aliasing
if ( aliasing ) {
    // this cast FORCEFULLY changes the type-qualifiers of the declared parameters
    funcp = (void *(*)(void *, const void *, size_t)) &memmove;
}

// later on ...
if ( buf2size <= buf1size ) {
    (*funcp)( buf1, buf2, buf2size ); // funcp() works too, I prefer making it explicit
}

它可以工作,但我对切换到memmove(). 我认为该标准证实了我的怀疑(当我需要它们时永远找不到这些该死的东西......使用C99顺便说一句),但由于代码有效,我想更加确定,因为如果它可以这样它会拯救我从复制buf2开始,使用副本并在完成后释放它。

4

2 回答 2

1

我相信术语“内存区域重叠”使用得更频繁。

没有可移植的方式来进行这种指针比较。标准库实现必须比较指针,但在这种情况下,库的作者确切地知道这种比较是如何工作的。

最流行的 glibc 实现使用unsigned long longorunsigned long整数来比较指针(或者更确切地说执行地址算法)。

Q2。如果是这样,我们是否可以根据情况有选择地使用 memcopy() 或 memmove() ,像这样

这是没有意义的,因为 remove 会检查它本身。我知道的大多数实现不遵循移动内存区域的 C 标准方式 - 即不创建任何临时数组,仅决定复制内存区域的方向。如果内存区域不重叠,则复制操作的速度与使用memcpy.

最流行的实现(gnu C 库 glibc):

rettype
inhibit_loop_to_libcall
MEMMOVE (a1const void *a1, a2const void *a2, size_t len)
{
  unsigned long int dstp = (long int) dest;
  unsigned long int srcp = (long int) src;

  /* This test makes the forward copying code be used whenever possible.
     Reduces the working set.  */
  if (dstp - srcp >= len)   /* *Unsigned* compare!  */
    {
      /* Copy from the beginning to the end.  */

#if MEMCPY_OK_FOR_FWD_MEMMOVE
      dest = memcpy (dest, src, len);
#else
      /* If there not too few bytes to copy, use word copy.  */
      if (len >= OP_T_THRES)
    {
      /* Copy just a few bytes to make DSTP aligned.  */
      len -= (-dstp) % OPSIZ;
      BYTE_COPY_FWD (dstp, srcp, (-dstp) % OPSIZ);

      /* Copy whole pages from SRCP to DSTP by virtual address
         manipulation, as much as possible.  */

      PAGE_COPY_FWD_MAYBE (dstp, srcp, len, len);

      /* Copy from SRCP to DSTP taking advantage of the known
         alignment of DSTP.  Number of bytes remaining is put
         in the third argument, i.e. in LEN.  This number may
         vary from machine to machine.  */

      WORD_COPY_FWD (dstp, srcp, len, len);

      /* Fall out and copy the tail.  */
    }

      /* There are just a few bytes to copy.  Use byte memory operations.  */
      BYTE_COPY_FWD (dstp, srcp, len);
#endif /* MEMCPY_OK_FOR_FWD_MEMMOVE */
    }
  else
    {
      /* Copy from the end to the beginning.  */
      srcp += len;
      dstp += len;

      /* If there not too few bytes to copy, use word copy.  */
      if (len >= OP_T_THRES)
    {
      /* Copy just a few bytes to make DSTP aligned.  */
      len -= dstp % OPSIZ;
      BYTE_COPY_BWD (dstp, srcp, dstp % OPSIZ);

      /* Copy from SRCP to DSTP taking advantage of the known
         alignment of DSTP.  Number of bytes remaining is put
         in the third argument, i.e. in LEN.  This number may
         vary from machine to machine.  */

      WORD_COPY_BWD (dstp, srcp, len, len);

      /* Fall out and copy the tail.  */
    }

      /* There are just a few bytes to copy.  Use byte memory operations.  */
      BYTE_COPY_BWD (dstp, srcp, len);
    }

  RETURN (dest);
}
于 2021-05-21T13:21:48.717 回答
1

对于任何两个通用指针,您都不能真正对它们进行指针运算。这是由加法运算符 C17 6.5.6/8 规定的:

如果指针操作数和结果都指向同一个数组对象的元素,或者超过数组对象的最后一个元素,则计算不应产生溢出;否则,行为未定义。

关系运算符 (6.5.8) 存在类似的文本 - 与它们进行比较的任何两个指针必须指向同一个数组,否则行为未定义。

理论上,您可以将指针转换为整数形式uintptr_t并对其进行算术运算。如果您确定buf1点在项目数组的开头buf1size,那么理论上您可以buf2通过对uintptr_t. 但这并没有什么好处。

相反,您可以简单地将函数编写为

void func (char* restrict buf1, char* restrict buf2);

并将确保两个缓冲区不别名的责任推给调用者。

至于您选择memcpyor的函数指针memmove,显然主流编译器(gcc,clang)似乎忽略了一个版本具有restrict合格的指针。如果这是否符合行为,我不确定。

于 2021-05-21T13:27:49.747 回答