16

この質問は、いくつかの混合言語プログラミングの結果として生じました。C++コードから呼び出したいFortranルーチンがありました。Fortranは、すべてのパラメーターを参照によって渡します(特に指定しない限り)。

だから私は自分のC++コードで賢く(すぐに悪いスタートを切る)、Fortranルーチンを次のように定義すると思いました:

extern "C" void FORTRAN_ROUTINE (unsigned & flag);

このコードはしばらくの間機能しましたが、(もちろん、私が去る必要があったときに)突然、折り返し電話で爆発し始めました。変更されたコールスタックの明確な表示。

別のエンジニアが私の後ろに来て問題を修正し、ルーチンをC++で次のように定義する必要があると宣言しました。

extern "C" void FORTRAN_ROUTINE (unsigned * flag);

私は2つのことを除いてそれを受け入れます。1つは、コンパイラーが参照パラメーターを参照によって渡さないことは直感に反しているように思われることです。そのことを示すドキュメントはどこにも見つかりません。もう1つは、彼がそこにある他のコード全体を同時に変更したことです。したがって、理論的には、問題が何であれ、それを修正した別の変更であった可能性があります。

問題は、C++が実際に参照パラメーターをどのように渡すのかということです。小さな値などのコピーイン、コピーアウトを行うのはおそらく無料ですか?言い換えれば、参照パラメータは混合言語プログラミングではまったく役に立たないのでしょうか?私は知りたいので、これと同じコード殺害の間違いを二度としないようにします。

4

4 に答える 4

9

C ++は、実装のあり方を定義していません。単なる言語です。したがって、参照の「a」実装はありません。

とはいえ、参照はポインタを使用して実装されます。これは多くの混乱を招きます(「参照は単なるポインタです」、「参照はありふれた部分が取り出された単なるポインタです」)が、そうではありません。参照はエイリアスであり、常にエイリアスになります。

コンパイラは変数のアドレスを渡し、そのポインタで動作します。これには同じ効果があります(ただし、同じセマンティクスではありません!)。より具体的には、コンパイラはこれを「置き換える」可能性があります。

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

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

これとともに:

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

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

(実際には、このような単純な関数はインライン化されますが、要点はわかります。)したがって、同僚がポインターの使用を提案した理由。

参照が優先されることに注意してください。ここで、参照とポインタの区別が重要になります。変数のエイリアスを作成しますか、それともそれをポイントしますか?ほとんどの場合、前者。Cでは、これを行うためにポインターを使用する必要がありました。これは、参照が実際にはポインターであるという一般的なCプログラマーの誤解の原因になります。

同様のセマンティクスを取得するには(変数をポイントしており、エイリアスを作成していないため)、ポインターの値が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...
}

安全のために。

于 2010-05-29T22:59:09.150 に答える
4

チャイムを鳴らすだけで、あなたは正しいと思います。私は常にFortran関数にパラメーターを渡すための参照を使用しています。私の経験では、Fortran-C++インターフェースで参照またはポインターを使用することは同等です。GCC / Gfortran、およびVisual Studio / IntelVisualFortranを使用してこれを試しました。コンパイラに依存するかもしれませんが、基本的にすべてのコンパイラはポインタ受け渡しによってリファレンスを実装していると思います。

于 2010-05-30T00:45:29.663 に答える
3

理論的には、C ++では、参照されるものは通常のポインターとして実装されます。次に、コンパイラは関数tonehaveのコードを参照のように変更しますが、アドレスをロードしてから間接的にアドレスを移動します。

これが小さなアプリケーションです:

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


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

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

それをアセンブルして、gccで生成されたアセンブル(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

ご覧のとおり、両方の関数で、コンパイラーは(stack movl 8(%ebp), %eax)を読み取り、両方の呼び出しで、コンパイラーはアドレスをスタック(leal -4(%ebp), %eax)

GMan-SavetheUnicoがC宣言について与えた答えが問題かもしれません。問題はCとFortran(少なくとも使用している2つのコンパイラ)間の相互運用性にあるようです。

于 2010-05-30T06:09:44.297 に答える
1

変わりはない。

署名なし&フラグ

あなたが書くのとまったく同じです

unsigned*constフラグ

オブジェクトメンバーにアクセスする演算子(それぞれ「。」と「->」)を除きます。

于 2010-05-30T07:23:23.783 に答える