您好我有一个关于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;
不起作用
答案 0 :(得分:4)
因为*(void **)dest = *(void **)src;
!= memmove(dest, src, elementSize);
首先只是分配操作,其中memmove()
将内存内容从src
复制到dest
(深复制)
修改强>
假设您的dest
和src
是这样的?
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;
将dest
和src
两者解释为指向void*
的指针,并将sizeof(void *)
字节从src
指向的位置复制到dest
指向。
memmove(dest, src, elementSize);
将elementSize
字节从src
指向dest
指向的位置移动。如果属性elementSize == sizeof(void *)
,如果受影响的区域不重叠且src
和dest
都适当对齐,则两者都会产生相同的效果。如果dest
和src
中的任何一个未正确对齐,则第一个将调用未定义的行为(如果受影响的区域重叠,则至少有一个区域可能未正确对齐)。
如果您有特定的Element
类型,则只需将dest
和src
投射到正确的类型,
*(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 );
无论如何,如果向量的元素包含指向堆中内存的指针,这将无法正常工作。好吧,你要复制指针,而不是它们指向的内容本身。如果你不记住这一点,你可能会发现自己有意外的惊喜。
希望这有帮助。