【发布时间】: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 = &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,谢谢。我总是假设做奇怪的事情是不确定的,除非我知道:)