从未初始化的变量memcpy是未定义的行为吗?

Tor*_*erg 21 c memcpy undefined-behavior language-lawyer

是使用未初始化的变量作为srcmemcpy用C未定义行为?

void foo(int *to)
{
  int from;
  memcpy(to, &from, sizeof(from));
}
Run Code Online (Sandbox Code Playgroud)

Sha*_*our 14

C委员会提出了对缺陷报告451的回应:未初始化的自动变量的不稳定性是:

问题3的答案是,当用于不确定的值时,库函数将表现出未定义的行为.

如果确实如此,那么缺陷中的问题已经寻求对memcpy和fwrite的豁免:

[...]一个人希望能够使用memcpy在没有未定义行为的情况下在结构中复制未初始化的填充字节这一事实是使用未初始化对象的值不是未定义的行为的原因.这似乎表明具有未初始化的填充字节的结构的fwrite不应该表现出未定义的行为.

提议响应的这一部分似乎是针对未初始化填充的问题:

该委员会还指出,结构内的填充字节可能是"摇摆"表示的一种不同形式.

我们可以看到表单缺陷报告338:C99似乎将不确定的值排除在未初始化的寄存器之外,这与过去的预期有些不同.它说:

[...]我认为排除类型unsigned char与陷阱表示的意图是允许它用于复制(通过memcpy)任意内存,在内存可能包含某些类型的陷阱表示的情况下.[.. ]

阅读不确定内容的博客文章也可能未定义,涵盖了在C井中阅读不确定值的演变,并使我对上面提到的变化有了更多的了解.

值得注意的是,这与C++的不同之处在于,从狭义的无符号字符读取不确定的值不是未定义的行为,缺陷报告240注意到这种差异:

C委员会正在处理DR338中的类似问题.根据这一分析,他们计划采用几乎与上述方法相反的方法,通过增加左值到左值转换版本的描述.如果将无符号字符分配到寄存器中并且需要在这种情况下重新评估提议的分辨率,CWG不会认为对无符号字符的访问可能仍会陷阱.另见问题129.

  • 我认为这是迄今为止最好的答案,但最终委员会从未真正回答过从未初始化的变量中读取"memcpy"是否是未定义的行为本身. (2认同)

Man*_*dis 5

这是与复制操作相关的已定义行为,除非int系统中有陷阱表示.int from定义时,内存在堆栈上分配.这个内容int就是当时在堆栈中该位置发生的任何事情.因此,最终结果int是,to未定义复制到的值(不确定).

其他答案引用了C标准的引用,当未初始化变量的值被"使用"时,会发生未定义的行为.如果您不使用该值,这显然不适用.在复制/分配未初始化的变量时,C11标准中还有另一个提到的未定义行为:

6.3.2.1p2

如果左值指定了一个自动存储持续时间的对象,该对象可以使用寄存器存储类声明(从未使用其地址),并且该对象未初始化(未使用初始化程序声明,并且在使用之前未对其进行任何赋值) ),行为未定义.

这也不会影响您的代码,因为from您致电时会收到地址memcpy

C11标准的另一个相关部分是6.2.6.1

某些对象表示不需要表示对象类型的值.如果对象的存储值具有这样的表示并且由不具有字符类型的左值表达式读取,则行为是未定义的.如果这样的表示是由副作用产生的,该副作用通过不具有字符类型的左值表达式修改对象的全部或任何部分,则行为是未定义的.这种表示称为陷阱表示.

一些很老的处理器可以有一个陷阱表示对于int非二进制补码架构将是软件可见的奇偶校验位或"负零".例如,x86处理器没有陷阱表示int.

  • 请提供该声明的来源.理想的C应该如何表现是不够的.特别是,复制相同的未初始化变量两次会导致相同的值两次吗?毕竟,如果真正的"随机数据"被复制,它可能会复制堆栈的随机位,即"memcpy"可能使用的堆栈. (2认同)