简体   繁体   中英

Pointer inside struct appears to change when struct is passed by value to function

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM