如何在C++函数中传递指针并动态分配内存

8

我正在尝试声明一个指针并将该指针传递给一个函数,其中会分配内存。这是一个最小的示例:

#include <string>
#include <iostream>

using namespace std;

void alloc_mem(int &size, double *x);

int main()
{

        double *X;
        int imax;

        alloc_mem(imax, X);

        cout << "imax = " << imax << endl;
        for (int i = 0; i < imax; i++) {
                cout << "X = " << X[i] << endl;
        }

        delete[]X;
        return 0;

}

void alloc_mem(int &size, double *x)
{

        size = 10;
        x = new double[size];
        for (int i = 0; i < size; i++) {
                x[i] = (double)i;
        }

}

这段代码可以编译通过,但是当我尝试打印X的值时,会出现分段错误。我知道我没有正确地将变量传递到函数中,但我不确定如何做到这一点。我相信我正在操作X的副本。

此外,这段代码是为了复制我在一个更大的代码中遇到的问题而编写的。


4
请试用这个原型:void alloc_mem(int &size, double*& x);,否则 x 只会在函数内被修改。 - Jarod42
1
我会使用 std::vector<double> - Ivan
第一条评论中提到的原型是有效的。我发誓昨晚2点钟已经尝试过了。谢谢! - James
除了好奇心,你为什么想要实现上述功能?这种方法非常不安全。我会听取Ivan的建议,使用std::vector<>代替。 - Adam Wulkiewicz
更新:你们都是对的,std::vector<>更好/更安全。我已经将整个代码转换为使用向量而不是动态分配的数组。 - James
显示剩余2条评论
3个回答

19

参数 double *x 是函数alloc_mem的本地变量。当函数结束执行时,该变量将被销毁。在主函数中的原始变量X不知道对这个参数所做的任何操作,因为它是按值传递的,即在函数中使用了它的副本。

要么通过指针或引用方式传递指针。例如

void alloc_mem(int &size, double **x);

void alloc_mem(int &size, double * &x);

void alloc_mem(int &size, double **x) 
{
   size = 10;

   *x = new double [size];

   for ( int i = 0; i < size; i++ ) ( *x )[i] = i;
}

void alloc_mem(int &size, double * &x) 
{
   size = 10;

   x = new double [size];

   for ( int i = 0; i < size; i++ ) x[i] = i;
}

就我个人而言,我会这样定义该函数:

double * alloc_mem( int &size ) 
{
   size = 10;

   x = new double [size];

   for ( int i = 0; i < size; i++ ) x[i] = i;

   return x;
}

如果在调用函数之前知道了大小,那么它甚至可以写得更简单。

double * alloc_mem( int size ) 
{
   x = new double [size];

   for ( int i = 0; i < size; i++ ) x[i] = i;

   return x;
}

需要考虑到那个循环

   for ( int i = 0; i < size; i++ ) x[i] = i;

可以替换标准算法std::iota,例如:

std::iota( x, x + size, 0.0 );

4

定义分配函数的标准C++机制是operator new

这就是为什么标准将其称为分配函数的原因。

请注意,operator newnew表达式不同。

new表达式使用相关的分配函数(operator new函数)来分配内存,并使用相关的构造函数进行初始化。

然而,在您的情况下,您只是使用(您命名的)分配函数来分配和初始化一个动态数组。在标准C++语言设计中,分配和初始化是完全分开的责任,有很好的理由,最好遵循这个惯例。使用std::vector来创建数组,如果您真的真的需要自定义分配(非常怀疑您需要),那么可以为该std::vector使用自定义分配器


具体示例。

替换您当前的代码

int main () { 

// Declaring variables
double* X;
int imax;

// Calling function
alloc_mem(imax,X);

// Printing
cout << "imax = " << imax << endl;
for (int i=0; i<imax; i++) {
    cout << "X = " << X[i] << endl;
}

使用

#include <vector>

int main() {
    int const imax = whatever();
    std::vector<double> X( imax );

    cout << "imax = " << imax << endl;
    for (int i=0; i<imax; i++) {
       X[i] = i;  // If you really want these values in the vector.
       cout << "X = " << X[i] << endl;
    }
}

我使用迭代式的方法来撰写答案。由于视力不佳、笔记本键盘极差,更别提不可靠且有时缓慢的连接,这是我唯一实用的方法。:( 但还是非常感谢! - Cheers and hth. - Alf
“我在撰写答案时采用迭代方法。” 我也是这样做的,通常我会先写一个评论,然后发现它可以成为一个合适的答案,将其复制到答案编辑器中并发布第一篇。之后我继续编辑,通常不希望有人在此期间更正我的帖子 ;) ... - πάντα ῥεῖ

3

当你有一个输出参数时,可以通过引用或指针进行传递。

对于你的size参数,由于它是一个输出参数,因此你通过引用进行了传递。

另一个参数是一个double*,因此你可以添加一个引用:

void alloc_mem(int & size, double* & x)  // (1)

或者添加另一个指针(即另一级间接性):
void alloc_mem(int & size, double** x)   // (2)

为了保持一致性,既然你在size使用了引用(&)技术,我建议你也在x中使用(如(1)所示)。
另请注意,在C++中,您可能只想传递一个std::vector,它知道自己的大小,并且由于其析构函数而进行自动清理。
void alloc_mem(std::vector<double> & x)

注意,您也可以将向量作为返回值返回(简化您的代码):
std::vector<double> alloc_mem()

代码片段:
// Note: the caller must free the memory using delete[].
void alloc_mem(int& size, double*& x) {
    size = 10;
    x = new double[size];
    for (int i = 0; i < size; i++) {
        x[i] = i;
    }
}

// Note: automatic vector cleanup. No need of manual delete.
std::vector<double> alloc_mem() {
    const int size = 10;
    std::vector<double> x(size); // size is 0
    for (int i = 0; i < size; i++) {
        x[i] = i;
    }
    return x;    
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接