常量传递是否自动按引用传递?

3

如果我将一个变量传递给函数,但将该变量标记为常量,它是否会自动以引用的方式传递?


C++中

void foo(const int a)

测试后:不行,因为它打印了不同的内存位置。
void foo(const int a)
{
    cout << &a << endl;
}

int main()
{
   int a = 5;
   foo(a);
   cout << &a << endl;
   return 0;
}

C

不可以吗?因为我只能通过指针传递内存位置,而不是真正的引用。就像这里一样:在C中按引用传递


Java

也许编译器可以推断出final应该指向同一个内存位置?

void foo(final int a)

C#

与Java相同吗?

void foo(const int a)

我知道我的问题涉及到4种语言,但由于我经常使用这4种语言,所以我想知道。

有没有一种方法可以强制Java和C#来处理这个问题?我知道在C++中,我可以在函数中添加&,这正是我想要的。


 void f(const int &a)

实际上,这就是我希望Java和C#编译器在幕后做的大部分工作。

3
所有这些都是语义上的按值传递。编译器是否可以在幕后进行优化?当然,这是可能的。 - Oliver Charlesworth
我猜你问这个问题是因为你经常看到C++函数原型将一个const对象的引用作为参数。这是一种有意的优化,允许函数参数值成为rvalue而不必为函数调用复制它们(C++11添加了rvalue引用作为替代方案)。但是,默认情况下,const函数参数不会通过引用传递。 - Sam Varshavchik
1
对于具有多种语言标签且实际上与所有标签相关的问题,加1。 - jdphenix
1
Java基本类型没有引用。Java始终按值传递。 - Elliott Frisch
2
你的问题“有没有办法强制Java和C#处理[]这个符号”的目的是什么?如果你想传递相同的内存位置,以便方法可以修改参数并允许调用者看到修改后的值,则可以在Java中实现此操作(我不太了解C#)。但如果不需要这样做,那么为什么希望编译器在幕后传递地址,并且为什么关心呢?请注意,通过地址传递“int”往往会使程序变慢,因为它需要间接访问该值。 - ajb
显示剩余5条评论
3个回答

4
在Java中,所有变量都是按值传递的。原始类型实际上是被复制的,而对象变量是引用,这意味着对象变量的值实际上是引用——您正在传递引用的值。尽管如此,您总是按值传递,与C++相同,如果您不显式使用引用,则将按值传递。我几乎可以肯定,在C#中也是这样的。编辑:我忘了C。在C中也是这样:默认情况下按值传递。显然,您可以传递指针。在C++中,这被扩展为您可以将引用作为参数。
void function(int& intByReference) {
    cout<<&intByReference<<endl; //prints x
}

int main() {
    int n = 0;
    cout<<&n<<endl; //prints x
    function(n);
}

C++包含类,因此有一个默认构造函数来复制对象:

class MyClass {

    public:
        MyClass(MyClass& objectToCopy) {
            //copy your object here
            //this will be called when you pass an instance of MyClass to a function
        }
};

剩下的部分怎么样? - J4v4
C++部分也是如此 :-) 你实际上是按值传递的,如果直接传递对象,则甚至会使用构造函数进行复制。考虑到这一点,C#中默认应该是按值传递,特别是对于基元类型,这是唯一有意义的事情。 - J4v4
在C#中也是一样的。除非参数被标记为refout,否则它总是按值传递,如果被标记为refout,则按引用传递。 - newacct

1
你犯了一个错误,认为你的直觉可以指导你如何实现语言的细节。这是完全不正确的。在C++和C的情况下,规范非常明确地说明了应该如何处理这个问题。
作为按值传递的语言,Java可能在final/const/not touched值的规范中有一些规定。但只有规范才能允许这种行为。

1
这并不是完全清楚的。你一开始暗示这些是实现细节,但接着又提到了规范,而规范与实现细节无关... - Oliver Charlesworth
@OliCharlesworth 我的意思是他正在将直觉推广到规范所涵盖的内容上。如果我表达不清楚,请编辑或建议修改。 - Captain Giraffe

0
在回答你的问题之前,让我举个例子来测试一下你的场景。
假设你从方法void calledFrom()中通过传递变量int a调用了一个方法void called(int a)
据我理解:
如果在任何一个方法中改变a的值会影响另一个方法(即在calledFrom中改变a的值会影响called中a的值),那么它就是按引用调用,否则就是按值调用
现在让我们使用final在Java中进行测试。
public class MyClass
{
    int a;
    public void setA(final int a)
    {
        this.a=a;
    }
    public void printA()
    {
        System.out.println(a);
    }

}

从主方法中你可以这样做:

int a=10;
MyClass mclass=new MyClass();

mclass.setA(a);
mclass.printA();
a++;
mclass.printA();

输出结果为: 10 10

因此在main中的更改并不会改变MyClassa的值。所以对public void setA(final int a)的调用不是按引用调用,而是按值调用。

final关键字可能会引起一些混淆。当您在方法签名中写入final时,它不会以任何其他方式影响其行为,除非您不能在该方法内更改该参数的值。 例如,如果您编写:

public void setA(final int a)
{
    a++; //error
    this.a=a;
}

你会收到编译错误。
但这很容易做到。
 public void printA()
{
    a++; //ok
    System.out.println(a);
}

我认为这不仅在Java中,所有其他语言都有相同的行为。你可以尝试其他语言。

要在Java上下文中清楚地了解按值传递和按引用传递的概念,您可以阅读this线程

或者阅读this答案


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接