指针错误导致的分段错误(简单情况)

3

我有一个函数,该函数返回指向双精度数组的指针:

double * centerOfMass(System &system) {
        long unsigned int size = system.atoms.size();

        double x_mass_sum=0.0; double y_mass_sum=0.0; double z_mass_sum=0.0; double mass_sum=0.0;

        for (int i=0; i<=size; i++) {
                double atom_mass = system.atoms[i].m;
                mass_sum += atom_mass;

                x_mass_sum += system.atoms[i].pos["x"]*atom_mass;
                y_mass_sum += system.atoms[i].pos["y"]*atom_mass;
                z_mass_sum += system.atoms[i].pos["z"]*atom_mass;
        }

        double comx = x_mass_sum/mass_sum;
        double comy = y_mass_sum/mass_sum;
        double comz = z_mass_sum/mass_sum;

        double* output = new double[3];  // <-------- here is output
        output[0] = comx*1e10; // convert all to A for writing xyz
        output[1] = comy*1e10;
        output[2] = comz*1e10;
        return output;
}

当我尝试通过将数组保存到变量中(在另一个函数中)来访问输出时,程序运行时会出现分段错误(但编译正常):

void writeXYZ(System &system, string filename, int step) {

        ofstream myfile;
        myfile.open (filename, ios_base::app);
        long unsigned int size = system.atoms.size();
        myfile << to_string(size) + "\nStep count: " + to_string(step) + "\n";

        for (int i = 0; i < size; i++) {
                myfile << system.atoms[i].name;
                myfile <<  "   ";
                myfile << system.atoms[i].pos["x"]*1e10;
                myfile <<  "   ";
                myfile << system.atoms[i].pos["y"]*1e10;
                myfile <<  "   ";
                myfile << system.atoms[i].pos["z"]*1e10;
                myfile << "\n";
        }

        // get center of mass
        double* comfinal = new double[3]; // goes fine
        comfinal = centerOfMass(system); // does NOT go fine..
        myfile << "COM   " << to_string(comfinal[0]) << "   " << to_string(comfinal[1]) << "   " << to_string(comfinal[2]) << "\n";

        myfile.close();
}

运行程序时,功能正常,直到尝试调用centerOfMass函数。我已经检查了大多数可能的解决方案,我认为我缺乏对C ++中指针及其作用域的理解。我精通PHP,因此处理内存显式地是有问题的。谢谢你。

1
for (int i=0; i<=size; i++) { 看起来很奇怪。难道不应该是 for (int i=0; i<size; i++) { 吗? system.atoms 的类型是什么? - songyuanyao
@songyuanyao 拍手,谢谢。我错过了那个。感谢你的关注。如果您发布一个答案,我会接受 :3 - khaverim
@khaverim 好消息——你并不一定误解了C++中指针及其作用域。你只是犯了一个简单的错误,这没关系。 - synchronizer
你的程序中有两个内存泄漏。你在这里初始化了 comfinaldouble* comfinal = new double[3];,然后在这一行中重新分配了指针:comfinal = centerOfMass(system); 当你执行第二次赋值时,你泄漏了你在第一次调用 new 时分配的内存。最后,在程序结束时,你没有调用 comfinal 上的 delete,因此泄漏了它在程序结束时所持有的内存。 - Alex Zywicki
@khaverim,不,它实际上在许多情况下可能会使事情变得更糟。您需要将这两行代码更改为 double* comfinal = centerOfMass(system);,然后在 writeXYZ 的末尾添加 delete[] comfinal - Alex Zywicki
显示剩余2条评论
2个回答

4

我不确定 system.atoms 的类型。如果它是像 std::vector 这样的STL容器,那么函数 centerOfMass 内部的 for 循环条件是错误的。

long unsigned int size = system.atoms.size();
for (int i=0; i<=size; i++) {

应该是

long unsigned int size = system.atoms.size();
for (int i=0; i<size; i++) {

PS1: 您可以使用 基于范围的for循环(自C++11起) 来避免这种问题。

PS2: 您没有删除动态分配的数组。考虑使用 std::vectorstd::array 或者 std::unique_ptr,它们旨在帮助您避免这类问题。


1
除了songyuanyao指出的问题外,在writeXYZ()中使用该函数会导致内存泄漏。
要看到这一点,请注意centerOfMass()执行以下操作(已删除冗余细节)。
 double* output = new double[3];  // <-------- here is output
 // assign values to output
 return output;

writeXYZ() 做了什么(请注意,我已更改注释以反映实际发生的情况,与您对正在发生的事情的评论不同)

double* comfinal = new double[3]; //  allocate three doubles
comfinal = centerOfMass(system); //   lose reference to them

// output to myfile

如果writeXYZ()被多次调用,那么每次都会泄漏三个double,即使某处随后执行了delete [] comfinal。如果该函数被多次调用(例如在循环中),最终泄漏的内存量可能超过可用内存,导致后续分配失败。

解决此问题的一种方法是更改writeXYZ()相关部分为:

double* comfinal = centerOfMass(system);

// output to myfile

delete [] comfinal;    // eventually

介绍上面的std::unique_ptr可以缓解症状,但这更多是代码中的一个偶然事件,而不是良好逻辑(仅分配内存后立即丢弃而未使用它很少是好技术)。

实际上,在实践中,最好使用标准容器(std::vector等),并尽可能避免使用运算符new。但它们仍然要求您保持在范围内。


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