使用传递引用/取消引用的奇怪行为

时间:2019-03-25 14:37:25

标签: c pass-by-reference dereference

这是我的演示代码:

#include <stdio.h>

#define WORK 0

typedef struct FooStruct {
    int x;
} FooStruct;

void setX(FooStruct *foo_ptr) {
    FooStruct foo = *foo_ptr;

    if (WORK) {
        foo_ptr->x = 10;
    } else {
        foo.x = 10;
    }
}

FooStruct makeFoo() {
    FooStruct foo = { 0 };

    setX(&foo);

    return foo;
}

int main(void) {
    FooStruct foo = makeFoo();

    printf("X = %d\n", foo.x);

    return 0;
}

如果WORK定义为1,则代码将按预期执行并显示“ X = 10”。

但是,如果WORK设置为0,则打印“ X = 0”,或者如果未默认初始化FooStruct(即,将FooStruct foo = {};更改为FooStruct foo;),则valgrind将抛出一个值printf行上未初始化的错误。

我确定这是由于我的理解上的空白,但是对我来说,两个不同的WORK分支在操作上应该基本相同,所以我不确定误解是从哪里来的。

这是在带有/不带有valgrind的gcc 8.2.0中编译的,结果相同。

======================

编辑:

简化示例:

#include <stdio.h>

#define WORK 0

void setX(int *x_ptr) {
    if (WORK) {
        *x_ptr = 5;
    } else {
        int x = *x_ptr;
        x = 5;
    }
}

int main(void) {
    int x = 0;
    setX(&x);

    printf("X = %d\n", x);

    return 0;
}

2 个答案:

答案 0 :(得分:3)

当您#define WORK 0时,该代码似乎也可以正常工作。如果您在结构的副本中设置成员,则不应期望修改原始成员。

您有:

void setX(FooStruct *foo_ptr) {
    FooStruct foo = *foo_ptr;

    if (WORK) {
        foo_ptr->x = 10;    // Modify the structure pointed to by foo_ptr
    } else {
        foo.x = 10;         // Modify the local copy of the structure
    }
}

由于您已经制作了MCVE(Minimal, Complete, Verifiable Example-谢谢!),setX()中修改后的结构副本没有任何作用。

答案 1 :(得分:3)

在此功能中:

void setX(FooStruct *foo_ptr) {
    FooStruct foo = *foo_ptr;

    if (WORK) {
        foo_ptr->x = 10;
    } else {
        foo.x = 10;
    }
}

foo是类型FooStruct局部变量。因此,该函数在foo上执行的任何修改都不会在函数外部产生可见的效果。

使用参数foo所指向的FooStruct的内容的副本来初始化foo_ptr。特别是,它不会使函数的本地foo成为foo_ptr所指向内容的别名。

另一方面,如果函数修改了foo_ptr指向的对象,那么对于任何其他可以直接或间接看到该对象的代码,当然都是可见的。