我正在向大尺寸结构指针投射数据缓冲区。它会引起任何问题吗?我在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);
}
答案 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
打包在一起,则有可能说服编译器以这种方式打包数据结构,只是让处理器在访问“双字节对齐”长时崩溃值。
换句话说:在不是预期长度的数据之上转换数据结构会导致各种问题,而编译器不会说任何内容。