深入理解C/C++形参、传指针、传引用

一则经典的问题:

//为了突出程序主要目的,忽略了malloc()分配是否成功检查与free()相关逻辑
//这是第一段代码
struct test {
    int num;
};

void change_value(struct test *out)
{
    struct test *in = (struct test *)malloc(sizeof(struct test));
    in->num = 100;
    out = in;
    printf("in num:%d\n",out->num);
}

int main()
{
    struct test *out = (struct test *)malloc(sizeof(struct test));
    out->num = 0;

    change_value(out);

    printf("out num:%d\n",out->num);
    return 0;
}

输出

in num:100
out num:0

为何out num:0,在change_value内部我们已经把out指向in。in->num=100,那么out->num应该也等于100才是,导致这种“潜意识”的原因是我们经常会看见change_value如下实现:

//这是第二段代码
void change_value(struct test *out)
{
    out->num = 100;
    //.....
}

输出

out num:100

这里我们在change_value内部改变了out->num,回到main后out->num也理所应当的被改变了。这个时候可能会觉得在第一段change_value代码中,struct test *in使了什么魔术,为了验证是不是如此,我们继续改写change_value:

//这是第三段代码
void change_value(struct test *out)
{
    struct test *in = (struct test *)malloc(sizeof(struct test));
    in->num = 100;
    out->num = 90;
    out = in;
    out->num = 80;
    printf("in num:%d\n",out->num);
}

输出

in num:80
out num:90

是不是感觉更加神奇的事情来了,在change_value内部out->num=80,在main里out->num=90。难道是out = in这一行有什么神奇的魔法?不是,其实归根到底还是形参的问题。

形参 – 全称为“形式参数”是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传递的参数。形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量

分析:

在void change_value(struct test *out),out作为一个地址形参传入,它仅仅只是一个“副本”。之所以在第二段代码中可以改变out->num的值,原因是在change_value内部out虽然作为副本,但是out->num偏移的地址仍然“有效的”(这里有效是指,副本out与原始out地址相同,所以他们偏移指向的内容也相同)。

一旦地址改变,例如第三段代码中out=in,那么out->num就偏移到新的(in的)地址内容了,这也解释了第三段代码输出in num:80,out num:90的原因。简单图示:

引用

这样也解释了下面2段程序输出区别的原因

struct test {
    int *pnum;
};

void change_value(struct test *out)
{
    out->pnum = (int *)malloc(sizeof(int));
}

int main()
{
    struct test *out = (struct test *)malloc(sizeof(struct test));
    change_value(out);
    *(out->pnum) = 1;
    printf("out pnum:%d\n",*(out->pnum));
    return 0;
}

输出

out pnum:1


void change_value(int *out)
{
    out = (int *)malloc(sizeof(int));
}

int main()
{
    int *out = NULL;
    change_value(out);
    *out = 1;
    printf("out pnum:%d\n",*out);
    return 0;

输出

Segmentation fault (core dumped)

那么如何“忽略副本”,改变实际的out地址值呢?

1.使用指针的指针,即二维指针
struct test {
    int num;
};

void change_value(struct test **out)
{
    struct test *in = (struct test *)malloc(sizeof(struct test));
    in->num = 100;
    (*out)->num = 90;
    (*out) = in;
    (*out)->num = 80;
    printf("in num:%d\n",(*out)->num);
}

int main()
{
    struct test *out = (struct test *)malloc(sizeof(struct test));
    change_value(&out);
    printf("out num:%d\n",out->num);
    return 0;
}

输出

in num:80
out num:80
2.传引用
struct test {
    int num;
};

void change_value(struct test *&out)
{
    struct test *in = (struct test *)malloc(sizeof(struct test));
    in->num = 100;
    out->num = 90;
    out = in;
    out->num = 80;
    printf("in num:%d\n",out->num);
}

int main()
{
    struct test *out = (struct test *)malloc(sizeof(struct test));
    change_value(out);
    printf("out num:%d\n",out->num);
    return 0;

输出

in num:80
out num:80

小结:这里没有什么奇怪的魔法:),只是需要仔细理解形参的意义-即传入指针的地址,这个地址偏移意味着什么,如果将地址副本改变偏移又意味什么。

(全文结束)


转载文章请注明出处:漫漫路 - lanindex.com

Leave a Comment

Your email address will not be published.