铸造具有更大尺寸结构的缓冲器

时间:2013-05-17 05:51:50

标签: c++ casting

我正在向大尺寸结构指针投射数据缓冲区。它会引起任何问题吗?我在Visual Studio上尝试了以下代码,没有发现任何警告或错误。

#include <iostream>
using namespace std;

struct test
{
    char var1;
    char var2;
    long var3;
};
void function(char* data);

int main (void)
{
    char data[5] = {1, 0, 0, 3, 4};
    function(data);

    system("Pause");
    return 0;
}

void function(char* data)
{
    test* pTest = reinterpret_cast<test*>(data); // casting
    printf("%x\n", pTest->var1);
    printf("%x\n", pTest->var2);
    printf("%x\n", pTest->var3);
}

3 个答案:

答案 0 :(得分:4)

您必须了解内存管理的工作原理。您静态分配了数组data,因此它将被放在堆栈上(它包含具有被调用函数的局部变量的帧)。堆栈是为每个进程静态分配的(这意味着,通过进程生命周期,它具有恒定的大小)。

从现在开始,堆栈看起来更像是这样:

data[1, 0, 0, 3, 4]

现在你调用了函数function。新的框架被放在堆栈上,所以看起来更像是这样:

data[1, 0, 0, 3, 4] | return-pointer[a b c d] pTest [e f a b]
(main data)           (function data)

现在,您将数据转换为您的类型,其大小至少为6个字节(假设它可能需要8个或更多字节,具体取决于类内容对齐)。所以你试着访问这些字节:

data[1, 0, 0, 3, 4] | return-pointer[a b c d] pTest [e f a b]
(main data)           (function data)
     *  *  *  *  *                   *

(请注意,我的堆栈可视化简化了,可能会在调用函数后剩下更多数据)。

您在以后的printf期间访问的所有内存都属于您的应用程序,因此操作系统不会引发访问冲突错误。您阅读了数据,因此您不会损坏任何内容。但现在假设你要写一些东西给pTest->var3。这些字节将被覆盖:

data[1, 0, 0, 3, 4] | return-pointer[a b c d] pTest [e f a b]
(main data)           (function data)
     *  *  #  #  #                   #

现在看,你刚刚损坏了返回指针 - 很可能,你的程序现在会在尝试退出function并返回main时崩溃。

通常强烈建议仅投射兼容的数据类型(大小相同)。编译器通常无法知道,如果你的演员表是有效的(特别是,如果你在路上投射指向void *)。因此,你应该非常谨慎地在你的程序中进行演员表。

答案 1 :(得分:0)

这种类型的演员非常危险(换句话说,你对编译器说 - “我知道我这样做”),所以在这种情况下你必须自己检查这些演员的正确性。

答案 2 :(得分:0)

reinterpret_cast<xxx>(yyy)告诉编译器“相信我,我知道我在这里做什么”。这意味着你必须绝对确定自己正在做的事情是正确的。

在这种特殊情况下,你会导致未定义的行为,因为你的结构比你传入的数据大,这绝对不是也绝不是一件好事 - 它可能会导致几乎任何事情发生(并且包括“类似于你期望的那样“) - 更糟糕的是,它可能会因编译时使用的选项而有所不同,因此它可能看起来在调试模式下工作,然后在发布模式下编译时会出现问题。至少var3会有一些未定义的内容,基于以下事实:var3中至少有一部分不在您定义的数据之内。这取决于long的大小和范围。在Windows中,这是4个字节并对齐到4个字节。因此,在两个char字段和long数据之间有2个字节的空间。

但是,如果您知道自己在做什么,并且未在var3代码中访问function,则可以使用此类方案。

最后,如果这是comms协议的一部分,则可能需要从“填充是/需要添加什么”的角度来看待它。如果协议需要一定的间隔,则需要正确处理 - 例如,处理器可能对“长”读取的对齐有严格的要求。因此,如果数据实际上以BYTE|BYTE|LONG打包在一起,则有可能说服编译器以这种方式打包数据结构,只是让处理器在访问“双字节对齐”长时崩溃值。

换句话说:在不是预期长度的数据之上转换数据结构会导致各种问题,而编译器不会说任何内容。