본문 바로가기

Language/C++

[C++] C 포인터 (call-by-value, call-by-reference) - 컴도리돌이

728x90

- int variables in memory

 

int num1 = 5;
int num2 = 129;

&num1 == ?
&num2 == ? 

 

정수형 (int)는 4byte를 크기를 갖는다.  num1의 주소가 10246이라 가정하면 10249 까지는 num1의 변수가 메모리에  저장된다. 

 

'&' 주는 주소 연산자이며 해당 변수의 주소 값을 반환한다. 그렇기 때문에 &num1의 값은 10246이고 &num2의 값은 10272이다.

 

 

example

-double, float variables in memory

 

double a = 3.14;
float b = 1.1;


&a == ?
&b == ?

 

double 형과 float 형은 각각 8byte, 4byte의 크기를 갖는다. 아래 그림의 예를 가지고 a와 b의 주소 값을 구해보면 &a 값은 10246 값을 갖고, &b 값은 10272를 갖는다.

 

example

 

- char variable, C string in memory

 

char ch = 'A' ;
char str[10] = "Hello";

&ch == ?
str == ?

 

문자 열는 1byte의 크기를 갖는다. ch 문자열 변수는 크기가 1byte이며, &ch 주소 값은 10244를 갖는다.  str 문자열 배열은 10byte의 크기를 갖는다. 배열의 주소 값을 알고 싶을 때는 '&' 주소 연산자 없이 배열의 이름만 입력하면 된다. str의 주소 값은 10266이다.

 

example

 

<Practice>

#include <stdio.h>

int main()
{
	char ch1 = 'a';
	char* pch1 = &ch1;
	printf("value of ch1: %d\n", ch1);
	printf("address of ch1: %p\n", &ch1);
	printf("value of pch1: %p\n", pch1);
	printf("address of pch1: %p\n", &pch1);
	return 0;
}
value of ch1: 97
address of ch1: 1636819
value of pch1: 1636819
address of pch1: 1636804

 


Pointer : 포인터는 주소를 저장하는 변수이다. 

 

- int* : 정수형 변수의 주소를 저장한다.

- double* , char* , float* ,... 

 

 

-  & operator

  • 변수의 주소를 반환
  • 연산의 주소
  • 변수 -> 주소

 

 

-  * operator

 

  • 연산자(포인터)가 가리키는 메모리 공간(변수) 언급한다.
  • 양방향 연산자
  • 주소 -> 변수

 

int num = 5;
int* pnum = &num;

// store 20 to the varaiable

pointed by pnum
*pnum = 20;

 

<quiz1>

#include <stdio.h>

int main()
{
    int i = 10;
    double d = 3.14;
    char c = 'a';
    
    int* pi = &i;
    double* pd = &d;
    char* pc = &c;
    
    (*pi)++;
    (*pd)++;
    (*pc)++;
    
    printf("%d %f %c\n", i, d, c);
    
    return 0;
}
11 4.140000 b

 

*pi, *pd, *pc는 각각 i, d, c의 주소들을 가리킨다. 그 상태에서 *pi, *pd, *pc의 값들을 1씩 증가시키면 각 포인터 변수들이 가리키는 i, d, c 변수의 값들이 1씩 증가된다.


An Array in Memory

 

#include <stdio.h>

int main()
{
  int arr[3] = {5, 10, 20};
  printf("arr: %p\n", arr);
  printf("&arr[0]: %p\n", &arr[0]);
  printf("&arr[1]: %p\n", &arr[1]);
  printf("&arr[2]: %p\n", &arr[2]);
  return 0;
}
arr: 0x7fffe174bd2c
&arr[0]: 0x7fffe174bd2c
&arr[1]: 0x7fffe174bd30
&arr[2]: 0x7fffe174bd34

 

해당 코드의 결과 값을 보면 첫 번째 printf에서 arr를 출력하면 해당 배열의 첫 주소 값이 나온다. arr에 있는 원소들의 주소 값들을 출력하면 &arr[0]은 arr 주소 값과 같다. 그 이후 주소 값들을 자세히 보면 4씩 증가하는 것을 발견할 수 있다. 왜냐하면 정수형(int)은 4byte 크기를 메모리에 할당하기 때문이다.

 

<arr == &arr>

int arr[3] = {5, 10, 20};
printf("arr: %p\n", arr);
printf("&arr: %p\n", &arr);
arr: 0x7fffe174bd2c
&arr: 0x7fffe174bd2c

 


 Arrays와 Pointer의 유사함 / 차이점

 

int arr[] = {5, 10, 15};
int* ptr = arr;


printf("%d %d %d %d\n", arr[0], *arr, ptr[0], *ptr);
5 5 5 5

 

<sizeof 연산자의 결과에서 차이>

 

int arr[3] = {5, 10, 20};
int* ptr = arr;
int size1 = sizeof(arr); // size1 = 12
int size2 = sizeof(ptr); // size2 = 4 (in 32-bit program)

 

<quiz2 - arr[i] == *(arr+i)>

 

#include <stdio.h>

int main()
{
  int arr[] = {5, 10, 15, 20};
  int* ptr = arr;
  printf("%d %d\n", *(arr+3), ptr[1]);
  return 0;
}
20 10

 

Call-by-value

 

 

void swap_wrong(int n1, int n2)
{
  int temp = n1;
  n1 = n2;
  n2 = temp;
 }
  
  int main()
 {
  int num1=10, num2=20;
  swap_wrong(num1, num2);

  return 0;
}
  num==10, num2==20

 

  • 함수가 호출될 때, 메모리 공간 안에서는 함수를 위한 별도의 임시 공간이 생성된다. (c++의 경우 stack frame) 함수가 종료되면 해당 공간은 사라진다.
  • 스택 프레임(Stack Frame) : 함수 호출 시 할당되는 메모리 블록(지역변수의 선언으로 인해 할당되는 메모리 블록)
  • call-by-value 값에 의한 호출 방식은 함수 호출 시 전달되는 변수의 값을 복사하여 함수의 인자로 전달한다.
  • 복사된 인자는 함수 안에서 지역적으로 사용되는 local value의 특성을 가진다.
  • 따라서 함수 안에서 인자의 값이 변경되어도, 외부의 변수의 값은 변경되지 않는다.

 

 

Call-by-reference

 

void swap(int* p1, int* p2)
{
  int temp = *p1;
  *p1 = *p2;
  *p2 = temp;
}
int main()
{
  int num1=10, num2=20;
  swap(&num1, &num2);

  return 0;
}
num==20, num2==10

 

  • 함수가 호출될 때, 메모리 공간 안에서는 함수를 위한 별도의 임시 공간이 생성된다. (예: stack frame) 함수가 종료되면 해당 공간은 사라진다.
  • call-by-reference 참조에 의한 호출 방식은 함수 호출 시 인자로 전달되는 변수의 레퍼런스를 전달한다. (해당 변수를 가리킨다.)
  • 따라서 함수 안에서 주소의 변수 값이 변경되면, argument로 전달된 해당 주소의  변수 값도 함께 변경된다.