дело в том
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;
}
почему возврат значений не вызывает ошибку ошибки сегментации, а возврат адреса?
Компилятор может встроить код func. Возможно, он даже сможет выполнять вычисления во время компиляции, чтобы значения вообще не сохранялись. И в этом конкретном случае, поскольку retVal не используется, компилятор, скорее всего, вообще не будет вызывать функцию, а просто проигнорирует ее и переменную retVal.
@ScottHunter, как и в случае возврата адреса локальной переменной из функции, это будет висячий указатель, но в том же случае для возврата значения локальной переменной я никогда не видел никаких проблем с этим.





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, если он хочет получить возвращаемое значение.
это вполне понятно, но в соответствии со стандартами программирования не рекомендуется возвращать адрес локальной переменной из-за проблемы с висячим указателем, поэтому, как этот метод надежен, чтобы напрямую возвращать значение или выражения?
@geeeekyDeveloper: этот метод является напрямую возвращает значение. Он не возвращает адрес. Я не уверен, что понимаю ваш вопрос.
@JohnBode Я отредактировал свой вопрос, это может помочь вам правильно понять мой вопрос.
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, позволяют легко увидеть, что компилятор сделал с вашим кодом.
"Надежный" в каком смысле?