了解记忆

Understanding memcpy

本文关键字:记忆 了解      更新时间:2023-10-16
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?

你的代码存在一些问题:

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

我不认为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 或无符号 char 指针来访问各个字节,但我不建议这样做,除非您真正了解自己在做什么,因为有很多陷阱。

    unsigned x = 0;
    unsigned char *px = reinterpret_cast<unsigned char *>(&x);
    px[0] = 0xFF;
    px[2] = 0xAA;

这里的危险之一是您假设计算机如何存储整数的知识。 在 x86 系统上,x 将被0x00AA00FF但在 Sun Sparc 系统上,它将是0xFF00AA00。

如果您需要设置整数的一部分,通常最好使用"or"和"shift"。

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

将为您提供任何架构0xFF00AA00。0xFF<<24 将0xFF的值向左移动 24 位,从而0xFF000000。0xAA<<8 将0xAA的值向左移动 8 位,从而0x0000AA00。

我们一起"或"他们,给予0xFF00AA00。

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

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

取代

long long b;

long long b[2];

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

你的口水需要同时喂食 b[0] 和 b[1]。

如果要将"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] = {''}; // Will hold: {'1','0','1','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;
}

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

我没有声誉得分>= 50,所以我无法发表评论,即使作为评论帖子的原作者 ->在我看来这简直是愚蠢的......

因此,这是我对user207421发布的评论的回答:

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

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

问候。

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

错误的解释。

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

如果memcpy()函数基于逐字节复制,那将是致命的慢......

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

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

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