分配指针vs memcpy / memmove

时间:2013-05-23 09:47:36

标签: c pointers gcc

您好我有一个关于C指针的问题(特别是void *)

我正在使用void *指针,这些指针指向作为Vector实现的单元格的任意内存块。这些blob在堆上分配。

我的问题是为什么要分配

    void *dest = CVectorNth(cv, i);
    void *src = CVectorNth(cv, i-1);
    *(void **)dest = *(void **)src;

时不能正常工作
    memmove(dest, src, elementSize);

确实有效。

为什么我需要使用memmove?在我的脑海中,我正在将指针的值更改为src指向的地址。

我知道memmove是正确的方法,但现在我甚至不能想到为什么

    dest = src;

不起作用

6 个答案:

答案 0 :(得分:4)

因为*(void **)dest = *(void **)src; != memmove(dest, src, elementSize);首先只是分配操作,其中memmove()将内存内容从src复制到dest (深复制)

修改

假设您的destsrc是这样的?

  src           5   6  7  8    
  +-----+      +--+--+--+---+
  | 5   +----->| A|B |C | D |
  +-----+      +--+--+--+---+

  dest          18  19 20 21  
  +-----+      +--+--+--+---+
  | 18  +----->|  |  |  |   |
  +-----+      +--+--+--+---+

现在,*(void **)dest = *(void **)src;是什么?

喜欢

  src           5   6  7  8    
  +-----+      +--+--+--+---+
  | 5   +----->| A|B |C | D |
  +-----+      +--+--+--+---+

  dest          18  19 20 21  
  +-----+      +--+--+--+---+
  | 18  +----->| A|  |  |   |
  +-----+      +--+--+--+---+

因为通过分配,您将位置5的内容(因为使用*)复制到位置18。

它粗略的图表,因为*(void **)dest = *(void **)src;在我的低估中出现了错误
考虑larsmans的answer here

然而,通过memmove(dest, src, elementSize);

喜欢

  src           5   6  7  8    
  +-----+      +--+--+--+---+
  | 5   +----->| A|B |C | D |
  +-----+      +--+--+--+---+

  dest          18  19 20 21  
  +-----+      +--+--+--+---+
  | 18  +----->| A|B |C |   |
  +-----+      +--+--+--+---+

假设elementSize = 3

从src到dest指向内存区域(deepcopy)的memmove copy elementSize元素

dest = src做:

  src           5   6  7  8    
  +-----+      +--+--+--+---+
  | 5   +----->| A|B |C | D |
  +-----+  --->+--+--+--+---+
           | 
  dest     |    18  19 20 21  
  +-----+  |   +--+--+--+---+
  | 5   +---   |  |  |  |   |
  +-----+      +--+--+--+---+

影子副本:

来自:Thomas(谢谢!)

  

更具体地说,后一种技术被称为“指针交换”   确实有其用途,但与深度不直接兼容   内存复制(它需要不同的设计,特别是两者   被“交换”的内存区域需要持久化)。对于矢量   实施它不太可能是我们的任何人

答案 1 :(得分:4)

*(void **)dest = *(void **)src;

destsrc两者解释为指向void*的指针,并将sizeof(void *)字节从src指向的位置复制到dest指向。

memmove(dest, src, elementSize);

elementSize字节从src指向dest指向的位置移动。如果属性elementSize == sizeof(void *),如果受影响的区域不重叠且srcdest都适当对齐,则两者都会产生相同的效果。如果destsrc中的任何一个未正确对齐,则第一个将调用未定义的行为(如果受影响的区域重叠,则至少有一个区域可能未正确对齐)。

如果您有特定的Element类型,则只需将destsrc投射到正确的类型,

*(Element *)dest = *(Element *)src;

达到预期的效果。

答案 2 :(得分:1)

这是因为C遵循的设计理念归结为“你不为你不需要的东西买单,而你却不希望你不要求”。特别是,当您声明变量具有类型void*时,您可以请求指向任何内容的指针,而您获得的只是 这样的指针;不是指针+类型字段+指向的东西的大小字段,因此编译器不知道您要复制多少字节。

请注意

dest = src;

工作,它只是不按预期执行。它重新指定dest指向与src相同的东西,这意味着你现在有两个同名的名字(可能还有内存泄漏)。这可能非常有用(除了泄漏),但它是一个非常浅的副本。

至于

*(void **)dest = *(void **)src;

在大多数情况下,这是未定义的行为。当它确实有效时,src实际上是指向void*的指针并将其分配给void*所指向的dest,所以它仍然是指针副本,间接的。

答案 3 :(得分:0)

有2个指针你可以做类似的事情:

*(char*) dest = *(char*) src;
*(double*) dest = *(double*) src;
*(struct{char[50] a;}*) dest = *(struct{char[50] a;}*) src;

相当于:

memcpy(dest, src, sizeof(char));

但不幸的是,C中的指针不知道它的分配大小。通过它的类型,人们知道它的元素大小,但任何数组大小都是未知的。

答案 4 :(得分:0)

*(void **)dest = *(void **)src;

将dest和src 视为指向void *的指针,并将void **src复制到*dst。< / p>

这导致复制sizeof(void *)字节,这可能比您的elementSize更多或更少(结果可能会。

(从您的样本中不清楚CVectorNth做了什么,它的参数是什么,或者实际的底层数据结构是什么)。

答案 5 :(得分:0)

memmove()的调用意味着内存从一个位置到另一个位置的有效副本。每当复制的内存大小正确时,调用将始终有效。

int x = 5;
int y = 6;

memmove( &x, &y, sizeof( int ) );

...这正是你的第一个代码中缺少的东西。指向void的指针void *是一种指向任何变量的方法,无论其类型是什么。这意味着在处理void *指针时没有固有的大小信息(因为不同类型的大小不同)。

例如:

int x = 5;
int * ptr = &x;

在此代码中,ptr包含两个信息:存储x的地址,以及32位系统中的四个字节的信息(64位中的8个字节) ),即sizeof(int)的结果。因此,这将有效:

*ptr = 11;

...因为系统知道存储rvalue(11)的位置以及字节数。

因此,为了使第一个代码起作用,我们应该编写类似的东西(假设向量元素的类型是int,并且我理解CVectorNth的作用):

*( (int *) dest ) = *( (int *) src );

无论如何,如果向量的元素包含指向堆中内存的指针,这将无法正常工作。好吧,你要复制指针,而不是它们指向的内容本身。如果你不记住这一点,你可能会发现自己有意外的惊喜。

希望这有帮助。