可以通过以下方式将结构转换为不同的大小吗?
Posted
技术标签:
【中文标题】可以通过以下方式将结构转换为不同的大小吗?【英文标题】:Can structs be cast to a different size in the following way? 【发布时间】:2013-08-21 21:07:25 【问题描述】:操作系统:Windows x86、MFC、CRT、VS2010
从 Visual Studio 2005 (SP1) 升级到 VS2010-SP1 后,我遇到了运行时 CRT 堆损坏异常。
当我的应用程序使用调试编译时,CRT 只会抱怨损坏。而且我认为 VS2010 在评估内存访问时过于严格。
可疑的违规代码执行以下操作:
typedef struct _T_TEST
DWORD flag;
TCHAR str[1];
T_TEST;
// Then the structure is used in this way
void test(TCHAR * p_str)
DWORD size = sizeof(DWORD) + (_tcslen(p_str) + 1) * sizeof(TCHAR);
T_TEST * foo = (T_TEST *) calloc(size, 1);
foo->flag = 0;
_tcscpy_s(foo->str, size - sizeof(DWORD), p_str);
// Do IPC with the struct
// When finished, free it
free(foo);
此代码有时会在 free(foo)
上失败,而在将分配的缓冲区转换为 (T_TEST *)
时有时会失败。
以这种方式扩展结构大小是否有效? VS2010 中是否有一些不同的东西将其视为堆损坏?
任何帮助将不胜感激!
【问题讨论】:
Mark Ransom 指出了您的错误。我只想评论一下,如果 Visual Studio 说你的堆损坏了,它就是在告诉你真相并试图帮助你。请不要与之争论!努力寻找错误,而不是试图证明边缘代码的合理性。 您的问题也被标记为 C++,但您使用的是 C 技术(calloc 和 free 而不是 new 和 delete)现代 C++ 比 C 安全得多,如果您正确使用它,您会少很多可能会遇到此类错误。 注明。如何删除标签? (没关系) 【参考方案1】:_tcscpy_s
的第二个参数是元素的数量,但您输入的是字节数。根据the documentation:
这些函数的调试版本首先用 0xFE 填充缓冲区。
如果 sizeof(TCHAR) != 1,这将导致缓冲区溢出。
【讨论】:
呃!你一定认为我是个大白痴。我正在将一个大型项目转换为使用 Unicode,并且有成千上万个这样的小错误。谢谢您的帮助。但我仍然很好奇这种动态大小增加是否可以接受。 @rbhkamal,我从不判断 - 我们都有糟糕的日子,错过了简单的东西。 C 和 C++ 对于动态大小的结构并没有任何好的工具,所以这可能是你能做的最好的。我没有看到任何明显的问题。我唯一要改变的是使用new char[size]
而不是calloc
。【参考方案2】:
代码有 2 个问题可能导致堆损坏:将字节大小而不是字符数传递给 _tcscpy_s
并计算错误的缓冲区大小。
正确的实现应该大致如下:
void test(TCHAR * p_str)
// Calculate the input string length
size_t len = _tcslen(p_str);
// Calculate the buffer size (1)
size_t bufferSize = offsetof(T_TEST, str[len + 1]);
// Allocate a buffer
vector<char> buffer(bufferSize);
// Map the structure onto the buffer to get a T_TEST*
T_TEST* foo = reinterpret_cast<T_TEST*>(&buffer[0]);
foo->flag = 0;
// This potentially writes beyond the end (2)
//_tcscpy_s(foo->str, size - sizeof(DWORD), p_str);
_tcscpy(foo->str, len + 1, p_str);
// Do IPC with the struct
// When finished, free it
// buffer will be automatically cleaned up
//free(foo);
(1) 结构的大小不一定与其成员大小的总和相同。根据目标平台和/或编译器设置,编译器将对齐各个成员以满足对齐要求。为此,它(可能)在它们之间添加填充。使用offsetof
是让编译器进行计算的便捷方式。
(2) 在原始代码中有两个问题: 对_tcscpy_s
的调用需要目标缓冲区的字符数大小,而不是字节大小。还有一个隐藏的陷阱:表达式foo->str
指示编译器计算偏移量,同时考虑潜在的填充。然而,表达式size - sizeof(DWORD)
不考虑填充,这使得写入超出分配的缓冲区末尾的可能性。
由于您正在处理 MFC 应用程序,因此我假设使用 C++ 是可以的,即使该问题未标记为 C++
。如果您不能或不想使用std::vector
,您可以使用new char[bufferSize]
和delete[] buffer;
分配缓冲区进行清理。
【讨论】:
以上是关于可以通过以下方式将结构转换为不同的大小吗?的主要内容,如果未能解决你的问题,请参考以下文章
C++/QT - 通过 QByteArray 将数据转换为不同格式不同于读入结构