2

所以我有几个结构......

struct myBaseStruct
{
};

struct myDerivedStruct : public myBaseStruct
{
    int a, b, c, d;
    unsigned char* ident;
};

myDerivedStruct* pNewStruct;

...并且我想动态分配足够的空间,以便我可以在某些数据中“memcpy”,包括以零结尾的字符串。基本结构的大小显然是“1”(我假设它不能为零),派生的大小是 20,这似乎是有道理的(5 x 4)。

所以,我有一个大小为 29 的数据缓冲区,前 16 个字节是整数,其余 13 个字节是字符串。

如何为 pNewStruct 分配足够的内存,以便为字符串提供足够的内存?理想情况下,我只想去:

  • 在 pNewStruct 分配 29 个字节;
  • memcpy 从缓冲区到 pNewStruct;

谢谢,

4

9 回答 9

6

您回到 C 或放弃这些想法,并按预期实际使用 C++。

  • 使用构造函数分配内存,使用析构函数删除它。
  • 不要让其他代码写入您的内存空间,创建一个确保分配内存的函数。
  • 使用 std:string 或 std::vector 来保存数据,而不是滚动您自己的容器类。

理想情况下,您应该说:

myDerivedClass* foo = new myDerivedClass(a, b, c, d, ident);

于 2009-12-16T23:00:34.843 回答
4

在当前的 C++ 标准中,myDerivedStruct是非 POD,因为它有一个基类。memcpy将任何东西放入其中的结果是未定义的。

我听说 C++0x 会放宽规则,所以 POD 的类比 C++98 中的多,但我没有研究过。另外,我怀疑很多编译器会以与 POD 不兼容的方式布置您的类。我希望你只会遇到一些没有做空基类优化的问题。但它就在那里。

如果它是 POD,或者如果你愿意在你的实现中冒险,那么你可以使用malloc(sizeof(myStruct)+13)new char[sizeof(myStruct)+13]分配足够的空间,这与 C 中的基本相同。动机大概是为了避免内存和时间开销只需std::string在您的班级中加入一个成员,但代价是必须为手动内存管理编写代码。

于 2009-12-16T23:00:15.953 回答
3

您可以为任何类实例过度分配,但这意味着一定数量的管理开销。唯一有效的方法是使用自定义内存分配调用。在不更改类定义的情况下,您可以这样做。

void* pMem = ::operator new(sizeof(myDerivedStruct) + n);
myDerivedStruct* pObject = new (pMem) myDerivedStruct;

假设您没有operator delete在层次结构中重载,那么delete pObject将是销毁 pObject 并释放分配的内存的正确方法。当然,如果您在多余的内存区域分配任何对象,那么您必须在释放内存之前正确释放它们。

然后,您可以访问n此地址处的原始内存字节:void* p = pObject + 1. 您可以memcpy随心所欲地进出该区域的数据。您可以分配给对象本身,而不需要分配给memcpy它的数据。

您还可以在类本身中提供自定义内存分配器,它需要额外size_t描述要分配的多余内存量,使您能够在单个new表达式中进行分配,但这需要在类设计中产生更多开销。

myDerivedStruct* pObject = new (n) myDerivedStruct;

struct myDerivedStruct
{
    // ...
    void* operator new(std::size_t objsize, std::size_t excess storage);

    // other operator new and delete overrides to make sure that you have no memory leaks
};
于 2009-12-17T07:40:09.553 回答
1

您可以通过执行以下操作动态分配空间:

myDerivedStruct* pNewStruct = reinterpret_cast<myDerivedStruct*>(new char[size]);

然而

你确定你要这么做吗?

另外,请注意,如果您打算使用 ident 作为指向字符串开头的指针,那将是不正确的。您实际上需要 &ident,因为 ident 变量本身就是未使用空间的开头,因此将该空间中的内容解释指针很可能是没有意义的。因此,如果 ident are unsigned charorchar而不是ident 会更有意义unsigned char*

[再次编辑] 我只想强调你正在做的事情真的是一个非常非常糟糕的主意。

于 2009-12-16T22:57:45.077 回答
1

memcpy在这种情况下,混合new似乎是一个糟糕的主意。考虑malloc改用。

于 2009-12-16T22:57:47.603 回答
1

您可以使用malloc分配您想要的任何大小:

myDerivedStruct* pNewStruct = (myDerivedStruct*) malloc(
      sizeof(myDerivedStruct) + sizeof_extra data);

但是,您有一个不同的问题,因为 myDerivedStruct::ident 是一个非常模棱两可的构造。它是一个指向char(数组)的指针,那么结构以char数组开始的地址结束?ident 可以指向任何地方,并且非常模糊谁拥有 ident 指向的数组。在我看来,您希望结构以实际的 char 数组本身结束,并且结构拥有额外的数组。这样的结构通常有一个 size 成员来跟踪它们自己的大小,以便 API 函数可以正确地管理和复制它们,并且按照惯例,额外的数据在结构结束后开始。或者它们以 0 长度数组结尾,char ident[0]尽管这会给某些编译器带来问题。由于许多原因,在这样的结构中没有继承的地方:

struct myStruct 
{
size_t size;    
int a, b, c, d;    
char ident[0];
};
于 2009-12-16T23:10:56.913 回答
0
char* buffer = [some data here];
myDerivedStruct* pNewStruct = new myDerivedStruct();
memcpy(buffer,pNewStruct,4*sizeof(int));
pNewStruct->ident = new char[ strlen(buffer+(4*sizeof int)) ];
strcpy(pNewStruct->ident,buffer+(4*sizeof int));

类似的东西。

于 2009-12-16T22:58:47.437 回答
0

缓冲区大小在编译时是否已知?在这种情况下,静态分配的数组将是一个更简单的解决方案。否则,请参阅上面的 Remus Rusanu 的回答。这就是 win32 api 管理可变大小结构的方式。

struct myDerivedStruct : public myBaseStruct
{
    int a, b, c, d;
    unsigned char ident[BUFFER_SIZE];
};
于 2009-12-16T23:15:51.770 回答
0

首先,我不明白拥有myBaseStruct基地有什么意义。你没有提供任何解释。

其次,您在原始帖子中声明的内容不适用于您描述的数据布局。对于您在 OP 中描述的内容,您需要结构的最后一个成员是数组,而不是指针

struct myDerivedStruct : public myBaseStruct {
    int a, b, c, d;
    unsigned char ident[1];
};

数组大小无关紧要,但它应该大于 0。大小为 0 的数组在 C++ 中是明确非法的。

第三,如果您出于某种原因想要new专门使用,则必须分配char所需大小的对象缓冲区,然后将结果指针转换为您的指针类型

char *raw_buffer = new char[29];
myDerivedStruct* pNewStruct = reinterpret_cast<myDerivedStruct*>(raw_buffer);

之后你可以做你的memcpy,假设大小是正确的。

于 2009-12-16T23:20:44.243 回答