How do C++ compilers actually pass reference parameters?

后端 未结 4 1908
野性不改
野性不改 2020-12-14 09:49

This question came about as a result of some mixed-language programming. I had a Fortran routine I wanted to call from C++ code. Fortran passes all its parameters by referen

相关标签:
4条回答
  • 2020-12-14 10:13

    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.

    0 讨论(0)
  • 2020-12-14 10:19

    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.

    0 讨论(0)
  • 2020-12-14 10:27

    No difference.

    unsigned & flag

    is exactly as you would write

    unsigned * const flag

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

    0 讨论(0)
  • 2020-12-14 10:29

    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).

    0 讨论(0)
提交回复
热议问题