길민호(ethan.mino)
코딩수첩
길민호(ethan.mino)
전체 방문자
오늘
어제
  • 분류 전체보기 (215)
    • Computer Science (0)
    • Web (6)
      • CSS (0)
      • HTML (0)
    • Node.js (0)
    • Javascript (2)
    • Java (46)
      • Spring (27)
      • Jsp (0)
    • C\C++ (2)
    • Programming (0)
    • AI (0)
    • Database (7)
    • Git (5)
    • Algorithm (119)
      • Stack (0)
      • Queue (0)
      • Linked List (0)
      • Sort (0)
      • Simulation (27)
      • Recursion (0)
      • Backtracking (4)
      • Two Pointer (3)
      • Dynamic Programming (19)
      • Greedy (10)
      • Graph (3)
      • Dijkstra (1)
      • BFS\DFS (8)
      • Floyd (1)
      • MST (4)
      • Tree (4)
      • Binary Search (8)
      • Binary Search Tree (4)
    • IntelliJ (4)
    • Vscode (0)
    • Operating System (0)
    • 후기 (3)
    • 성장일지 (13)
    • 스터디 (7)
    • 설치 (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • ㅡ

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
길민호(ethan.mino)

코딩수첩

C\C++

C++ (Call By Value, Call By Reference)

2022. 8. 19. 00:29

C++  (Call By Value, Call By Reference)


#include <iostream>

int func1(int a) {
    a += 1;
    return 20;
}

int func2(int * a) {
    *a += 2;
    return 32;
}

int func3(int & a) {
    a += 3;
    return 100;
}

int main() {
    int t = 5;
    t += 3;

    printf("Before : %d\n", t);
    func1(t);
    printf("After func1() : %d\n", t);
    func2(&t);
    printf("After func2() : %d\n", t);
    func3(t);
    printf("After func3() : %d\n", t);
}
Before : 8
After func1() : 8
After func2() : 10
After func3() : 13

보시면, main 함수에서는 t에 5를 넣어주고, 3을 더해줍니다. 

그리고, func1() 함수를 호출해줍니다.

func1()을 보시면, 파라미터의 타입이 int 타입이고, a에 1을 더해줍니다. 

func1()가 반환한 이후에 main()에서 t 값을 출력해보면, 값이 바뀌지 않고 8이 그대로 출력되는 것을 확인할 수 있습니다.

func1() 호출부의 아규먼트 값이 파라미터에 단순히 복사되었기 때문입니다.

 

다음으로, func2() 함수를 호출해줍니다. 

func2()를 보시면, 파라미터의 타입이 int 포인터 형이고, 포인터 변수를 이용하여 값을 2 증가시켜주었습니다.

func1()과는 다르게, a가 t의 주소를 가리키고 있기 때문에, func2()가 반환했을 때, t의 값이 바뀝니다.

 

다음으로, func3() 함수를 호출해줍니다.

func3()의 파라미터는 int형의 참조자인데요, 참조자를 사용하면, t의 메모리 공간에 a라는 또 다른 이름을 붙인 것처럼 사용할 수 있습니다.

func3() 함수에서는 a에 3을 더해주고, func3()가 반환한 이후 t의 값은 변경되어서 13이 출력됩니다.

     3: int func1(int a) {
      /*
          func1() 함수의 실행을 준비하는 prologue
      */  
     4: 	a += 1;
00A51808  mov         eax,dword ptr [ebp+8]  // eax 레지스터에 8이 들어감
00A5180B  add         eax,1  
00A5180E  mov         dword ptr [ebp+8],eax  
     5: 	return 20;
00A51811  mov         eax,14h  
     6: }
     
  	8: int func2(int * a) {
      /*
          func2() 함수의 실행을 준비하는 prologue
      */ 
     9: 	*a += 2;	// func3()와 동일
00A51868  mov         eax,dword ptr [ebp+8]  
00A5186B  mov         ecx,dword ptr [eax]  
00A5186D  add         ecx,2  
00A51870  mov         edx,dword ptr [ebp+8]  
00A51873  mov         dword ptr [edx],ecx  
    10: 	return 32;
00A51875  mov         eax,20h  
    11: }
    
        13: int func3(int & a) {
      /*
          func3() 함수의 실행을 준비하는 prologue
      */
    14: 	a += 3;	// // func2()와 동일
00A518D8  mov         eax,dword ptr [ebp+8]  
00A518DB  mov         ecx,dword ptr [eax]  
00A518DD  add         ecx,3  
00A518E0  mov         edx,dword ptr [ebp+8]  
00A518E3  mov         dword ptr [edx],ecx  
    15: 	return 100;
00A518E5  mov         eax,64h  
    16: }
    
int main() {
      /*
          main() 함수의 실행을 준비하는 prologue
      */
    19: 	int t = 5;
00A51A48  mov         dword ptr [ebp-8],5  
    20: 	t += 3;
00A51A4F  mov         eax,dword ptr [ebp-8]  
00A51A52  add         eax,3  
00A51A55  mov         dword ptr [ebp-8],eax  
    21: 
    22: 	printf("Before : %d\n", t);
00A51A58  mov         eax,dword ptr [ebp-8]  
00A51A5B  push        eax  
00A51A5C  push        0A57B30h  
00A51A61  call        00A5104B  
00A51A66  add         esp,8  
    23: 	func1(t);
00A51A69  mov         eax,dword ptr [ebp-8] // eax에 8이 들어감 
00A51A6C  push        eax  
00A51A6D  call        00A51190  
00A51A72  add         esp,4  
    24: 	printf("After func1() : %d\n", t);
00A51A75  mov         eax,dword ptr [ebp-8]  
00A51A78  push        eax  
00A51A79  push        0A57B40h  
00A51A7E  call        00A5104B  
00A51A83  add         esp,8  
    25: 	func2(&t);
00A51A86  lea         eax,[ebp-8]  // eax에 t의 주소값이 들어감
00A51A89  push        eax  
00A51A8A  call        00A510A0  
00A51A8F  add         esp,4  
    26: 	printf("After func2() : %d\n", t);
00A51A92  mov         eax,dword ptr [ebp-8]   
00A51A95  push        eax  
00A51A96  push        0A57B58h  
00A51A9B  call        00A5104B  
00A51AA0  add         esp,8  
    27: 	func3(t);
00A51AA3  lea         eax,[ebp-8]  // eax에 t의 주소값이 들어감
00A51AA6  push        eax  
00A51AA7  call        00A511BD  
00A51AAC  add         esp,4  
    28: 	printf("After func3() : %d\n", t);
00A51AAF  mov         eax,dword ptr [ebp-8]  
00A51AB2  push        eax  
00A51AB3  push        0A57B70h  
00A51AB8  call        00A5104B  
00A51ABD  add         esp,8  
    29: }

위 어셈블리 코드를 보면,

func1()을 호출할 때, ebp-8 주소에 저장된 값을 eax 레지스터에 넣어주는데요, ebp-8에 저장된 값이 바로 t의 주소값입니다.

따라서 eax 레지스터에는 8이라는 값이 들어가고, eax 레지스터를 스택에 push 해줍니다.

그리고 func1()을 호출해줍니다. 그럼 func1()에서는 복사된 값을 사용하는 것이죠.

 

다음으로, func2()를 호출할 때에는, ebp-8에 저장된 값을 eax 레지스터에 저장하고, eax를 스택에 push 해줍니다.

그리고 func2()를 호출해줍니다. 즉, eax에는 t의 주소값이 들어가는 것이죠.

func2()에서는 a에 저장된 t의 주소값을 eax에 저장한 후, eax에 저장된 주소로부터 4바이트를 불러와서 ecx에 저장한 다음, 2를 더해주고, t의 주소에 저장해줍니다. 

따라서, 2를 더한 값을 t의 주소에 저장해주었기 때문에 func2()가 반환한 이후에도 값이 변경된 것입니다.

 

그렇다면, 참조자는 어떻게 동작하는 걸까요?

func2()와 비교해보자면 func3()은 func2()와 완벽히 동일합니다. 

호출부도 동일하고, 함수 내부의 동작도 동일합니다. 

따라서, 포인터든 참조자든 호출 시 주소를 전달하고, 참조자는 컴파일 되었을 때, 포인터와 동일한 방식으로 사용되는 것을 확인할 수 있습니다.

 

정리하자면, 어셈블리어를 보니 C의 포인터와 C++의 참조자 모두 주소를 전달하는 방식이었습니다.

 

저의 경우 C/Java는 Call by Value, C++의 참조자는 Call by Reference라고 배웠는데요,

"C는 Call by Reference다", "아니다 주소 '값'을 전달하기 때문에 Call by Value다",

"자바의 Primitive 타입은 Call by Value고 Reference는 Call by Reference다", "아니다 일종의 포인터 역할을 하기 때문에 Call by Value다." 등 책마다, 교수님마다, 사람마다 서로 다른 의견을 갖고 있기 때문에, Call by Value, Call by Reference를 명확히 정의하는 것은 어렵겠습니다만,  

제가 생각했을 때는, 주소의 전달 여부가 아닌, 함수 실행부에서 전달 받은 파라미터를 마치 아규먼트의 Aliasing처럼 사용할 수 있느냐로 Call by Reference냐 Call by Value냐를 구분할 수 있을 것 같습니다.

'C\C++' 카테고리의 다른 글

C언어 포인터는 왜 타입별로 구분되어 있는가?  (0) 2022.08.19
    'C\C++' 카테고리의 다른 글
    • C언어 포인터는 왜 타입별로 구분되어 있는가?
    길민호(ethan.mino)
    길민호(ethan.mino)
    💻 호기심 많은 서버 개발자 길민호입니다.

    티스토리툴바