我在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);
}
}
答案 0 :(得分:2)
在gcd
中,您有三个expr*
,即a, b
和t
。
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;
}