How do C++ compilers actually pass reference parameters?

倖福魔咒の 提交于 2019-11-28 20:41:15

Just to chime in, I believe you are right. I use references for passing parameters to Fortran functions all the time. In my experience, using references or pointers at the Fortran-C++ interface is equivalent. I have tried this using GCC/Gfortran, and Visual Studio/Intel Visual Fortran. It may be compiler dependent, but I think basically all compilers implement references by pointer passing.

C++ doesn't define how implementations should be, it's just a language. So there isn't "a" implementation of references.

That said, references are implemented with pointers. This leads to a lot of confusion ("references are just pointers", "references are just pointers with the mundane parts taken out") but that is not the case. References are aliases and will always be aliases.

The compiler will pass the address of a variable, and operate with that pointer. This has the same effect (but not the same semantics!). To be more concrete, a compiler might "replace" this:

void make_five(int& i)
{
    i = 5;
}

int main(void)
{
    int i = 0;
    make_five(i);
}

With this:

void make_five(int* const i)
{
    *i = 5;
}

int main(void)
{
    int i = 0;
    make_five(&i);
}

(In practice such a simple function would be inlined, but you get the point.) Hence why your colleague suggested you use a pointer.

Keep in mind references are to be preferred. This is where the distinction between references and pointers is important. Do you want to alias a variable, or do you want to point at it? Most of the times, the former. In C, you had to use a pointer to do this, and this contributes to the common C-programmer misconception that references are actually pointers.

To get similar semantics (since you are now pointing to a variable, and not aliasing it), you should ensure the value of the pointer is not null:

extern "C" void FORTRAN_ROUTINE (unsigned * flag)
{
    assert(flag); // this is normally not a problem with references, 
                  // since the address of a variable cannot be null.

    // continue...
}

Just to be safe.

In theory, in C++ referenced are implemneted as normal pointers. The compiler then changes the code for the function tonehave like a reference, but loading the address and then movifing indirectly the address.

Here is a small application:

void foo( int & value )
{
    value = 3;
}


void bar( int *value )
{
    *value = 3;
}

void do_test()
{
    int i;
    foo(i);
    bar(&i);
}

Lets assemble it and look at the gcc generated assemble (gcc -s):

        .file   "test-params.cpp"
        .text
.globl _Z3fooRi
        .type   _Z3fooRi, @function
_Z3fooRi:
.LFB0:
        .cfi_startproc
        .cfi_personality 0x0,__gxx_personality_v0
        pushl   %ebp
        .cfi_def_cfa_offset 8
        movl    %esp, %ebp
        .cfi_offset 5, -8
        .cfi_def_cfa_register 5
        movl    8(%ebp), %eax
        movl    $3, (%eax)
        popl    %ebp
        ret
        .cfi_endproc
.LFE0:
        .size   _Z3fooRi, .-_Z3fooRi
.globl _Z3barPi
        .type   _Z3barPi, @function
_Z3barPi:
.LFB1:
        .cfi_startproc
        .cfi_personality 0x0,__gxx_personality_v0
        pushl   %ebp
        .cfi_def_cfa_offset 8
        movl    %esp, %ebp
        .cfi_offset 5, -8
        .cfi_def_cfa_register 5
        movl    8(%ebp), %eax
        movl    $3, (%eax)
        popl    %ebp
        ret
        .cfi_endproc
.LFE1:
        .size   _Z3barPi, .-_Z3barPi
.globl _Z7do_testv
        .type   _Z7do_testv, @function
_Z7do_testv:
.LFB2:
        .cfi_startproc
        .cfi_personality 0x0,__gxx_personality_v0
        pushl   %ebp
        .cfi_def_cfa_offset 8
        movl    %esp, %ebp
        .cfi_offset 5, -8
        .cfi_def_cfa_register 5
        subl    $20, %esp
        leal    -4(%ebp), %eax
        movl    %eax, (%esp)
        call    _Z3fooRi
        leal    -4(%ebp), %eax
        movl    %eax, (%esp)
        call    _Z3barPi
        leave
        ret
        .cfi_endproc
.LFE2:
        .size   _Z7do_testv, .-_Z7do_testv
        .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
        .section        .note.GNU-stack,"",@progbits

As you can see, in both functions, the compiler reads the (stack movl 8(%ebp), %eax), and in both calls the compiler saves the address to the stack (leal -4(%ebp), %eax).

The answer GMan - Save the Unico gave about C declaration might be the problem. It seems the problem is interoperability between C and fortran (at least those two compilers you are using).

No difference.

unsigned & flag

is exactly as you would write

unsigned * const flag

except for the operators to access object members ("." and "->" respectively).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!