使用 memcpy 复制 n 个字符

Copying n characters using memcpy

我正在尝试使用 memcpy 复制 32 个字符的确切数量,但是我在正确使用它时遇到问题,因为多个在线 g++ 编译器以及我机器上的编译器给出的结果与相同的源代码。

代码:

#include <iostream>
#include <cstring>

int main()
{
    const char* source = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eu ipsum nec elit mattis consequat. Curabitur sollicitudin ligula et quam bibendum euismod.";
    char dest[32];

    std::memcpy(&dest, source, sizeof(dest));
    std::cout << dest << "(" << strlen(dest) << ")";
}

代码编译于 here (G++4.9.2)。

输出不包含 32 个字符(正在添加垃圾值):

Lorem ipsum dolor sit amet, cons †¿(36)

代码编译于 here (G++4.9)。

输出包含所需的结果:

Lorem ipsum dolor sit amet, cons(32)

我机器上的输出类似于第一个输出(36 个字符)。

为什么每个结果都不一样?

memcpy 复制 n 个字符的有效用法应该是什么?

strlen函数和operator<< (const char *)函数只适用于C风格的字符串。它们不能用于输出或测量任意数据块的长度。

想一想——他们怎么可能确定长度?他们可能使用什么方法?

Why is each result different?

因为您使用的函数只能用于非 C 风格字符串的 C 风格字符串。这是一个根据平台内存布局的具体情况而表现不同的错误。

What should be the valid usage of memcpy to copy n numbers of characters?

就是这样。你复制了字符。但是现在你只有一堆字符,而不是字符串。如果您使用打印字符束的函数,它们会工作正常。

试试这个:

#include <iostream>
#include <cstring>

int main()
{
    const char* source = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eu ipsum nec elit mat
    char dest[32];

    std::memcpy(&dest, source, sizeof(dest));

    for (int i = 0; i < sizeof(dest); ++i)
        std::cout << dest[i];
}

dest 不是空终止的。因此,像 strlenoperator << 这样的函数不知道它们已经到达缓冲区的末尾,并且即使在到达 32 个字符后仍会继续。在 dest[31] 之后的未知内存中遇到 null 时,它们会停止,可以是在 10、1000、1000000 字节甚至根本没有之后。您需要的是:

#include <iostream>
#include <cstring>

int main()
{
    const char* source = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eu ipsum nec elit mattis consequat. Curabitur sollicitudin ligula et quam bibendum euismod.";
    char dest[33];

    std::memcpy(&dest, source, sizeof(dest)-1);
    dest[32] = '[=10=]';
    std::cout << dest << "(" << strlen(dest) << ")";
}

memcpy 函数不检查源中的任何终止空字符 - 它总是精确地复制 num 个字节。你应该以 null 结束。

您可以使用 std::string:

#include <iostream>
#include <string>

int main()
{
    const char* source = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eu ipsum nec elit mattis consequat. Curabitur sollicitudin ligula et quam bibendum euismod.";

    std::string s(source, 32);
    std::cout << s << "(" << s.length() << ")";
}

对于memcpy,你通常不想用在c风格的字符串上,因为字符串的长度是内存块的大小减1。

这是因为C++内存分配的奇怪方法

解决方案之一是确定数组的长度。 如果一个数组定义在函数内部,它不会显示为好像它充满了 nuls。更明显的是,strlen() 函数通过找到第一个 nul 字节来计算字符串的长度。在函数内部,变量最初未初始化,并且将包含任意数据。这块内存,是直接从操作系统堆中取出来的。

如果像这样把数组放在外面:

#include <iostream>
#include <cstring>
using namespace std;
char dest[32];

int main(int argc, char** argv) {
....

它将正常运行,因为在函数外部声明的任何变量最初都初始化为零。

解决这个问题的另一种方法是,就像@Lucas 所说的那样,在外面留一个字节为空,即:

char dest[33];
memcpy(dest, source, sizeof(char) * 32);

这不受数组是否位于函数外部的影响。

具体的strlen函数原理类似:

int strlen(char* str)
{
    for (int i = 0; ; i++)
        if (str[i] == 0)
            return i;
    return 0;
}

有网友指出我不能保证第33字节是空的。现在我找到了解决方案:

char dest[33];
memset(dest, 0, sizeof(char) * 33);
memcpy(dest, source, sizeof(char) * 32);

或者简单地将最后一个字节设置为nul。

char dest[33];
dest[32] = 0;

一些更安全和更好看的方法包括直接内存分配。然而根据一些统计数据,new 命令和 malloc() 函数会导致性能下降。

char *dest = new char[32];
memcpy(dest, source, sizeof(char) * 32);

如果你使用下面的代码,你会遇到意想不到的结果。

char *dest = new char[32];
memcpy(dest, source, sizeof(char) * 32);

因此,在 C/C++ 中编程时,请始终记住考虑边界。