代码:
union foo
{
char c;
int i;
};
void func(void * src)
{
union foo dest;
memcpy(&dest, src, sizeof(union foo)); //here
}
如果我这样打func()
:
int main()
{
char c;
int i;
func(&c);
func(&i);
return 0;
}
在通话func(&c)
中,c
的大小小于sizeof(union foo)
,这可能很危险,对吗?
memcpy
的行是否正确?如果没有,如何修复它?
我希望安全地致电memcpy
,将void *
指针复制到union
。
一点背景:这是从一个非常复杂的函数中提取的,包含func()
参数的void *
的签名是我无法控制的。当然这个例子没有任何用处,因为我删除了所有与提供最少代码示例无关的代码。
答案 0 :(得分:6)
在通话
func(&c)
中,c
的大小小于sizeof(union foo)
,这可能很危险,对吗?
是的,这将导致未定义的行为。 dest
可能包含c
周围内存区域的一些字节,这些字节取决于编译器的内部工作方式。当然,只要您只访问dest.c
,在大多数情况下这不会导致任何问题。
但是让我更具体一点。根据C标准,编写dest.c
但是阅读dest.i
将总是产生未定义的行为。但是大多数平台上的大多数编译器也会对这些情况有一些明确定义的行为。所以经常写dest.c
,但阅读dest.i
是有道理的,尽管标准说的是什么。然而,在这种情况下,从dest.i
读取仍然会受到未知周围变量的影响,因此不仅从标准的角度来看它是不确定的,而且在非常实际的意义上也是如此。
您应该考虑一种罕见的情况:c
可能位于已分配内存页的最后。 (这是指从操作系统和最终内存管理单元(MMU)硬件分配的内存页,而不是由malloc
和朋友完成的逐块用户空间分配。)在这种情况下,阅读更多单字节可能会导致访问未映射的内存,从而导致严重错误,很可能是程序崩溃。鉴于您的c
作为自动变量在main中的位置,这似乎不太可能,但我认为此代码段只是一个示例。
memcpy
的行是否正确?如果没有,如何修复它?
取决于你想做什么。就目前而言,代码没有多大意义,所以我不知道你可能会想到什么样的正确合理的应用程序。也许您应该将sizeof
src
对象传递给func
。
答案 1 :(得分:3)
memcpy的行是否正确?如果没有,如何修复它?
你应该传递void指针所指向的 size 内存,这样你就可以知道src
有这么多的大小,所以你只需要复制这么多的数据......
为了安全起见,您应该计算目的地的大小,并根据您应该传递的尺寸,以便在读取和写入时可以避免非法访问。< / p>
答案 2 :(得分:1)
memcpy
没问题。通过传递联盟中最小成员的地址,您将在较大的成员中以垃圾结束。避免垃圾位的一种方法是默认情况下对func
的所有调用 - 我认为你控制它 - 只使用指向较大成员的指针 - 这可以通过将较大的成员设置为较小的成员来实现:i = c
,然后致电func(&i)
。
答案 3 :(得分:1)
func
本身就可以。
问题在于调用者是否确实确保调用func()
时引用的内存至少为sizeof(union foo)
。
如果后者总是如此,一切都很好。在OP的例子中,对func()
的两次调用不是这样的。
如果调用func()
时引用的内存少于sizeof(union foo)
,则memcpy()
会引发未定义的行为。
答案 4 :(得分:0)
由于您知道要复制的内容和大小,为什么不给出更明确的函数,让函数知道如何复制无效指针指向的正确大小的内存。
union foo
{
char c;
int i;
};
void func(void * src, const char * type)
{
union foo dest;
if(strcmp(type, "char") == 0){
memcpy(&dest, src, 1);
}else if(...){
}
}