【问题标题】:gcc generates memcpy with source == dest?gcc 使用 source == dest 生成 memcpy?
【发布时间】:2020-11-18 17:28:07
【问题描述】:

当调用memcpy() 时,我尝试确保源/目标指针不重叠,因为规范说这是未定义的行为。但是,对于以下简单示例,GCC 会生成一个 memcpy(),其中 source == dest。

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

struct my_struct {
  int a[5000] ;
} ;

int main(void)
{
  struct my_struct a ;
  struct my_struct *p_a = &a ;
  memset( &a, 0, sizeof(a) ) ;

  *p_a = a ;
  printf("%d\n",p_a->a[10] );

  return 0 ;
}

使用 gcc -O0,我的本地 GCC (5.4.0) 和 godbolt.org GCC x86-64 10.2 为 *p_a = a 生成以下行:

call memcpy

(我没有使用-O2,因为这需要更复杂的示例来演示问题)

我问是因为我们有一个嵌入式系统,当memcpy() 源/目标重叠时会生成警告。但是,由于上面的示例,有些人希望在 source==dest 时关闭警告。什么是合理的?

【问题讨论】:

  • 您认为p_a = &amp;a; *p_a = a; 也不是未定义的行为,但我不太确定。 [已定义;见下文] /// 但让我们假设它已定义。你必须明白,未定义的行为仅仅意味着规范没有提供关于编译器在特定情况下应该做什么的指导。编译器知道它自己的memcpy 实现,并且可以在它知道可以使用它的情况下使用它,即使规范没有。
  • 当源和目标重叠时 C 标准没有定义 memcpy 的行为这一事实并不意味着 C 编译器不能要求、知道和依赖 memcpy 的事实。当源和目标重叠时,它会根据需要使用。当然,在源和目的相同的情况下调用memcpy效率很低,但那是另外一回事。
  • @ikegami:没有什么未定义的。 = 的操作数的评估在存储结果的副作用之前排序(C 2018 6.5.16 3)。 (否则,你不能这样做x = x+1;。)C 标准明确讨论了这种情况:“如果存储在一个对象中的值是从另一个对象读取的,该对象以任何方式与第一个对象的存储重叠,那么重叠应该是准确的,并且两个对象应该具有兼容类型的合格或不合格版本;否则,行为未定义”(C 2018 6.5.16.1 3)。
  • @Eric Postpischil,谢谢。我总是假设做奇怪的事情是不确定的,除非我知道:)

标签: c gcc


【解决方案1】:

当源和目标重叠时,C 标准没有定义 memcpy 的行为这一事实并不意味着 C 编译器不能要求、知道和依赖它使用的特定 memcpy 行为如下的事实当源和目标重叠时需要。

在这种情况下,对memcpy 的唯一要求(超出 C 标准的要求)是它在源和目标相同的情况下工作。如果 C 编译器需要它使用的 memcpy 的该属性,那么按照 C 标准这是可以的——C 实现可能对其软件有任何内部要求。 C 标准仅管理软件的外部行为,而不管理软件内部的工作方式。

这是一个相当温和的要求,不定义源和目标重叠时的行为的原因是memcpy 的各种实现可能在写入重叠目标的过程中改变之前的部分源他们已被阅读。但是,当源和目标相同时不会发生这种情况,至少在memcpy 的正常实现中不会发生。所以这是一个很容易满足的要求。

编译器生成的memcpy 效率低下,因为它没有净效应,但这无关紧要,因为代码是在禁用优化的情况下生成的。

【讨论】:

  • 谢谢 - 如果 source == dest,听起来任何与 GCC 一起使用的 C 库(例如 musl、glibc 等)都必须工作? (旁注:我面临的实际 memcpy() 远离指针分配,因此即使使用“-O2”,编译器也不知道 source==dest。)
  • @Will:看起来是这样,尽管我不希望在 GCC 文档的任何地方都明确说明。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-06
  • 1970-01-01
  • 2016-09-07
  • 2018-07-08
  • 2013-10-09
相关资源
最近更新 更多