8

代码:

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 *指向a 的指针union


一点背景:这是从一个非常复杂的函数中提取的,func()包含void *参数的签名不在我的控制范围内。当然,该示例没有任何用处,那是因为我删除了所有与提供最少代码示例无关的代码。

4

5 回答 5

6

在调用func(&c)中,大小c小于sizeof(union foo),这可能很危险,对吧?

对,这将导致未定义的行为。dest可能会包含来自周围内存区域的一些字节c,这些字节取决于编译器的内部工作。当然,只要您只访问dest.c,在大多数情况下应该不会造成任何问题。

但让我更具体一点。根据 C 标准,写dest.c但读总是dest.i会产生未定义的行为。但是大多数平台上的大多数编译器也会对这些情况有一些明确定义的行为。尽管标准说了什么,但经常写作但阅读是有意义的。但是,在这种情况下,读取 from仍然会受到未知周围变量的影响,因此不仅从标准的角度来看,而且在非常实用的意义上也是未定义的。dest.cdest.idest.i

您还应该考虑一种罕见的情况:c可能位于分配的内存页面的最后。(这指的是从操作系统分配的内存页,最终是内存管理单元 (MMU) 硬件,而不是由malloc和朋友完成的按块分配的用户空间。)在这种情况下,读取超过单个字节可能会导致访问到未映射的内存,从而导致严重错误,很可能是程序崩溃。鉴于您c在 main 中作为自动变量的位置,这似乎不太可能,但我认为此代码片段只是一个示例。

线memcpy是否正确?如果没有,如何解决?

取决于你想做什么。就目前而言,代码没有太多意义,所以我不知道您可能想到的正确合理的应用程序。也许您应该将sizeof对象src传递给func.

于 2013-09-02T08:02:02.623 回答
3

memcpy 的行是否正确?如果没有,如何解决?

你应该传递void 指针指向的内存大小src,这样你就可以知道有这么多大小,所以你只需要复制这么多数据......

此外,为了安全起见 ,您应该计算目标的大小,并在此基础上传递大小,这样 可以避免读取和写入的非法访问。

于 2013-09-02T08:05:55.687 回答
1

func本身没问题。

问题在于调用者是否真的确保调用时引用的内存func()至少为sizeof(union foo).

如果后者总是如此,那么一切都很好。func()在 OP 的示例中,这两个调用并非如此。

如果调用时引用的内存func()较少,sizeof(union foo)则会memcpy()引发未定义的行为。

于 2013-09-02T09:06:26.293 回答
1

memcpy很好。通过传递联合中最小成员的地址,您将以较大成员中的垃圾结束。避免垃圾位的一种方法是默认情况下进行所有调用func- 我假设您可以控制 - 仅使用指向较大成员的指针 - 这可以通过将较大成员设置为较小成员来实现:i = c然后调用func(&i).

于 2013-09-02T08:11:24.703 回答
0

既然你知道要复制什么和什么大小,为什么不给出一个更明确的函数,让函数知道如何复制 void 指针指向的正确大小的内存。

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(...){

    }
}
于 2013-09-02T08:33:59.880 回答