【问题标题】:Understanding memcpy了解内存
【发布时间】:2012-06-13 23:54:33
【问题描述】:
int a = 10;
int* pA = &a;
long long b = 200;
long long* pB = &b;

memcpy (pB,pA,4);
memcpy (pB+1,pA,4);

cout<<"I'm a memcpy!: "<<*(pB)<<endl;

我正在使用 memcpy 进行一些测试,以自学内存的工作原理。我要做的是使 b = 为“1010”。我可以将值从 a 复制到 b,但随后我尝试将内存偏移 1 个字节并再写入 10,但它不起作用,它只输出“10”。

我需要做什么才能获得 1010 的值?

【问题讨论】:

  • pB+1 将其增加超过 1 个字节:确切地说是sizeof(long long) 字节。

标签: c++ memory memcpy


【解决方案1】:

您的代码存在一些问题:

  • 您复制了 4 个字节,但目标是类型 int。由于int 不能保证为任何特定大小,因此在执行那种memcpy 之前,您需要确保它至少有4 个字节长。
  • memcpy 在字节级别上工作,但整数是一系列字节。根据您的目标架构,整数中的字节可能会以不同的方式排列(大端、小端等)。在整数上使用memcpy 可能会也可能不会达到您的预期。学习memcpy和朋友的工作原理时最好使用字节数组。
  • 您的第二个memcpy 使用pB+1 作为目标。这不会使指针前进一个字节,而是将其前进sizeof(*pB) 字节。在这种情况下,它会指向一个无效的地址(在变量末尾之后)。对memcpy 的调用会损坏随机内存,这可能会使您的程序崩溃或导致不可预知的结果。

【讨论】:

  • 酷我学到了很多。不过,我确实有一个问题,您说 int 不保证占用 4 个字节……我以为是吗? 4bytes只是它可以占用的限制吗?当我创建一个 int 并将其 intilize 为一个值时,它是否只使用位数或字节 nesccary?
  • Int 大小取决于机器和编译器。我想不出没有 4 字节整数的现代编译器/CPU,但我能想到很多旧的。未来的机器很可能会有更大的整数。当我们得到 128 位机器时,int 可能是 64 位(8 字节)。
  • @user1372122- 基本类型shortintlonglong long 没有标准化大小。 int 具有固定 大小,但该固定大小因平台而异(例如在 x86 和 MIPS CPU 之间,或在 32 位和 64 位操作系统之间)。然而,种数据类型保证具有特定的大小。它们的名称类似于 uint32_tint16_t(分别代表 32 位无符号整数和 16 位有符号整数),它们来自 stdint.h
【解决方案2】:

我不认为memcpy() 是为你想要的而设计的。通常,您会使用memcpy() 复制一个或多个完整对象(其中对象可能是 int、char、long long 等)

    int a[4] = { 1, 2, 3, 4 };
    int b[3];
    int c[5] = { 0 };

    ::memcpy(b, a, 3 * sizeof(int));   // b is { 1, 2, 3 }
    ::memcpy(c+2, b, 3 * sizeof(int)); // c is { 0, 0, 1, 2, 3 }

c+2 不是“c + 2 个字节”。它是“c + 2 ints”(在 Win32/x86 系统上为 8 个字节)。

可以通过转换为 char 或 unsigned char 指针来访问各个字节,但我不建议您这样做,除非您真正了解自己在做什么,因为有很多陷阱。

    unsigned x = 0;
    unsigned char *px = reinterpret_cast<unsigned char *>(&x);

    px[0] = 0xFF;
    px[2] = 0xAA;

这里的一个危险是你假设你知道计算机如何存储一个整数。在 x86 系统上,x 将为 0x00AA00FF,但在 Sun Sparc 系统上,x 将为 0xFF00AA00。

如果您需要设置整数的一部分,通常最好使用“或”和“移位”。

    x = (0xFF<<24) | (0xAA<<8);

将在任何架构上为您提供 0xFF00AA00。 0xFF

我们将它们“或”在一起,给出 0xFF00AA00。

【讨论】:

    【解决方案3】:

    查看指针算法:http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/pointer.html。向指针添加值实际上会增加你正在处理的任何大小的单位。

    【讨论】:

      【解决方案4】:

      pB 是 long long* 类型,因此将其加 1 将给出变量 b 后面的下一组 8 个字节的地址。您将 pB[0] 作为变量 b,但 pB[1](相当于 pB+1)指向未定义的内存。写入它可能会导致崩溃。

      替换

      long long b;
      

      long long b[2];
      

      (使用你喜欢的任何初始值)

      你的 cout 需要同时喂 b[0] 和 b[1]。

      【讨论】:

        【解决方案5】:

        如果你想将“10”复制到之后的字节并得到“1010”,你想复制字符串。这确实是 strcat() 而不是 memcpy() 的工作,但您可以使用 memcpy() 做到这一点:

        #include <assert.h>
        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
        
        int main(void)
        {
          constexpr char src[] = "10";
          constexpr size_t n = sizeof(src) - 1; // Don’t count the terminator.
          char dest[2*n+1] = {'\0'}; // Will hold: {'1','0','1','0','\0'}
        
          static_assert( sizeof(dest) > 2*n, "" ); // Always check array bounds in C++!
        
          memcpy( dest, src, n );
          memcpy( dest + n, src, n );
        
          puts(dest);
        
          return EXIT_SUCCESS;
        }
        

        特别要注意,目标是一个长度足以容纳两个副本的字节数组。

        【讨论】:

          【解决方案6】:

          我的信誉评分不 >= 50,所以我不能发表评论,即使是评论帖子的原作者 -> 从我的角度来看,这简直是愚蠢的......

          所以,这是我对 user207421 发表的评论的回答:

          1. 过去 20 年(至少)在 PC 中使用的每个 CPU 中都实现了 SIMD 指令,因此除非您使用一些 非常 旧的 CPU,否则您关于“最近足够" CPUs 基本无效。

          2. 请参阅我上面帖子中的第 2 点:“最快的架构相关方法”意味着 f.e.在 8 位 AVR 上,“movw”指令可以(并且它正在)用于实现类似 memcpy 的快速功能,因为它的工作方式类似于 x86 上的 SIMD:它可以在单个周期内复制 2 个 8 位 CPU 字.

          问候。

          【讨论】:

            【解决方案7】:

            memcpy 在字节级别上工作,但整数是一系列字节。

            错误的解释。

            上述答案对于strcpy() 来说或多或少是正确的,但在memcpy() 的情况下则完全错误,因为所有mem_xxx() 函数都使用SIMD 指令来加速操作。

            如果memcpy() 函数是基于逐字节复制的,那将会非常缓慢...

            基本上所有的mem_xxx函数都是这样实现的:

            1. 如果源和/或目标指针未对齐,则这些函数通过首先处理未对齐/奇数内存块来对齐指针。
            2. 接下来,使用最快的依赖于架构的方法来处理对齐的内存区域。
            3. 最后,处理剩余的未对齐/奇数字节。

            我建议在发布错误答案之前阅读代码。

            【讨论】:

            • 您似乎在评论this answer。你应该说清楚。您自己的答案假定 x86 是最新的,可以有 SIMD 指令。
            猜你喜欢
            • 1970-01-01
            • 2019-07-08
            • 2019-06-02
            • 2011-06-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-12-13
            相关资源
            最近更新 更多