일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- OS
- struct반환
- disassemble
- 숏코딩
- C
- NASM
- movaps
- assembly
- struct
- stackalign
- csproj
- modr/m
- void main
- GCC
- WPF
- 운영체제
- return
- compare
- 어셈블리
- C언어
- load effective address
- C#
- effective address
- movdqu
- modrm
- movups
- instruction
- sib
- 유효 주소
- call instruction
- Today
- Total
프로그래밍 잡화점
함수의 반환에 대한 고찰 본문
오늘은 함수 반환에 대해 간단하게 알아보고자 한다.
* 컴파일 옵션은 다음과 같다
gcc -o Main Main.c -O0
보통 함수에서 값을 반환하게 되면, eax(32bits), rax(64bits) 레지스터를 사용하게 된다.
그리하여 main에서 int를 반환할때도 eax나 rax에 exit_code를 전달해서 main을 탈출하게 되는데.
여기서 한가지 의문이 생기게 된다.
그렇다면 구조체 같이 복잡한 구조의 타입을 반환하면 어떻게 될까?
다음과 같이 한 구조체를 만들고 컴파일을 한 뒤 어셈블리 코드로 뜯어보았다
typedef struct {
int a, b, c;
} A;
A test() {
A a = { 1, 2, 3 };
return a;
}
int main() {
A a = test();
return a.a - 1;
}
생각보다 신기한 결과를 내놓았다.
main에서 먼저 구조체의 크기만큼 스택변수를 할당해준 뒤 해당 스택 변수의 위치를 rcx(ecx)에 담고 함수를 호출하였다.
그 뒤 test 함수에서는 받은 rcx의 주소 값을 바탕으로 해당 위치에 구조체의 값을 차례대로 넣었고 eax(rax)에 받았던 주솟값을 다시 돌려주었다.
그리하여 마지막 return a.a - 1에서는 main에서 할당해두었던 곳에서 읽어서 반환하는 것을 보였다.
우리가 주목해야할 것은 test라는 녀석이 반환한 것이 결국 포인터라는 것이다.
eax, rax의 크기에 맞지 않는 반환 타입을 갖는 함수는 그 이전 함수에서 미리 공간을 만들고 메모리 주소를 넘겨주어 값을 반환 받는 방식으로 돌아가는 것 같다.
이번에는 다음과 같이 main 함수를 고쳐서 뜯어보았다
int main() {
return test().a - 1;
}
아마도 최적화 옵션을 -O0으로 주어서 그런지 위에와 같은 결과를 보였다.
정리하자면, 정수 타입의 경우에는 eax, rax에 값을 그대로 넣어서 반환하지만, 구조체와 같은 복합 데이터 형식은 미리 할당한 스택 변수를 통해 값을 주고 받는다고 할 수 있다.
'C, C++ 분석 보고서' 카테고리의 다른 글
struct 반환에 대한 고찰 (2) | 2023.04.06 |
---|---|
main의 반환형에 관한 고찰 (In C) (2) | 2022.05.04 |
if에 관한 고찰 (In C) (0) | 2022.03.13 |
bool과 int의 관계 (In C) (0) | 2022.03.12 |
for에 관한 작은 고찰 (In C) (0) | 2022.03.11 |