3

您好,我有一个关于 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;

行不通

4

6 回答 6

4

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

编辑

假设,你的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   +---   |  |  |  |   |
  +-----+      +--+--+--+---+

影印本:

来自:托马斯(谢谢!)

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

于 2013-05-23T09:49:34.213 回答
4
*(void **)dest = *(void **)src;

将 和都解释为指向 的指针,dest并将字节从 where点复制到 where点。srcvoid*sizeof(void *)srcdest

memmove(dest, src, elementSize);

elementSize字节从 wheresrc点移动到 wheredest点。如果elementSize == sizeof(void *)可能,如果受影响的区域不重叠并且两者都适当对齐,则两者将具有相同的src效果destdest如果其中一个和没有正确对齐,第一个将调用未定义的行为src(如果受影响的区域重叠,至少一个可能不会正确对齐)。

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

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

以达到预期的效果。

于 2013-05-23T09:58:31.703 回答
1

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

注意

dest = src;

确实有效,只是没有达到您的预期。它重新分配dest以指向与 相同的事物src,这意味着您现在有两个相同事物的名称(并且可能是内存泄漏)。这可能非常有用(除了泄漏),但它是一个非常浅的副本。

至于

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

在大多数情况下,这是未定义的行为。当它起作用时,它src实际上是一个指针void*并将其分配给void*指向的 by dest,因此它仍然是一个指针副本,尽管是间接的。

于 2013-05-23T09:57:14.190 回答
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;

...因为系统知道在哪里存储右值 (11),以及存储多少字节。

所以,为了让第一个代码工作,我们应该写类似的东西(假设向量元素的类型是int,并且我理解它是做什么CVectorNth的):

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

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

希望这可以帮助。

于 2013-05-23T10:03:27.107 回答
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 中的指针不知道其分配的大小。通过它的类型知道它的元素大小,但任何数组大小都是未知的。

于 2013-05-23T09:56:57.203 回答
0
*(void **)dest = *(void **)src;

将 dest 和 src视为指向 a 的指针,并将其从void *复制到。void **src*dst

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

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

于 2013-05-23T10:00:01.710 回答