【问题标题】:How does this work? copying anything into an array of bytes (chars)这是如何运作的?将任何内容复制到字节数组(字符)中
【发布时间】:2010-09-22 18:10:54
【问题描述】:
struct MyRect
{
    int x, y, cx, cy;
    char name[100];
};

int main()
{
    MyRect mr;
    mr.x = 100;
    mr.y = 150;
    mr.cx = 600;
    mr.cy = 50;
    strcpy(mr.name, "Rectangle1");

    MyRect* ptr;

    {
        unsigned char bytes[256];

        memcpy(bytes, &mr, 256);

        ptr = (MyRect*)bytes;
    }

    printf("X = %d\nY = %d\nCX = %d\nCY = %d\nNAME = %s\n", 
        ptr->x, ptr->y, ptr->cx, ptr->cy, ptr->name);

    return 0;
}

我只是在测试如何将结构/类放入字节数组中,当它编译和工作时我很惊讶,printf 打印了我在 mr 变量中设置的所有值。

对“ptr”到底指的是什么有点困惑?它是否在某处为 ptr 分配了内存?

【问题讨论】:

  • 那么是否可以使用不同的方法通过字节数组传递任何数据,而不会出现未定义的行为?
  • hmm 只是想创建一个变量 MyRect 而不是指针,而不是 ptr = (MyRect*)bytes;复制 = (MyRect)bytes;然后当我超出范围或进入不同的功能时,这会保证数据的副本吗?所以我可以做一些事情,比如通过 TCP 通过套接字发送我自己的类/结构

标签: c++ c memory


【解决方案1】:

这纯属偶然。

首先,您基本上是逐字节复制该结构并使用memcpy 将其放置在堆栈分配的缓冲区中。但是,您不应该在实践中这样做。这次碰巧起作用了,因为您的结构是POD(普通旧数据或C-结构),但是如果您的结构是带有构造函数/复制构造函数的C ++对象或您有什么,您可能已经得到一个令人讨厌的惊喜。

其次,当您通过指针使用它时,包含该结构的堆栈分配缓冲区超出范围,因此您所做的完全是undefined behavior。它只能在纯属偶然的情况下工作,不能保证在不同的计算机或使用不同的编译器甚至在一天中的不同时间再次工作。

【讨论】:

  • 另外,未定义行为的另一个原因是您正在复制 256 个字节,而您的结构的大小仅为 116 个字节左右(根据实现而定。)所以您可能正在复制一半的调用堆栈进入缓冲区,导致更有趣的未定义行为。
  • 哎呀,我认为参数占用了目的地的大小,
  • memcpy 的 size 参数是指要复制的字节数,这意味着它将尝试从源读取那么多字节,并将那么多字节写入目标。
  • 我真的笑死了,more hilarious undefined behavior。你让我的日子过得更好,谢谢。 :-)
【解决方案2】:

unsigned char bytes[256] 分配在堆栈上,即每次输入您的函数(在本例中为 main)时,堆栈上都会为变量 bytes 保留 256 个字节。通过演员ptr现在指向堆栈上的这个区域并被解释为MyRect类型。由于您首先将这样的结构复制到堆栈区域,这一切都很好且有效。但是一旦你离开mainptr 指向的区域就消失了,所以你不能在这个函数之外存储指向该区域的指针。

【讨论】:

    【解决方案3】:

    好吧,您的程序会导致未定义的行为,因此您可能不会对它碰巧起作用感到惊讶。或者如果它碰巧不起作用或导致宇宙终结,就此而言。在包含bytes 定义的块之后,ptr 超出范围,并且可能仍指向有效内存,也可能不指向有效内存。在你的情况下,确实如此。但你不能依赖这种行为。

    【讨论】:

    • 我同意詹姆斯的观点。特别是因为大多数编译器很可能会愉快地在堆栈帧中保留相应的空间,就好像没有子范围一样。
    • 没有解释为什么内存在块之后应该是无效的:-)
    【解决方案4】:

    ptr 仍然指向bytes 的地址。或者,曾经被称为bytes。即使您已将bytes 放入其自己的块中,并且该变量在该块之外在语义上是不可访问的,但在函数退出之前,内存会一直保持不变。这是一种典型的实现技术,但没有被标准定义,所以不要依赖它。

    【讨论】:

      【解决方案5】:
      ptr = (MyRect*)bytes;
      

      “bytes”是数组在内存中的地址。

      ptr 在这段代码中被分配了那个地址。

      强制转换告诉编译器忽略数据类型的差异。

      如果您详细了解编译器在幕后所做的工作,这当然可以正常工作。唯一的问题是更改编译器或编译器设置可能会导致此代码失败。它可能有点脆弱。

      【讨论】:

        【解决方案6】:

        它之所以有效,是因为尽管 'bytes' 数组超出范围,但在您调用 printf() 时,它所在的堆栈空间还没有被踩到。它也可以工作,因为虽然 'mr' 不是 256 字节大,但它后面的内存(在堆栈上)并不关心你正在阅读它。

        C 是一种非常松散的非类型安全语言。指针可以指向几乎任何内存位置,并且您可以转换为您喜欢的任何指针类型。

        所以我同意,你的程序基本上是偶然的。但它确实这样做了,因为 C 允许使用指针来完成一些疯狂的事情。

        【讨论】:

          猜你喜欢
          • 2013-05-26
          • 1970-01-01
          • 1970-01-01
          • 2018-10-05
          • 2011-12-07
          • 1970-01-01
          • 1970-01-01
          • 2015-03-19
          • 1970-01-01
          相关资源
          最近更新 更多