В какой части памяти сохраняется результат выражения операторов return?

дело в том

 int func(void){
        int A = 10;
        int B = 20;
        return A+B
    }

который вызывается основной функцией

int main(void){
    int retVal = func();
    return 0;
}

в функции func() две локальные переменные будут храниться в стеке для области действия func(), но где хранится результат A+B?

а по вызову по ссылке насколько надежен этот метод?

в чем разница между следующими функциональными органами

int func(void){
    int A = 20;
    return A;
}

и

int* func(void){
    int A = 20;
    return &A;
}

почему возврат значений не вызывает ошибку ошибки сегментации, а возврат адреса?

"Надежный" в каком смысле?

Scott Hunter 09.05.2022 14:05

Компилятор может встроить код func. Возможно, он даже сможет выполнять вычисления во время компиляции, чтобы значения вообще не сохранялись. И в этом конкретном случае, поскольку retVal не используется, компилятор, скорее всего, вообще не будет вызывать функцию, а просто проигнорирует ее и переменную retVal.

Some programmer dude 09.05.2022 14:06

@ScottHunter, как и в случае возврата адреса локальной переменной из функции, это будет висячий указатель, но в том же случае для возврата значения локальной переменной я никогда не видел никаких проблем с этим.

aTechieSmile 09.05.2022 14:10
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
52
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

where does the result of A+B stored?

Этот сильно зависит от конкретной архитектуры и конкретного соглашения о вызовах — каждая архитектура отличается. Рассмотрим самый распространенный — x86-64 в Linux (см. https://en.wikipedia.org/wiki/X86-64, https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl, Где задокументирован x86-64 System V ABI?, https://en.wikibooks.org/wiki/X86_Assembly/X86_Architecture).

Представленная вами функция компилируется gcc в ссылка на божественную стрелу:

func:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], 10
        mov     DWORD PTR [rbp-8], 20
        mov     edx, DWORD PTR [rbp-4]
        mov     eax, DWORD PTR [rbp-8]
        add     eax, edx
        pop     rbp
        ret

В x86-64 возвращаемое значение хранится внутри регистра eax. add eax, edx помещает результат сложения внутрь регистра eax. После установки eax функция возвращается, и main может прочитать содержимое регистра eax, если он хочет получить возвращаемое значение.

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

aTechieSmile 09.05.2022 14:28

@geeeekyDeveloper: этот метод является напрямую возвращает значение. Он не возвращает адрес. Я не уверен, что понимаю ваш вопрос.

John Bode 09.05.2022 14:39

@JohnBode Я отредактировал свой вопрос, это может помочь вам правильно понять мой вопрос.

aTechieSmile 09.05.2022 14:47
Ответ принят как подходящий

in the function func() two local variables will be stored onto the stack for the scope of func() but where does the result of A+B stored?

Зависит от конкретного соглашения о вызовах для целевой архитектуры, обычно в регистре (например, eax на x86).

what is the difference between following function bodies

int func(void){
    int A = 20;
    return A;
}

and

int* func(void){
    int A = 20;
    return &A;
}

В первом случае вы возвращаете результат выражениеA, который представляет собой просто целочисленное значение 20; IOW, значение 20 записывается в какой-либо регистр или другое место в памяти, которое считывается вызывающей функцией.

Во втором случае вы возвращаете результат выражения &A, который является адрес переменной A в func. Проблема в том, что после выхода funcA перестает существовать, и эта ячейка памяти становится доступной для чего-то другого; значение указателя больше не равно действительный, а поведение при разыменовании недопустимого указателя не определено.

Учитывая, что вы пометили это ключевым словом «C», стоит сказать, что целью в первые дни существования C было то, что возвращаемое значение в виде целого числа или указателя должно помещаться в регистр процессора, поэтому память не выделяется. для сохранения значения.

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

При возврате указателя то, на что указывает указатель, является проблемой для программиста: для вас. Как вы обнаружили, если вы попытаетесь получить доступ к значению, которое вы только объявили как локальную переменную в вызываемой функции и вернули с помощью указателя, пространство локальной переменной — стек вызова функции — интенсивно используется повторно, и ваше значение будет быстро выброшено. .

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

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

Такие инструменты, как godbolt, позволяют легко увидеть, что компилятор сделал с вашим кодом.

Другие вопросы по теме