使用 memcpy 复制 n 个字符

Copying n characters using memcpy

本文关键字:字符 复制 memcpy 使用      更新时间:2023-10-16

我正在尝试使用 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) << ")";
}

此处编译的代码 (G++4.9.2(。

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

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

此处编译的代码 (G++4.9(。

输出包含所需的结果:

Lorem ipsum dolor sit amet, cons(32)

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

为什么每个结果不同?

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

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

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

为什么每个结果不同?

因为您正在使用只能在非 C 样式字符串上使用的函数。这是一个错误,根据平台内存布局的细节,其行为会有所不同。

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

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

试试这个:

#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不是以 null 结尾的。因此,像 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] = '';
    std::cout << dest << "(" << strlen(dest) << ")";
}
您可以使用

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;

一些更安全的方法和更漂亮的方法包括直接内存分配。然而,根据一些统计数据,命令和 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++ 编程时,请始终记住要考虑边界。

memcpy 函数不会检查源中的任何终止空字符 - 它总是准确地复制字节数。应以 null 终止。