memcpy和startIndex有什么关系?

50

我希望从指定起始点将特定长度的内容从一个缓冲区复制到另一个缓冲区。我查看了memcpy(),但它仅接受要复制的内容长度,而我想要指定起始索引。

是否有任何函数可以执行此操作,或者是否有任何好的方法可以使用现有的memcpy函数进行操作?


请注意:memcpy是C语言的,而不是C++。尽管我经常使用它,但毫无疑问,memcpy与C++的主要概念,如类型安全、继承、异常处理等完全相反。使用memcpy很容易搞砸一切。 - RED SOFT ADAIR
4
memcpy是C++标准库的一部分。 - anon
当然,从 C 头文件中提供了 memcpy 函数。但是,C++ 本身提供了 std::copy 函数,通常情况下它与 memcpy 相当甚至更好,并且更加灵活和类型安全。 - underscore_d
9个回答

102

我总是更喜欢这种语法

memcpy( &dst[dstIdx], &src[srcIdx], numElementsToCopy * sizeof( Element ) );

10
同样的。在如此低层次的内存复制中,你不能使用过多的文字。给你的索引命名得当会增加你在5个月后能够准确回忆起正在发生的事情的机会。 - Matt Green

33

只需将您想要的偏移量添加到缓冲区的地址即可。

char abuff[100], bbuff[100];
....
memcpy( bbuff, abuff + 5, 10 );

这个程序会将从abuff[5]开始的10个字节复制到bbuff中。


2
这只是一个稍微有些误导性的例子。它之所以能够工作,是因为 char 类型保证长度为 1 字节。对于所有其他大小不等于 1 的类型,这段代码虽然可以编译,但运行时会出现错误(当然除了 void 类型)。 - JaredPar
1
@JaredPar:依我之见,Neil的回答完全正确。问题陈述为“...我想指定起始索引”。将常量添加到指针中,将考虑指向的类型的大小。即“int *p = ...; p += 5;”将使‘p’指向更大的sizeof(int)*5字节。 - Frerich Raabe
问题中根本没有提到char,但术语“buffer”略微暗示了它。 - CsTamas
7
无论abuff的类型是什么,该代码都会执行我所说的操作。 - anon
+1,做了被要求的事情。它按照你说的去做,我认为这就是OP所说的。但是这可能有些争议,因为OP想要什么还不确定。虽然提到了“起始索引”,但OP可能打算将该索引量化为字节,因为它与长度在同一呼吸中提到。如果它实际上不是源数组中的索引,那么需要更多的转换。这是否是JaredPar所假设的,导致得出一个结论,即数组类型影响答案的正确性? - Steve Jessop
@Frerich,我并没有说Neil的答案是错的,只是觉得有点误导。我读问题的方式是startIndex是一个字节偏移量而不是索引。大多数人都是另一种理解方式。 - JaredPar

14

只需要将偏移量加到地址上即可。例如,如果您想复制从第N个字节开始的缓冲区:

memcpy( destination, source + N, sourceLen - N );

这将复制到destination。如果你还想偏移目标位置-在两个位置都加上偏移量:

memcpy( destination + N, source + N, sourceLen - N );

2
这仅适用于源指向长度为1字节的类型的情况。对于所有其他情况,您将获得不正确的偏移量(void根本无法编译)。 - JaredPar
@JaredPar:没错,那么就需要进行更多的算术运算。 - sharptooth

4

不需要索引,因为您可以通过指定的字节数简单地更新源指针。以下包装器应该能解决问题。

void* memcpy_index(void *s1, const void *s2, size_t index, size_t n) {
  s2 = ((char*)s2)+index;
  return memcpy(s1, s2,n);
}

你假设索引是字节索引,n 是字节数。在这种情况下,s1 和 s2 也可以被定义为 char * 类型。 - Indy9000
@Indeera,我所做的唯一假设是索引(index),就像大小字段n一样,以字节为单位指定。 - JaredPar

2

只需将指针增加到起始索引。

示例

const unsigned char * src = reinterpret_cast<const unsigned char*>(your source);
unsigned char * dest = reinterpret_cast<unsigned char *>(your dest);
memcpy(dest, src + offset, len);

使用STL集合来避免内存访问错误怎么样?

1

这个网站需要一种方法,允许匿名回复,除了匿名答案。

为什么我会看到这种疯狂的说法,认为“索引”必须以1字节为单位?这完全与惯例相反。“索引”通常是象征性的,其物理字节偏移量由数组中元素的大小确定(或向量,可能甚至没有数组的物理布局,但是memcpy()当然也无关紧要)。

因此,数组中的第5个元素具有“索引”5,但:

  • 如果数组是char类型,则该“索引”的字节偏移量为5。
  • 如果数组是short类型(在x86上),则该“索引”的字节偏移量为10。
  • 如果数组是int类型(在x86上),则该“索引”的字节偏移量为20。
  • 如果数组是某个大型48字节对象的类型,则该“索引”的字节偏移量为240。

哪种方式是访问特定元素的正确方式是一个次要问题。重要的是您理解差异,选择一种并使代码正确。


在词义方面,我更愿意阅读:

 void* memcpy_offset(void *s1, const void *s2, size_t offset, size_t n);

比:

 void* memcpy_index(void *s1, const void *s2, size_t index, size_t n);

我认为一个完全通用的void *可以有一个“索引”的想法是具有误导性的。(顺便说一下,“dest”和“source”或“in”和“out”比“s1”和“s2”不太含糊。当您选择自说明的变量名称时,代码不需要太多注释。)


很好的解释,但并没有回答问题。 - YScharf

0
你可以编写如下的函数。
template<typename T>
T* memcopy_index(T* dst,T* src,unsigned int index, unsigned int element_count)
{
    return (T*)memcpy(dst,src + index, element_count * sizeof(T));
}

它可以如下使用:

int src[]={0,1,2,3,4,5,6};
int dst[15];

memcopy_index(dst,src,2,5);    //copy 5 elements from index 2

你必须确保目标缓冲区有足够的空间来复制元素。


0
如果您正在使用C ++,最好使用std :: copy()而不是memcpy()。 std :: copy可以像迭代器一样轻松地接受指针。
例如:
int src[20];
int dst[15];

// Copy last 10 elements of src[] to first 10 elements of dst[]
std::copy( src+10, src+20, dst );

与memcpy()一样,您有责任确保指针是有效的。

注意。如果您的使用对性能至关重要,您可能会发现在其他答案中详细介绍的memcpy()更快,但差别可能不大。


1
你的实现可以自由地使用 memcpy 实现对于普通旧对象的 std::copy。 - Matt Green

0

只需将索引添加到缓冲区的地址中,并将其作为源参数传递给memcpy(),例如从缓冲区b的第3个项目复制

char a[10], b[20];
::memcpy(a,b+2,10);

还要考虑缓冲区中项目的类型,memcpy()的长度(第三个)参数以字节为单位,因此要复制4个int,您应该放置4*sizeof(int) - 这可能是16(在32位系统上)。但是对于起始地址,类型并不重要,因为指针算术:

int a[10], b[10];
::memcpy( a+2, b, 2*sizeof(int) );
// a+2 will be address of 3rd item in buffer a
// not address of 1st item + 2 bytes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接