将malloc / realloc用于包含std向量的类/结构数组

时间:2016-08-06 18:17:22

标签: c++ arrays

我有一个问题是,malloc / realloc内存将包含一个class / struct数组(我试过结构和类,问题仍然存在)包含std向量的成员。我知道我可以通过使用new和std数组容器类来解决这个问题。但是,我想更好地理解为什么当我使用realloc而不是malloc时,以下小代码崩溃(因为我在将较大的代码项目从C转换为C ++的上下文中遇到了这个问题)。似乎我不一定在类/结构中设置向量的初始大小(有些编译器允许有些不...) - 所以什么是类中的向量 - 一个舒适的指针?

谢谢, 启

#include <stdlib.h>
#include <limits.h>
#include <float.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <sys/types.h>
#include <vector>
/* mpic++ -O3 -ffast-math -pedantic vec-alloc.cpp -o vec-alloc */

using namespace std;

class float_vector{
public:
  double x;
  double y;
  double z;
  float_vector() : x(0), y(0), z(0) {};
};


class voxel{
public:
  float_vector   x;
  vector<double> y;

  voxel() : x() {};
};

int main(){

  int i;
  double d =1.111;
  voxel v0, *Comp, *Comp2;

  /* dynamically allocate memory */
  Comp= (voxel*)malloc(10*sizeof(voxel));
  for(i=0;i<10;++i) Comp[i] = v0;
  printf("malloc done\n");

  /* dynamically re-allocate memory */
  Comp2= (voxel*)malloc(sizeof(voxel));  
  printf("realloc done\n");
  for(i=0;i<10;++i){
    Comp2 =(voxel*)realloc(&Comp2[0], (i+1)*sizeof(voxel));
    Comp2[i] = v0;
  }  

  printf("realloc done\n");

  for(i=0;i<10;++i) Comp[i].y.push_back(d);
  for(i=0;i<10;++i) printf("%lf\n",Comp[i].y[0]);

  for(i=0;i<10;++i) Comp2[i].y.push_back(d); // this crashes
  for(i=0;i<10;++i) printf("%lf\n",Comp2[i].y[0]);

  return 1;
} 

3 个答案:

答案 0 :(得分:2)

如果对非POD类使用malloc(),则必须手动调用构造函数(通过放置new)和析构函数。

使用未正确构造的对象会导致未定义的行为,这通常意味着指针崩溃。

显然,为对象释放内存而不对其进行适当的破坏会导致UB。

您的代码必须如下所示:

MyClass *arr = (MyClass *) malloc(10 * sizeof (MyClass));

for (int i = 0; i < 10; i++)
    new (arr + i) MyClass; // This line calls constructors

// Do something with the array here

for (int i = 0; i < 10; i++)
    arr[i].~MyClass(); // This line calls destructors.

free(arr);

此要求还意味着您不能将realloc()与非POD类型一起使用,因为它不会为旧数组调用析构函数,也不会为新数组调用构造函数。

手动重新分配代码可能如下所示:

MyClass *new_ptr = (MyClass *) malloc(new_size * sizeof (MyClass));

for (int i = 0; i < new_size; i++)
    new (new_ptr + i) MyClass((MyClass &&) old_ptr[i]);

for (int i = new_size; i < old_size; i++)
    new (new_ptr + i) MyClass;

for (int i = 0; i < old_size; i++)
    old_ptr[i].~MyClass();

free(old_ptr);

请记住,上面的代码并不是真正的异常安全。如果构造函数抛出异常并捕获它,那么您需要确保正确地破坏构造的对象。 谢谢@SteveJessop。

现在,当您了解为什么在C ++中通常应该避免使用malloc() / free()时,我希望您能够更加安全new / {{ 1}},为你完成所有构造和破坏。

答案 1 :(得分:1)

这可能与realloc无关。当您在开始时执行此操作时,您的代码已经具有未定义的行为:

for(i=0;i<10;++i) Comp[i] = v0;

Comp[0]从未被初始化(因为malloc返回未初始化的内存 - 它无法知道您打算使用它的类型,因此即使它想要也不可能初始化它)。然后您的代码尝试分配给它。对于vector等复杂类型,不允许这样做。

为什么不允许?在vector的情况下,因为当您分配给已经保存数据的向量时,它需要释放旧数据。如果没有什么可以自由的,它就不会有任何自由。但是未初始化的内存可能根本没有任何值,所以vector似乎有些东西应该被释放,实际上根本不是一个可释放的指针,更不用说vector由于这项任务应该解放。如果没有初始化,就会违反“this指针数据成员始终是空指针,或者是某个内存的地址,这是向量的责任”的某些类不变,因此vector代码不起作用。

假设您的代码以某种方式超越了这一点,您仍然无法realloc包含vector的内存。从标准的角度来看,这是因为vector<double>不是POD类型,因此它的逐字节副本(包括由realloc完成的)会导致未定义的行为。 / p>

从特定实现的角度来看,我们可能会问自己,实现者可能编写哪些代码,在逐字节复制向量的情况下会出错。一个假设的答案是,在某些情况下,vector可以包含指向自己身体的指针(作为所谓的小矢量优化的一部分)[编辑:实际上,我认为在内部不可能进行小矢量优化出于其他原因的标准,但我的一般观点是,因为向量不是POD,所以实现者可以自由地使用他们的创造力]。如果向量被重定位,则该指针不再指向向量自身的主体,因此不满足类不变量,并且代码不再有效。为了让实现者能够自由地编写这样的代码,作为类用户的自由受到限制,并且不允许通过逐字节复制来重定位向量(或通常任何非POD类型)。

答案 2 :(得分:0)

var time = toDays(21);
function toDays(years)
{
    var time;
    time = 365*years;
    return time;
}       
document.write("My age is " + time);

/* dynamically allocate memory */ Comp= (voxel*)malloc(10*sizeof(voxel)); 现在是指向未初始化内存的指针。

Comp

这会尝试调用for(i=0;i<10;++i) Comp[i] = v0; ,但Comp[i].operator=(v0)不是有效的初始化对象。在一个简单的测试/调试案例中,我们可能会很幸运,但在实践中我们会得到垃圾,而矢量会尝试释放/使用无效指针。

这并不仅仅意味着你需要Comp[i]内存,你不能假设初始化对象期望找到什么值。

calloc()

Comp2现在是指向单个体素的指针,并且没有“realloc”。

/* dynamically re-allocate memory */
Comp2= (voxel*)malloc(sizeof(voxel));  
printf("realloc done\n");

这只是古怪。它始于Comp2指向单个体素。然后,由于某种原因,您获取第一个元素(for(i=0;i<10;++i){ Comp2 =(voxel*)realloc(&Comp2[0], (i+1)*sizeof(voxel)); Comp2[i] = v0; } )的地址而不是仅使用第一个元素(&Comp2[0])的地址,并将其重新分配到相同的大小。然后,将v0复制到最后一个位置的未初始化内存中:

Comp2

简短:您无法将Comp2 = [...uninit...] for (i = 0 realloc(i + 1 == 1) Comp2 = [...uninit...] ^-- v0 i++ realloc(i+1 == 2) Comp2 = [.....v0.....][...uninit...] ^--v0 malloccalloc与非pod对象一起使用。你可能会偶尔侥幸逃脱,但你基本上是指着一只装满霰弹枪的脚。

  

似乎我不一定在类/ struct

中设置向量的初始大小

您可以轻松设置类中向量的默认大小,需要C ++ 11(gnu / clang编译器为realloc或更高版本,VS2013或更高版本)

-std=c++11

http://ideone.com/KA9fWB