使用 new 而不是 malloc 分配内存

Posted

技术标签:

【中文标题】使用 new 而不是 malloc 分配内存【英文标题】:Allocating mem using new instead of malloc 【发布时间】:2019-03-20 18:07:18 【问题描述】:

我正在尝试使用new 而不是malloc 来计算此代码的精确等效项。指向浮点数组[4]的指针:

float (**f)[4] = (float(**)[4])malloc(2 * sizeof(float(*)[4]));
for (int p = 0; p < 2; p++) 
    f[p] = (float(*)[4])malloc(3 * sizeof(float[4]));

我尝试了一堆组合,但找不到正确的语法。

float (**f)[4] = new ...

【问题讨论】:

首先引入 typedef 以更清楚地了解发生了什么。写的代码很恶心。 改用std::vector&lt;std::vector&lt;std::array&lt;float, 4&gt;&gt;&gt; 已经使用了std::vector。或者,如果您必须动态分配,至少使用智能指针。对于初学者,请查找 std::make_unique 见***.com/questions/7719905/… 我对所问问题的答案很感兴趣。我知道人们应该并且永远不会使用此代码。 【参考方案1】:

简单的旧声明(不要这样做):

try

    float *** f = new float ** [2];
    for (size_t i = 0; i < 2; i++)
    
       f[i] = new float * [3];
       for (size_t j = 0; j < 3; j++)
           f[i][j] = new float[4];
    
 catch(const std::exception & e)  /* This is a pain to deal with to avoid any leak */ 

// Now you can access via f[0][1][2]
// delete[] in reverse order.

更好一点(避免使用多次分配):

try 

    typedef float BigArray[3][4];
    BigArray * f = new BigArray[2];
 catch(const std::exception & e)  /* here it's simple */ 
// delete with delete[] f

有点麻烦,不过你不用再关心内存泄漏了:

std::vector<std::vector<std::vector<float>>> f(2, std::vector<std::vector<float>>(3, std::vector<float>(4)));
// Use with f[0][1][2]

另外,正如大多数人所说,您应该将数组存储在指向 float 的指针中,而不是指向 float 指针的指针,因为您不需要在 3 个步骤中取消引用,因此效率会更高访问一个元素,即:

int vectorElems = 4, columns = 3, rows = 2;
int rowStride = vectorElems * columns;  
float * f = new float[rows*columns*vectorElems];
// Access with stride:
f[0 * rowStride + 1 * vectorElems + 2] = 3.14f;

// Delete with delete[] f

还有模板矩阵类(例如在 opencv 中)通过提供重载 () 运算符来正确执行此操作,因此您可以访问像 f(0, 1, 2) 这样的对象

【讨论】:

非常感谢。但是可以精确地做到float (**f)[4] = new ...吗?语法让我很感兴趣。 不,恐怕不可能像你写的那样这样做。左边的部分声明了一个大小为 4 的数组,该数组指向 堆栈上的浮点指针。就像你写char buf[3] 时一样,你不应该给它分配一个堆分配的指针(buf = new char[3] 是未定义的行为) 越接近float (**f)[4] = new float *[3], new float *[3], new float *[3], new float *[3]。然而,您仍然需要迭代数组来分配数组中的内部指针。 很明显,但是如果尺寸是固定的,你可以float f[2][3][4] 啊,我明白我误解了 float (**f)[4] 的实际作用。我以为它是指向 4 个浮点数组的指针,但它实际上是指向浮点指针的 4 个指针数组。我这样说对吗?【参考方案2】:

您可以创建一个助手类,它可以通过转换自动神奇地new 为您提供正确的东西。

template <unsigned N = 0> struct NewAny;

template <unsigned N> struct NewAny 
    template <typename T>
    operator T * () const  return new T[N]; 
;

template <> struct NewAny<0> 
    template <typename T>
    operator T * () const  return new T; 
;

int main () 
    float (**x)[4] = NewAny<2>();
    delete[] x;
    x = NewAny<>();
    delete x;

在你的例子中:

float (**f)[4] = NewAny<2>();
for (int p = 0; p < 2; p++) 
    f[p] = NewAny<3>();

现代 C++ 教导通过使用容器和智能指针来避免手动管理动态分配的内存的容易出错的性质。您可以执行以下操作来创建D-Dimension vector

template <typename T, unsigned D> struct Vectorate;

template <unsigned N, typename T, unsigned D>
struct Vectorate<T[N], D> 
    typedef
    typename Vectorate<std::array<T, N>, D>::type type;
;

template <typename T, unsigned D>
struct Vectorate 
    typedef
    typename Vectorate<std::vector<T>, D-1>::type type;
;

template <typename T>
struct Vectorate<T, 0> 
    typedef T type;
;

在你的例子中:

Vectorate<float[4], 2>::type f;
f.resize(2);
for(auto &ff : f) ff.resize(3);

【讨论】:

除了语法糖,第一个代码应该避免,因为它允许任何无效的声明被接受(只需在 NewAny() 前面写任何你想要的,编译器会很乐意为你创建)。例如,在您的情况下,编译器将分配一个 2x array of 4 float ** 的数组,如果我正确理解最初的问题,这并不是您想要的。 最后一个例子更简洁,但它对于多维数组大小不是动态的(无论如何这可能是好的) @xryl669 NewAnynew 一样安全。如果左轴是T *,右轴是new T 只要去掉float (*f)[4] = NewAny&lt;2&gt;() 这样的一颗星,代码仍然可以编译。不像float * t = new float*[2] 编译器会阻塞。这是一个需要发现的微妙错误。 我同意。这不是批评,完全没问题。我的评论只是为了让外部观察者清楚,使用基于模板的转换运算符会删除通常使用普通旧新完成的赋值两侧的类似类型的 双重检查。这是一个巧妙而强大的技巧,但也应该说缺点。【参考方案3】:

我不确定... 但是试试这个:

float **f[4];
    for(int i = 0; i < 4; i++)
        f[i] = (float**)malloc(sizeof(float**));
    
    printf("%ld", sizeof(f));

【讨论】:

目标是使用new而不是malloc 发布答案时,您应该确定它。如果不是,您应该考虑在评论中留下您认为可能是他们想要的代码示例的链接。 浮点 *f[4]; for(int i = 0; i ; printf("%ld", f);

以上是关于使用 new 而不是 malloc 分配内存的主要内容,如果未能解决你的问题,请参考以下文章

内存动态分配之realloc(),malloc(),calloc()与new运算符

new 与malloc的区别

如何用new分配一块内存?

new和malloc的区别

动态内存分配malloc与new的区别

C++数组在内存中的分配