可以通过以下方式将结构转换为不同的大小吗?

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-&gt;str 指示编译器计算偏移量,同时考虑潜在的填充。然而,表达式size - sizeof(DWORD) 不考虑填充,这使得写入超出分配的缓冲区末尾的可能性。

由于您正在处理 MFC 应用程序,因此我假设使用 C++ 是可以的,即使该问题未标记为 C++。如果您不能或不想使用std::vector,您可以使用new char[bufferSize]delete[] buffer; 分配缓冲区进行清理。

【讨论】:

以上是关于可以通过以下方式将结构转换为不同的大小吗?的主要内容,如果未能解决你的问题,请参考以下文章

C++/QT - 通过 QByteArray 将数据转换为不同格式不同于读入结构

如何在 REST API 中使用下划线将驼峰大小写转换为小写?

Python迭代不同大小的数组

有没有办法强制转换结构以通过通道发送

我可以为单个结构以不同的方式多次实现相同的特征吗?

我可以将 char* 缓冲区转换为对象类型吗?