【问题标题】:segmentation fault while freeing pointer释放指针时出现分段错误
【发布时间】:2020-08-08 01:41:35
【问题描述】:
class matrix
{
public:
    int nrow;
    int ncol;
    double **m;

    matrix(int r, int c)
    {
        nrow = r; ncol = c;

        m = (double**)malloc(nrow*sizeof(double*));
        for(int i=0; i<nrow; i++)
            m[i] = (double*)malloc(ncol*sizeof(double));

        for(int i=0; i<nrow; i++)
            for(int j=0; j<ncol; j++)
                m[i][j] = 0;
    }

    ~matrix()
    {
        for(int i=0; i<nrow; i++)
        {
            printf("destructor loop: %d\n", i); fflush(stdout);
            free(m[i]);
        }
        free(m);
    }

    matrix(const matrix& that)
    {
        this->nrow = that.nrow;
        this->ncol = that.ncol;

        this->m = (double**)malloc(nrow*sizeof(double*));   // dynamic mem allocation
        for(int i=0; i<this->nrow; i++)
            m[i] = (double*)malloc(ncol*sizeof(double));

        for(int i=0; i<that.nrow; i++)
            for(int j=0; j<that.ncol; j++)
                this->m[i][j] = that.m[i][j]; 
    }

    void operator=(const matrix &that)
    {
        this->nrow = that.nrow;
        this->ncol = that.ncol;

        for(int i=0; i<nrow; i++)  // clear current m
            free(m[i]);
        free(m);

        this->m = (double**)malloc(nrow*sizeof(double*));   // dynamic mem allocation
        for(int i=0; i<this->nrow; i++)
            m[i] = (double*)malloc(ncol*sizeof(double));

        for(int i=0; i<that.nrow; i++)
            for(int j=0; j<that.ncol; j++)
                this->m[i][j] = that.m[i][j]; 
    }

    void show()
    {
        printf("  Marix:\n");
        for(int i=0; i<nrow; i++)
        {
            printf("  ");
            for(int j=0; j<ncol; j++)
            {
                if(m[i][j] >= 0) printf(" ");
                printf("%.6lf ", m[i][j]);
            }
            printf("\n");
        }
        printf("\n");
    }

    matrix cofactor(int r, int c) // returns cofactor of a[r][c]
    {
        printf("cofactor: size:(%d, %d) element:(%d, %d)\n", nrow, ncol, r, c); fflush(stdout);

        matrix ans(nrow-1, ncol-1);

        int x = 0, y = 0;
        for(int i=0; i<nrow; i++)
        {
            if(i == r) continue;
            for(int j=0; j<ncol; j++)
            {
                if(j == c) continue;
                ans.m[x][y] = m[i][j];
                y++;
            }
            x++;
        }
        return ans;
    }

    double det()
    {
        if(nrow != ncol)
        {
            printf("non-square matrix: (%d, %d)\n\n", nrow, ncol);
            exit(1);
        }

        if(nrow == 1) return m[0][0]; 
        if(nrow == 2) 
        {
            double ans = m[0][0]*m[1][1] - m[1][0]*m[0][1];
            return ans;
        }

        double ans = 0;
        int sign = 1;
        for(int i=0; i<nrow; i++)
        {
            printf("det[size:%d, %d] loop row: %d\n", nrow, ncol, i); fflush(stdout);
            matrix a(2, 2); 
            a = cofactor(i, 0);
            ans += (sign*a.det());
            sign = sign*(-1);
        }
        return ans;
    }
};

int main()
{
    matrix c(3, 3);
    c.m[0][0] = 2; c.m[0][1] = -3; c.m[0][2] =  1;
    c.m[1][0] = 2; c.m[1][1] =  0; c.m[1][2] = -3;
    c.m[2][0] = 1; c.m[2][1] =  4; c.m[2][2] =  5;

    c.show();
    printf("determinant: %lf\n", c.det());

    return 0;
}

在对 3x3 矩阵调用 det 函数时会发生分段错误。 我没有使用复杂调试器的经验,但使用 printf 语句看起来运行时错误发生在析构函数中的循环迭代期间。我也尝试过使用向量>而不是双**。我认为在这种情况下它不需要析构函数。然后它给了我一个“双重免费错误”。我了解它何时发生,但在我的代码中找不到错误。我也尝试过计算内联辅因子而不是调用函数,但它没有帮助。

【问题讨论】:

  • 我怀疑void operator=(const matrix &amp;that) 重载正在释放析构函数也释放的东西。
  • 使用-fsanitize=address 编译程序,当您出现内存错误时获得更多信息。
  • 在你的赋值运算符中,你设置this-&gt;nrow = that.nrow 在你释放你的旧数组。这意味着在 that.nrow != this.nrow 的任何情况下,您都在错误的行数上调用 free()
  • @JeremyFriesner 是的,对不起,我解决了这个问题。不幸的是,它没有解决错误。
  • @narutouzumaki798 尝试在您的每个malloc() 呼叫之后放置一个printf("@%i I just allocated %p\n", __LINE__, theReturnValueFromMalloc);,并在您的每个free() 呼叫之前放置一个printf("@%i I'm about to free(%p)\n", __LINE__, theValueYouAreAboutToPassToFree); 呼叫。然后运行您的程序,并将其 stdout 重定向到一个文件,然后进入该文件,对于每个“我只是 malloced(X)”行,找到下一个匹配的“我即将释放(X)”行和删除它们。一旦你完成了尽可能多的工作,任何“我即将释放”行仍然是你的错误。

标签: c++ segmentation-fault c++17 double-free


【解决方案1】:
    int x = 0, y = 0;
    for(int i=0; i<nrow; i++)
    {
        if(i == r) continue;
        for(int j=0; j<ncol; j++)
        {
            if(j == c) continue;
            ans.m[x][y] = m[i][j];
            y++;
        }
        x++;
    }

增加y 的逻辑在这里被破坏了。做一个简单的心理实验。如果原始矩阵是 3x3,并且它们在此处被缩减为 2x2 矩阵,即您的 ans,那么您希望 ans 中的每个值都在此处初始化,并且

            ans.m[x][y] = m[i][j];
            y++;

将被执行四次。由于 y 最初设置为 0并且永远不会重置为 0,因此在此分配中使用的 y 的值将介于 0 到 3 之间,因此,在在某些时候,这将尝试为 ans.m[x][3] 分配一些东西,当然,这不存在,并且随之而来的是山寨。

看来这里明显的意图是重置

  y=0;

作为外部for 循环中的第一项业务,而不是一开始就一次。

【讨论】:

    【解决方案2】:

    问题出在辅因子函数中。您已经在循环之外声明了int x = 0, y = 0。在 x (辅因子矩阵的行,ans)的情况下,它很好。但是我们希望每次从新行开始时 y 都为 0,即 y 应该与 j 同步,当 j==c 时跳过。

    发生的情况是您跳过第 0 行,从 i==r 开始,然后在下一行 (i=1),您遍历列,跳过 col 0,从 j==c 开始,然后遍历 col 1,col 2,所以执行的代码是 ans.m[0][0] = m[1][1], y++, ans.m[0][1] = m.[1][2],y++,那么我们就完成了列。

    我们增加 x(x=1 现在),我们转到下一行,第 2 行(i=2),但 y=2,而不是重置。所以下一行执行这段代码: ans.m[1][2] = m[2][1](跳过第 0 列), 这超出了ans 的范围,因为ans 有ncols-1 = 2 列。

    当ans 的析构函数在辅因子函数结束时被调用时,这会导致错误。我修复了辅因子代码,这在为 3*3 矩阵调用 det 时不会出现错误。

    matrix cofactor(int r, int c) // returns cofactor of a[r][c]
    {
        printf("cofactor: size:(%d, %d) element:(%d, %d)\n", nrow, ncol, r, c); fflush(stdout);
    
        matrix ans(nrow-1, ncol-1);
    
        int x = 0;
        for(int i=0; i<nrow; i++)
        {
            if(i == r) continue;
            int y = 0;
            for(int j=0; j<ncol; j++)
            {
                if(j == c) continue;
                ans.m[x][y] = m[i][j];
                y++;
            }
            x++;
        }
        return ans;
    }
    

    要调试,我会推荐 gdb。您可以使用 onlinegdb.com 将您的代码粘贴到那里,然后单击调试。然后通过 break 设置断点,然后运行,n 代表下一条指令,s 代表进入函数。您可以搜索以了解更多信息。

    【讨论】:

    • 你们都得到了我的支持。你能把你的第二段写成项目符号或其他更容易阅读的东西吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-18
    • 2023-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多