Вызов непустой функции без использования ее возвращаемого значения.Что на самом деле происходит? - PullRequest
16 голосов
/ 22 октября 2010

Итак, я нашел похожий вопрос здесь , но ответы больше касаются стиля и того, способны ли вы это сделать.

Мой вопрос: что на самом деле происходит, когдаВы вызываете не пустую функцию, которая возвращает объект, но вы никогда не назначаете или не используете указанный возвращенный объект?Итак, меньше о том, можете ли вы это сделать или нет, потому что я точно знаю, что вы можете и понимаете другой вопрос, связанный выше ... что делает среда компилятора / среды выполнения?

Это не вопрос конкретного языка, ноесли вы ответите, укажите, на каком языке вы говорите, так как поведение будет другим.

Ответы [ 5 ]

16 голосов
/ 22 октября 2010

Я полагаю, что и для C #, и для Java результат попадает в стек, а затем компилятор заставляет команду pop игнорировать ее.Сообщение Эрика Липперта в блоге "Пустота инвариантна" содержит дополнительную информацию по этому вопросу.

Например, рассмотрим следующий код C #:

using System;

public class Test 
{
    static int Foo() { return 10; }

    public static void Main()
    {
        Foo();
        Foo();
    }
}

ILгенерируется (компилятором MS C # 4) для метода Main:

.method public hidebysig static void Main() cil managed
{
    .entrypoint
    .maxstack 8
    L_0000: call int32 Test::Foo()
    L_0005: pop 
    L_0006: call int32 Test::Foo()
    L_000b: pop 
    L_000c: ret 
}

Обратите внимание на вызовы pop, которые исчезают, если вы сделаете Foo пустым методом.

7 голосов
/ 22 октября 2010

что делает компилятор?

Компилятор генерирует команду pop, которая отбрасывает результат из виртуального стека.

что делает среда выполненияdo?

Обычно он объединяет код в код, который возвращает возвращаемое значение обратно в регистр, а не в стеке.(Обычно EAX на архитектурах x86.)

Джиттер знает, что значение останется неиспользованным, поэтому он, вероятно, генерирует код, который очищает регистр.Или, возможно, он просто какое-то время остается в реестре.

Какая среда выполнения вас волнует?Их много, и у всех разные дрожания.

3 голосов
/ 22 октября 2010

Это немного зависит от используемого соглашения о вызовах.Для маленьких / простых типов возврат обычно происходит в регистре.В этом случае функция запишет значение в регистр, но больше ничего не будет обращать внимания, и в следующий раз, когда потребуется этот регистр (что обычно происходит довольно быстро), он будет перезаписан чем-то другим.

Для больших типов компилятор обычно выделяет структуру для хранения возвращаемого значения.Точно, где и как это происходит, распределение будет зависеть от компилятора - в некоторых случаях это будет статическая структура, и содержимое будет игнорироваться и перезаписываться при следующем вызове функции, возвращающей тот же тип.В других случаях он будет в стеке, и даже если вы его не использовали, он все равно должен быть выделен перед вызовом функции и освобожден после этого

1 голос
/ 22 октября 2010

Для C ++, как правило, компилятор оптимизирует возврат переменной, превращая ее в пустую функцию, и, если это допускает дальнейшую оптимизацию, компилятор может оптимизировать весь вызов функции или ее части, которые относятся тольковозвращаемое значение.Для Java и C # у меня мало идей.

1 голос
/ 22 октября 2010

В .NET, если возвращаемый объект является ссылочным типом, а приложение не имеет других ссылок на этот объект, тогда у вас все еще будет объект, плавающий в памяти, пока сборщик мусора не решит его собрать.

Это потенциально плохо, если возвращаемый объект держится за ресурсы. Если он реализует интерфейс IDisposable, то ваш код должен вызывать метод Dispose для него, но в этом случае метод Dispose никогда не будет вызываться.

РЕДАКТИРОВАТЬ: исправлена ​​опечатка.

...