使用memcpy复制n个字符

时间:2016-04-15 10:21:58

标签: c++ c++11 memcpy

我正在尝试使用memcpy复制32个字符的确切数量,但是由于多个在线g ++编译器以及我的机器上的编译器使用相同的源提供了稍微不同的结果,因此我遇到了正确使用它的问题代码。

代码:

Google Console

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

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

#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)。

输出包含所需的结果:

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

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

为什么每个结果都不同?

memcpy有效使用什么来复制n个字符?

5 个答案:

答案 0 :(得分:4)

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

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

  

为什么每个结果都不同?

因为您使用的函数只能用于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];
}

答案 1 :(得分:3)

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] = '\0';
    std::cout << dest << "(" << strlen(dest) << ")";
}

答案 2 :(得分:2)

您可以使用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.

答案 3 :(得分:1)

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

其中一个解决方案是确定阵列的长度。 如果在函数内部定义了数组,则不会将其显示为充满 nul 。更明显的是,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 ++进行编程时,请务必考虑边界。

答案 4 :(得分:0)

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