The problem concerns the cmps element of struct Variable.
typedef struct Variable {
struct Variable *cmps[2];
double (*d_op)(void **a, struct Variable b);
double val;
} Variable;
Variable new_var(double a)
{
Variable v;
v.cmps[0] = NULL;
v.cmps[1] = NULL;
v.d_op = NULL;
v.val = a;
return v;
}
Variable vs_mult(Variable a, Variable b)
{
Variable v = new_var(0.0);
v.cmps[0] = &a;
v.cmps[1] = &b;
v.d_op = &d_mult;
return v;
}
int var_same(Variable a, Variable b)
{
if ((a.cmps[0] == b.cmps[0]) && (a.cmps[1] == b.cmps[1]))
{
return 1;
}
return 0;
}
double derivative(Variable a, Variable b)
{
printf("(a.cmps)[0]->val: %lf; (a.cmps)[1]->val: %lf\n", (a.cmps)[0]->val, (a.cmps)[1]->val);
if (var_same(a, b))
{
return 1.0;
}
if (a.cmps[0] == NULL)
{
return 0.0;
}
return a.d_op(a.cmps, b);
}
int main(int argc, char** argv)
{
Variable x = new_var(2.0);
Variable y = new_var(5.0);
Variable z = vs_mult(x, y);
eval(&z);
printf("(z.cmps)[0]->val: %lf; (z.cmps)[1]->val: %lf\n", (z.cmps)[0]->val, (z.cmps)[1]->val);
printf("%lf\n", derivative(z, x));
}
When the above program is executed the immediate output is this:
(z.cmps)[0]->val: 2.000000; (z.cmps)[1]->val: 5.000000
(a.cmps)[0]->val: 2.000000; (a.cmps)[1]->val: 10.000000
Why is (a.cmps)[1]->val
not equal to (z.cmps)[1]->val
?
Stackoverflow wants more details but I think I've outlined the problem fully. There are of course a couple function definitions I left out but they shouldn't matter.
CodePudding user response:
I don't know what eval
is doing but there is something very dangerous in your code:
Variable vs_mult(Variable a, Variable b)
{
Variable v = new_var(0.0);
v.cmps[0] = &a;
v.cmps[1] = &b;
v.d_op = &d_mult;
return v;
}
Note that function parameters a
and b
are local variables and their lifetime ends when vs_mult
returns. Thus values in cmps
are dangling pointer. Using their values (what eval
likely does) invokes undefined behaviour.
To fix this the vs_mult
should take pointers to variables.
Variable vs_mult(Variable *a, Variable *b)
{
Variable v = new_var(0.0);
v.cmps[0] = a;
v.cmps[1] = b;
...
}
However, now the caller is responsible for assuring that lifetime of both variables encapsulates life of value returned by vs_mult
.
Alternatively, you can try reference counting.