在C中使用未初始化的变量作为src
memcpy
未定义的行为吗?
void foo(int *to)
{
int from;
memcpy(to, &from, sizeof(from));
}
答案 0 :(得分:14)
C委员会建议对defect report 451: instability of uninitialized automatic variables的答复是:
问题3的答案是库函数将展示 用于不确定值时的未定义行为。
如果确实如此,缺陷中的问题已经寻求对memcpy和fwrite的豁免:
[...]一个人希望能够复制未初始化的填充这一事实 使用memcpy而没有未定义行为的结构中的字节是原因 使用未初始化对象的值不是未定义的 行为。这似乎暗示了一个结构的fwrite 未初始化的填充字节不应出现未定义的行为。
这部分建议的回应似乎是针对未初始化填充的问题:
委员会还注意到结构中的填充字节是 可能是一种不同形式的"摇摆不定的"表示。
我们可以看到defect report 338: C99 seems to exclude indeterminate value from being an uninitialized register形式,这与过去的期望有所不同。除其他外,它说:
[...]我 相信将unsigned char类型排除在陷阱之外的意图 表示是允许它用于复制(通过memcpy) 任意内存,在内存可能包含陷阱的情况下 某些类型的表示。[...]
博客文章Reading indeterminate contents might as well be undefined涵盖了在C井中阅读不确定值的演变,并对我上面提到的变化有了更多的了解。
值得注意的是,这与C ++的不同之处在于reading an indeterminate value from a narrow unsigned char is not undefined behavior和defect report 240注意到这种差异:
C委员会正在处理DR338中的类似问题。根据这一分析,他们计划采用几乎与上述方法相反的方法,通过增加左值到左值转换版本的描述。如果将无符号字符分配到寄存器中并且需要在这种情况下重新评估提议的分辨率,CWG不会认为对无符号字符的访问可能仍会陷阱。另见问题129。
答案 1 :(得分:5)
这是与复制操作相关的已定义行为,除非int
在您的系统中有陷阱表示。在定义int from
时,内存已在堆栈上分配。此int
的内容是当时堆栈中该位置发生的任何内容。因此,最终结果是,正在复制到int
的{{1}}的值未定义(不确定)。
其他答案引用了C标准的引用,当未初始化变量的值被“使用”时,会发生未定义的行为。如果您不使用该值,这显然不适用。在复制/分配未初始化的变量时,C11标准中还有另一个提到的未定义行为:
6.3.2.1p2
如果左值指定一个自动存储持续时间的对象 可以用寄存器存储类声明(从来没有 它的地址),并且该对象未初始化(未声明 使用初始化程序并且之前未执行任何赋值 使用),行为未定义。
这也不会影响您的代码,因为当您致电to
from
的地址
C11标准的另一个相关部分是 6.2.6.1
某些对象表示不需要表示的值 对象类型。如果对象的存储值具有这样的值 表示,并由没有的左值表达式读取 字符类型,行为未定义。如果这样的表示是 由副作用产生,修改对象的全部或任何部分 通过一个没有字符类型的左值表达式, 行为未定义)这种表示称为陷阱 表示。
对于memcpy
软件可见奇偶校验位或非二进制补码架构中的“负零”,一些非常旧的处理器可能具有陷阱表示。例如,x86处理器没有int
的陷阱表示。
答案 2 :(得分:3)
(之前 - 没有注意到 from
的地址已通过)。不,这不会导致未定义的行为,只是from
具有不确定的价值。只要您不打算使用未初始化的变量的值,程序就会有明确定义的行为。
(因为分配空间而不是初始化变量不是 UB 。)