简体   繁体   English

当传递值作为引用返回时会发生什么?

[英]What happens when a pass-by-value is returned as a reference?

OK, before you jump to your feet, you need to understand what pass-by-value compared to pass-by-ref is.好的,在你站起来之前,你需要了解什么是 pass-by-value 与 pass-by-ref 相比。 You may not agree with this definition of pass-by-value but that is merely semantics because the real question is what transpires between stack alloc and heap alloc.您可能不同意按值传递的这种定义,但这仅仅是语义,因为真正的问题是堆栈分配和堆分配之间发生了什么。

Pass-by-value: The object to be passed is copied and the copy of the object is submitted as argument to a function (ok, OO purists you like to call it a "method" - semantics.), Therefore, at the end/return of the function.按值传递:要传递的 object 被复制,并且 object 的副本作为参数提交给 function(好吧,OO 纯粹是你喜欢的语义) /返回 function。 the original object is not modified regardless of what had been done to the copy of the object.无论对对象的副本做了什么,原始 object 都不会被修改。

So Java (and presumably C# too) is a pass-by-value language.所以 Java(也可能是 C#)是一种按值传递的语言。 Some people think they are pass-by-ref but in actual fact the args being passed are references.有些人认为它们是传递引用,但实际上传递的参数是引用。 So the copy of references is being passed to the function.所以引用的副本被传递给 function。 That is, the reference is passed-by-value arg, because the original reference is not changed at the end/return of the function.也就是说,引用是按值传递的 arg,因为原始引用在 function 的末尾/返回时没有更改。

Now that we have gotten this out of the way, and come to accept my def of pass-by-value, here is the question.既然我们已经解决了这个问题,并且接受了我的值传递定义,那么问题来了。

So a function argument is a copy of the original object/reference.所以 function 参数是原始对象/引用的副本。 It is allocated on the stack.它在堆栈上分配。 Stack is good because an allocated value is simply and immediately discarded at the end/return of the function.堆栈是好的,因为分配的值在 function 的结束/返回时被简单地立即丢弃。 What happens when my function takes the pass-by-value arg from the stack and returns it.当我的 function 从堆栈中获取传递值 arg 并将其返回时会发生什么。 See, it is on the stack.看,它在堆栈上。 Is the stack alloc of that object/reference copied and realloc onto the heap?该对象/引用的堆栈分配是否被复制并重新分配到堆上?

What exactly/precisely happens in Java and C#? Java 和 C# 中究竟/准确地发生了什么?

It sounds like you are asking what is the effect of something like this in Java:听起来你在问Java中这样的东西有什么效果:

public static void f(Object object) {
    return object;
}

public static void g() {
    Dog dog = new Dog("Spike");
    System.println(f(dog));
}

If so, the answer is, when g is called:如果是这样,答案是,当 g 被调用时:

  1. Memory is allocated on the heap, and a stack-allocated variable in g called dog is made to reference this memory. Memory 分配在堆上,g 中称为 dog 的堆栈分配变量引用这个 memory。 The "value" of dog is a reference to the object; dog 的“值”是对 object 的引用; it takes up one word of memory.它占用memory的一个字。

  2. A copy of this value is passed, through a register or on the stack to f.该值的副本通过寄存器或堆栈传递给 f。 f gets its own stack frame, unless optimized away by the compiler. f 获得自己的堆栈帧,除非被编译器优化掉。 But let's say it does get a stack frame.但是假设它确实得到了一个堆栈帧。 A simple word, containing the value of copy of the address is placed in this stack frame.一个简单的单词,包含地址副本的值被放置在这个堆栈帧中。 Really, it is no different than passing a plain old integer in a way, as you correctly pointed out that everything in Java is pass-by-value.实际上,这与以某种方式传递普通的旧 integer 没有什么不同,正如您正确指出的那样,Java 中的所有内容都是按值传递的。

  3. When f returns, it passes the value of object, itself just a word of memory pointing to the original Dog object back to its caller.当 f 返回时,它将 object 的值传递给它的调用者,它本身只是 memory 的一个字,指向原来的 Dog object。 This simple pointer value is usually passed back through a register.这个简单的指针值通常通过寄存器传回。 The point is, only a word is passed back.关键是,只有一个字被传回。

In C#, the reference is returned, by value.在 C# 中,按值返回引用。

In the example below, the same thing that went in is the same thing that is returned.在下面的示例中,输入的内容与返回的内容相同。

Distance FindMinimum (Distance threshold)
{
    Distance min = null;
    foreach (Distance compare in AllDistance) {
        if (compare > threshold && (min == null || compare < min))
            min = compare;
    }
    if (min == null)
        return threshold;
    return min;
}

In the example below, the reference to the newly found Distance object is returned.在下面的示例中,返回对新找到的距离 object 的引用。

Distance FindNewThreshold (Distance threshold)
{
    foreach (Distance compare in AllDistance) {
        if (compare < threshold)
            threshold = compare;
    }
    return threshold;
}

In both cases above, the original object passed in is not changed.在上述两种情况下,原来传入的 object 都没有改变。 But in the following example, the original object will be replaced.但在下面的例子中,原来的 object 将被替换。

void FindNewThreshold (Distance threshold, ref Distance output)
{
    foreach (Distance compare in AllDistance) {
        if (compare < threshold)
            threshold = compare;
    }
    output = threshold;
}

void Test ()
{
    Distance d = new Distance (50);
    Distance o;
    AllDistance.Add(new Distance(10));
    FindNewThreshold (d, ref o);
    Console.WriteLine ("{0} {1}", d, o);
}

This will produce "50 10".这将产生“50 10”。 Making changes to o will affect the first object in AllDistance.更改 o 将影响 AllDistance 中的第一个 object。

Consider the 'int' case.考虑'int'的情况。

public int returnIt(int arg) { return arg;}

and a call to the function并致电 function

int in  = 6;
int out = returnIt(in);

When the function is called, the contents of 'in' are copied onto the stack.当调用 function 时,'in' 的内容被复制到堆栈中。

When the function executes 'return arg', the contents are copied into (well, I don't know where in the JVM, in some architectures it's to a register in some it's to the current top of the stack).当 function 执行 'return arg' 时,内容被复制到(嗯,我不知道 JVM 在哪里,在某些架构中它是到一个寄存器中,有些是到当前堆栈的顶部)。

Then 'arg' is reclaimed from the stack, but it's value has already been copied.然后'arg'从堆栈中回收,但它的值已经被复制了。

When the assignment happens, it's not copying from 'arg' it's copying from the location of the return value.当赋值发生时,它不是从 'arg' 复制,而是从返回值的位置复制。

(Of course, this is probably all optimised away in 'real life' for an example this simple) (当然,这可能在“现实生活”中都被优化了,例如这个简单的例子)

Is that what you were asking?那是你问的吗?

On C#, only structs are created on the stack, and it is impossible to create an object on the stack.在C#上,只在栈上创建结构体,不可能在栈上创建object。

There is a difference when creating new structs and objects.创建新结构和对象时有所不同。

When you create a new object using the new keyword, the object is always created in the heap no matter what, and this is the only way to create an object in C#. When you create a new object using the new keyword, the object is always created in the heap no matter what, and this is the only way to create an object in C#. The garbage collector doesn't frees the memory for an object on the heap until there exists no other references to it;垃圾收集器不会为堆上的 object 释放 memory,直到没有其他对它的引用; this is until all the references to the object are out of scope.直到所有对 object 的引用都超出 scope 为止。

When you create a new struct using the new keyword, the struct is always created in the stack no matter what.当您使用new关键字创建新结构时,无论如何,该结构总是在堆栈中创建。 When you assign a struct to another, a member-wise copy takes place instead of a reference copy as it happens with objects.当您将一个结构分配给另一个结构时,会发生成员方式的副本,而不是像对象那样发生的引用副本。

When an object is passed into a method by value, what you receive in the method is the value of the reference to the object (a pointer to it: all C# objects are stored as pointers to their location in memory).当 object 按值传递给方法时,您在方法中收到的是对 object 的引用的值(指向它的指针:所有 C# 对象都存储为指向其位置的指针)。 When a struct is passed by value into a method, what you receive is a member-wise copy of it.当结构按值传递给方法时,您收到的是它的成员副本。

Note: when you use the ref keyword when passing an object to a method, it means that the method can change the memory location at which the reference is pointing to.注意:当您在将 object 传递给方法时使用ref关键字时,意味着该方法可以更改引用指向的 memory 位置。

Finally, there is no way you could create an object in the stack inside a method, and by returning an object that was passed to your method, you'll be returning the same reference that was received.最后,您无法在方法内的堆栈中创建 object,并且通过返回传递给您的方法的 object,您将返回收到的相同引用。 When you return a struct that was passed, a member-wise copy will be returned.当您返回一个已传递的结构时,将返回一个按成员计算的副本。

On java, the concepts are similar, except that there are no structs, nor ref and out parameters.在 java 上,概念类似,只是没有结构体,也没有refout参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM