使用C中的结构正确实现GCD

时间:2012-12-25 21:31:44

标签: c garbage-collection greatest-common-divisor

我在c中实现了一个非常简单的计算机代数系统。我的程序在一段时间后崩溃了,我遇到了一些问题。

这个想法有一个表达式 3 ^(21 + 8 ^(3 ^ 100))+ 4 并看到它等于2 mod 7.我已经在java中编写了程序,并试图将其移植到c。

我就是这样做的: 我有一个名为expr的结构。它可以是二进制表达式或原子int

struct expr
{
    struct expr* a;
    struct expr* b;
    char op;
    int value;
};
typedef struct expr expr;

expr* new_expr(int i)
{
    expr* e = malloc(sizeof(expr));
    e->op='i';
    e->value = i;
    return e;
}

expr* new_expr2(expr* a, expr* b, char op)
{
    expr* e = malloc(sizeof(expr));
    e->a=a;
    e->b=b;
    e->op=op;
    e->value = 0;
    return e;
}

void free_expr(expr* e)
{
    free_expr(e->a);
    free_expr(e->b);
    free(e);
}
/* ... other methods ... */

我怀疑(其中一个)问题是我没有在gcd函数中释放内存。这就是我编写gcd函数的方法。

expr* gcd(expr* a, expr* b)
{
    expr* t = b;
    while(!equals(b, zero))
    {
        t=b;
        b=mod(a, b);
        a=t;
    }
    return a;
}

这在java中运行得很好但是我不确定它是否在c中工作,因为它没有自动垃圾收集。当函数递归时,我不确定如何构造它。所以我想我的问题是,我应该在哪里放入free_expr函数? mod(a,b)分配一个新的expr结构,因此它最终将创建许多永远不会被释放的expr。 我怀疑这可能是它崩溃的原因。什么是正确的结构方式?或者我这样做是错的吗?

由于代码的可维护性,我宁愿在struct expr中进行计算而不是在int上进行计算。

感谢您的帮助。

[编辑] 这是我的mod函数

expr* mod(expr* number, expr* modulo)
{
    expr* result;
    if(number->op=='i')
    {
        if(modulo->op=='i')
        {
            int i = (number->value)%(modulo->value);
            while(i<0)
            {
                i=i+(modulo->value);
            }
            return new_expr(i);
        }
    }
    switch(number->op)
    {
    case '+':
        result = new_expr(mod(number->a,modulo)->value+mod(number->b,modulo)->value);
        break;
    case '-':
        result = new_expr(mod(number->a,modulo)->value-mod(number->b,modulo)->value);
        break;
    case '*':
        result = new_expr(mod(number->a,modulo)->value*mod(number->b,modulo)->value);
        break;
    case '/':
        result = new_expr(mod(number->a,modulo)->value/mod(number->b,modulo)->value);
        break;
    case '^':
        result = modexpEuler(number->a,number->b, modulo);
        break;
    }
    (result->value)%=(modulo->value);
    return result;
}


expr* modexpEuler(expr* a, expr* b, expr* n)
{
if(!(a->op=='i')||!(n->op=='i'))
{
    printf("wrong input ");
    exit(0);
}

if(equals(b, one))
{
    return mod(a,n);
}
if(equals(b, zero))
{
    if(b->op=='^'){
        printf("adf %d\n",b->a->value);
        printf("asdf %d\n", b->b->value);
    }else{

        printf("asdf %c\n", b->op);
        printf("asdf %d\n", b->value);
        printf("asdf %d\n", a->value);
        printf("asdf %d\n", a->b->value);
    }
    return copy_expr(zero);
}
if(equals(mod(a, n), zero))
{
    return copy_expr(zero);
}
if(b->op == 'i')
{
    return expmod(a, b, n);
}
if(equals(gcd(a,n), one))
{
    expr* tempA = mod(a, n);
    expr* tempB = mod(b, phi(n));
    printf("trying to use euler\n");
    printf("%d\n",a->value);
    printf("%d\n",b->value);
    return modexpEuler(tempA, tempB, n);
}
else
{
    printf("gcd not 1 ");
    exit(0);
}

}

phi(n)计算eulers totient function。

[编辑2] 这是我的eqauls

int equals(expr* a, expr* b)
{
    if((a->op)!=(b->op))
    {
        return 0;
    }
    if((a->op)=='i')
    {
        return (a->value)==(b->value);
    }else{
        return equals(a->a,b->a)&&equals(a->b,b->b);
    }
}

3 个答案:

答案 0 :(得分:2)

gcd中,您有三个expr*,即a, bt

expr* gcd(expr* a, expr* b)
{
    expr* t = b;
    while(!equals(b, zero))
    {
        t=b;
        b=mod(a, b);

现在t指向b之前指出的内容,b指向模数,a仍然指向循环体进入此迭代时指向的内容

        a=t;

现在已被覆盖并无法访问。所以正确的时间来释放它就在那个任务之前。在此之前你无法释放它,因为mod(a, b)中仍然需要它。之后你就无法释放它,因为你不再拥有它了。

    }
    return a;
}

请注意,传入的指向结构因此在gcd中被销毁,因此如果您在此之后需要它们,则应该制作(深层)副本。

段错误的直接原因可能是

void free_expr(expr* e)
{
    free_expr(e->a);
    free_expr(e->b);
    free(e);
}

那个绝对需要NULL检查。按原样,您尝试free_expr(0->a),这是未定义的行为,几乎肯定会崩溃。

答案 1 :(得分:1)

假设mod正在分配一个新的expr然后是,那就是泄漏。我可能会传入第三个expr来获取结果,而不是在mod中分配一个。这允许您的调用代码决定在内存方面做什么。

顺便检查一下valgrind可能会有用。它可以用来查找内存问题,并且很容易用于基本的东西。

答案 2 :(得分:1)

typedef struct expr
{
    struct expr* a;
    struct expr* b;
    char op;
    int value;
} expr;

expr* new_expr(int i)
{
    expr* e = malloc(sizeof(expr));
    e->a=NULL                          <----
    e->b=NULL                          <----
    e->op='i';
    e->value = i;
    return e;
}

expr* new_expr2(expr* a, expr* b, char op)
{
    expr* e = malloc(sizeof(expr));
    e->a=a;
    e->b=b;
    e->op=op;
    e->value = 0;
    return e;
}

void free_expr(expr* e)
{
    if (e!=NULL) {
        free_expr(e->a);
        free_expr(e->b);
        free(e);
    }
}
/* ... other methods ... */

expr* gcd(expr* aa, expr* bb)
{
    expr *a = copy_of(aa), *b = copy_of(bb);
    expr* t = b;
    while(!equals(b, zero))
    {
        t=b;
        b=mod(a, b);
        free_expr(a);
        a=t;
    }
    free_expr(b);
    return a;
}