繁体   English   中英

分配指针vs memcpy / memmove

[英]assigning pointers vs memcpy/memmove

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

我正在使用void *指针指向任意blob的内存,它们充当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;

不行

因为*(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的答案

然而,通过做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

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

通过dest = src做:

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

影子副本:

来自: 托马斯 (谢谢!)

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

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

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

memmove(dest, src, elementSize);

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

如果你有一个特定的Element类型,你只需要将destsrc转换为正确的类型,

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

达到预期的效果。

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

注意

dest = src;

确实有效 ,它只是没有做你期望的事情。 它重新指定dest指向与src相同的东西,这意味着你现在有两个同名的名字(可能还有内存泄漏)。 这可能非常有用(泄漏除外),但它是一个非常浅的副本。

至于

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

在大多数情况下,这是未定义的行为。 当它工作时,它需要src实际上是一个指向void*的指针,并将它指定给dest指向的void* ,所以它仍然是一个指针副本,虽然是间接副本。

有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中的指针不知道它的分配大小。 通过它的类型,人们知道它的元素大小,但任何数组大小都是未知的。

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

将dest和src 视为指向void *的指针,并将该void **src复制到*dst

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

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

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 );

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

希望这可以帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM