C++函数中动态分配内存——新手问题

12

我正在调查一个内存泄漏问题,从我的观察来看,问题是这样的:

int main(){
    char *cp = 0;
    func(cp);
    //code
    delete[] cp;
}

void func(char *cp){
    cp = new char[100];
}

//code注释处,我原本期望cp指向已分配的内存,但它仍然是一个空指针,意味着我从未删除这段内存。我做错了什么?


1
我猜测 cbuf 应该是 cp 才对? - GManNickG
如何发布真实的代码?除非代码是剪切和粘贴的,否则您可能会添加错误。这只会让事情变得更加困难,最终我们还要解决类似“cbuf -> cp”的剪切和粘贴错误问题。 - Martin York
抱歉,我会记住的。是的,cbuf应该是cp。 - user234885
7个回答

20

您正在将 cp 赋值为分配的内存的值。然而,这是一个在堆栈上的变量:是主函数中 cp 的副本!cp 局限于您当前所在的函数。

您想要的是一个引用:

void func(char *& cp)

这将会将cp作为传递进来的参数进行别名处理。


13
void func(char *cp){
    cp = new char[100];
}
在这个函数中,char *cp是通过“拷贝”传递的指针,这意味着它们指向同一个内存地址,但它们不是同一个指针。当你在函数内部改变指针,使其指向其他地方时,原来传递的指针将继续指向0。

6

参数cp是函数的局部变量——更改它不会影响函数外的任何内容。更好的编写函数的方式是:

char * func(){
    return new char[100];
}

虽然与您的问题直接无关,但您应该使用std::string和std::vector而不是动态分配数组。


1
哦,两个踩。虽然我并不是特别在意,但其他人可能会受益于知道这个答案的问题所在。 - anon
我没有看到任何问题。如果你要指出像你提到的C++特性这样的东西,我认为最好也加上一个关于不硬编码数组大小的部分。此外,如果你要使用这些特定的特性,你可能想通过引用传递一个指针来创建它,或者通过引用传递一个已经创建的指针来避免复制构造函数的惩罚...但是这个答案真的没有什么问题。 - San Jacinto
指针没有复制构造函数。 - anon
当我提到“你提到的C++特性”时,我指的是std::String和std::vector。我回复时可能有些混淆,对此很抱歉。针对你所拥有的,我会建议添加“不要硬编码...”,其余部分则是指“C++特性”。 - San Jacinto

1

虽然引用在提供直观抽象方面非常出色,而C++11的右值引用进一步增强了函数链接(和其他深奥的编码),但可以争论它们是否提供任何安全性(参见为什么引用被认为比指针更安全)。 在需要在ansi c和c ++中维护类似代码库的情况下,最好使用指向指针函数参数来解决上述问题。

#include <iostream>

using namespace std;

void func(char ** cp) {
    *cp = new char[100];
    //do something useful
    (*cp)[0] = 'A';
}

void func(char *& cp) {
    cp = new char[100];
    //do something useful
    cp[0] = 'B';
}

int main(int argc, char** argv) {
    char * cp;
    //pointer to pointer
    func(&cp);
    cout << "Index 0 : " << cp[0] << '\n' << flush;
    delete[] cp; //remember to delete!!
    //pointer to ref
    func(cp);
    cout << "Index 0: " << cp[0] << '\n' << flush;
    delete[] cp;
    return 0;
}

当然,超出实例化函数范围的内存资源处理违反了RAII。

1
你传递的是cbuf,而不是cp

即使他传递了*cp,但在//代码之后它仍将指向0。 - Andre Pena
1
这并不重要,因为你也是按值传递它。 - mob

1
函数只是改变了cp的一个副本。应该使用引用代替。

0
如GMan和Neil所提到的,为了使其工作,您需要将func更改为:
char* func();

void func(char*& p);
这将解决您的直接问题。
然而,存在维护问题。在任一情况下,func都返回指针。对于func的用户不清楚的是,返回的指针必须被删除。因此,除非100%必要,通常避免使用此结构。相反:
1. 帮助用户分配正确数量的内存,然后将其传递给func 2. 使用对象来存储分配的内存。当对象被销毁时,该对象可以删除字符数组。
因此,对于C++代码,我建议:

class CBuf
{
public
    CBuf() 
    {
        iBuf = new char[100];
    }
    ~CBuf
    {
        delete[] iBuf;
    }
    char* func()
    {
        //do stuff;
        return iBuf;
    }
private:
    char* iBuf;
};

int main()
    {
    CBuf cb;
    char* mychar = cb.func();
    //do stuff with character array

    //destructor gets called here because cb goes out of scope
    }

然而,在C编程中,有时候必须要有某种函数来创建数组。因此,在C编程中,你可以用CreateCBufDestroyCBuf函数替换析构函数。这样,你的库的用户就会知道返回的缓冲区需要被销毁。


std:vector很好用,它可以为你处理所有的内存管理。在OP的例子中问题在于所有权非常不清晰。 - doron

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