这个问题是由于一些混合语言编程而产生的。我有一个我想用C ++代码调用的Fortran例程。 Fortran通过引用传递所有参数(除非你另有说明)。
所以我认为在我的C ++代码中我会聪明(糟糕的开始)并定义类似这样的Fortran例程:
extern "C" void FORTRAN_ROUTINE (unsigned & flag);
这段代码工作了一段时间,但(当然,当我需要离开的时候)突然开始吹回电话。清除指示的电话堆栈。
另一位工程师来到我身后并解决了这个问题,声明例程已经在C ++中被定义为
extern "C" void FORTRAN_ROUTINE (unsigned * flag);
除了两件事,我接受了。一个是编译器没有通过引用传递引用参数似乎相当直观,而且在任何地方都找不到任何文档。另一个是他同时在那里改变了大量的其他代码,所以理论上它可能是另一个修改问题的修改。
所以问题是,C ++如何实际传递参考参数?是否可以免费为小值或其他东西进行复制,复制?换句话说,参考参数在混合语言编程中是完全无用的吗?我想知道所以我再也没有犯同样的代码杀戮错误。
答案 0 :(得分: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...
}
为了安全起见。
答案 1 :(得分:4)
只是为了报复,我相信你是对的。我使用引用将参数传递给Fortran函数。根据我的经验,在Fortran-C ++接口上使用引用或指针是等效的。我尝试过使用GCC / Gfortran和Visual Studio / Intel Visual Fortran。它可能依赖于编译器,但我认为基本上所有编译器都通过指针传递来实现引用。
答案 2 :(得分:2)
理论上,在C ++中引用的是作为普通指针实现的。编译器然后将函数tone的代码更改为引用,但加载地址然后间接地移动地址。
这是一个小应用程序:
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 - 拯救Unico给出了C声明可能是问题所在。似乎问题是C和fortran之间的互操作性(至少是你正在使用的那两个编译器)。
答案 3 :(得分:1)
没有区别。
unsigned&标志
与你写的完全一样
unsigned * const flag
除了操作员访问对象成员(分别为“。”和“ - >”)。