【问题标题】:assigning pointers vs memcpy/memmove分配指针与 memcpy/memmove
【发布时间】:2013-05-23 09:47:35
【问题描述】:

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

没用

【问题讨论】:

标签: c pointers gcc


【解决方案1】:

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

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

dest = src点赞:

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

影印:

发件人:Thomas(谢谢!)

更具体地说,后一种技术称为“指针交换” 确实有它的用途,但不直接兼容 deep 内存副本(它需要不同的设计,尤其是两个 被“交换”的内存区域需要是持久的)。对于向量 实施它不太可能属于我们

【讨论】:

  • 更具体地说,后一种技术被称为“指针交换”并且确实有它的用途,但不直接与深内存复制兼容(它需要不同的设计,特别是两个内存区域是“交换”需要持久)。对于矢量实现,它不太可能有任何用处。
  • 很棒的图表。谢谢 Grijesh,这些真的很有帮助
【解决方案2】:
*(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;

达到预期的效果。

【讨论】:

  • 我非常喜欢你的回答
【解决方案3】:

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

注意

dest = src;

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

至于

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

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

【讨论】:

  • @sbjluke:你知道,但编译器不知道,因为它的类型为void*。那是可以指向任何东西的 C 类型:FILEconst int **const *struct foo[4]
  • 好的,我现在明白了,谢谢。那么为什么 dest = src 不适合我。我知道这是一个浅拷贝,但是在堆栈上传递指向内存的指针似乎应该可以工作。
  • 谢谢!你是对的*(void **)dest = *(void **)src; 是未定义的行为.. 我没有限制写这个..
  • 我想通了。事实上,向量作为数组工作的方式我无法更改指针的值。我必须改变它所指向的值。
【解决方案4】:

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

【讨论】:

    【解决方案5】:
    *(void **)dest = *(void **)src;
    

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

    这会导致复制sizeof(void *) 字节,这可能大于或小于您的elementSize(结果可能。

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

    【讨论】:

      【解决方案6】:

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

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

      希望这会有所帮助。

      【讨论】:

        猜你喜欢
        • 2011-05-23
        • 2011-07-21
        • 2011-12-08
        • 1970-01-01
        • 2015-01-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多