Confused on pass-by-reference

2019-09-04 04:46发布

问题:

Consider the below example where I am attempting to pass-by-reference in the C way:

// Function prototypes
void increment(unsigned* number);

int main()
{
    unsigned* thing;
    increment(thing);
    cout << *thing;
    return 0;
}

void increment(unsigned* number)
{
    number = (unsigned*) malloc(sizeof(unsigned));
    *number = 1;
}

I get a program crash at the line cout << *thing. Yes, I am using C++ here but I wanted to try out the C version of pass-by-reference because my main project is in C.

I fixed it by changing the code as follows:

// Function prototypes
void increment(unsigned** number);

int main()
{
    unsigned* thing;
    increment(&thing);
    cout << *thing;
    return 0;
}

void increment(unsigned** number)
{
    *number = (unsigned*) malloc(sizeof(unsigned));
    **number = 1;
}

And now it works and the output is 1, like I expected. However, I do not understand why. I'm a bit confused as to why layering an extra pointer on top solves my problem.

Thanks!

回答1:

C doesn't have pass-by-reference. Except for arrays, all parameters are passed by value.

In your first version, you're passing the variable thing by value. In the increment function, it allocates memory and assigns that to the local variable number. But this has no effect on the caller's variable, because only its value was passed, not a reference to the variable. So thing is still uninitialized when increment returns, and indirecting through it results in undefined behavior.

If a function needs to modify a caller's variable, the caller has to pass a pointer to the variable, not just the value. That's what you did in the second version. Then the function can indirect through the pointer to update the variable.

This is essentially what's going on under the covers when you use references in C++. In C you have to code the extra level of indirection explicitly.



回答2:

Perhaps what you were looking for was something like this:

void increment(unsigned* number);

int main()
{
    unsigned thing;
    increment(&thing);
    cout << thing;
    return 0;
}

void increment(unsigned* number)
{
    *number = 1;
}

In C, all function parameters are passed by value. So you can't change a value in a function and expect to be reflected in the caller. But if the value passed in is a pointer, you CAN change what it points to. In this example, the address of thing is passed to increment. Then in increment, number contains the address of thing in main. So then you can change what number points to (i.e. thing), then when you return thing has been modified.

This differs slightly from your second example in that there's no dynamic memory allocation going on, which I believe is what you were aiming for.



回答3:

Saying to pass by reference in the context of your example means to pass a pointer to an object.

A pointer is itself an object as in your program

int main()
{
    unsigned* thing;
    //...

So to pass this object thing of type unsigned* by reference you have to pass a pointer to this object

void increment(unsigned** number);

int main()
{
    unsigned* thing;
    increment(&thing);
    //...

I think it will be more clear for you if to introduce a typedef. Imagine the following example

typedef unsigned* T;

void increment( T* number);

int main()
{
    T thing;
    increment( &thing );
    //...

I hope now it is more clear.:)



回答4:

The second way you posted is the correct way to do it. The reason the first way doesn't work is that you are trying to modify number when number has in fact been passed by value. So although you have in fact passed the variable thing to increment by reference, the address of thing has been passed to increment by value.