Post

C언어 :: 포인터와 함수에 대한 이해

C언어 :: 포인터와 함수에 대한 이해

C언어의 포인터와 함수에 대한 이해도를 높일 수 있는 글입니다.

함수의 인자

함수 호출 시 전달되는 인자의 값은 매개변수에 ‘복사’된다. 인자 자체가 전달되는 것이 아니라 값이 전달된다는 의미이다. 또한 함수의 인자로 배열을 전달하는 것은 불가능한데, 함수의 매개변수로 배열을 선언할 수 없기 때문이다. 대신 함수 내에서 배열에 접근할 수 있도록 배열의 주소 값을 전달하는 것은 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>

void showArrayElem(int *param, int len) {
  int i;
  for (i = 0; i < len; i++) {
    printf("%d ", param[i]);
  }
  printf("\n");
}

int main() {
  int arr1[3] = {1, 2, 3};
  int arr2[5] = {4, 5, 6, 7, 8};

  showArrayElem(arr1, sizeof(arr1) / sizeof(int));
  showArrayElem(arr2, sizeof(arr2) / sizeof(int));

  return 0;
}


위와 같이 배열의 주소를 매개변수로 전달하는 것이 가능하기 때문에 값의 변경 또한 가능하다.

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
#include <stdio.h>

void showArrayElem(int *param, int len) {
  int i;
  for (i = 0; i < len; i++) {
    printf("%d ", param[i]);
  }
  printf("\n");
}

void addArrayElem(int *param, int len, int add) {
  int i;
  for (i = 0; i < len; i++) {
    param[i] += add;
  }
}

int main() {
  int arr1[3] = {1, 2, 3};

  showArrayElem(arr1, sizeof(arr1) / sizeof(int));

  addArrayElem(arr1, sizeof(arr1) / sizeof(int), 1); 
  showArrayElem(arr1, sizeof(arr1) / sizeof(int));

  addArrayElem(arr1, sizeof(arr1) / sizeof(int), 2); // 변경
  showArrayElem(arr1, sizeof(arr1) / sizeof(int));

  return 0;
}


Call-by-value, Call-by-reference

함수를 호출할 때 단순히 값을 전달하는 형태의 함수호출을 가리켜 Call-by-value라 하고, 메모리의 접근에 사용되는 주소 값을 전달하는 형태의 함수호출을 가리켜 Call-by-reference라 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>

void swap(int *ptr1, int *ptr2) {
  int temp = *ptr1;
  *ptr1 = *ptr2;
  *ptr2 = temp;
}

int main(void) {
  int num1 = 10;
  int num2 = 20;

  printf("num1: %d \n", num1);
  printf("num2: %d \n\n", num2);

  swap(&num1, &num2);

  printf("num1: %d \n", num1);
  printf("num2: %d \n", num2);

  return 0;
}

위 결과를 통해 scanf 함수호출시 & 연산자를 붙였던 이유를 파악할 수 있다. 변수 num에 값을 채우기 위해 num의 주소 값이 필요하기 때문에 & 연산자를 사용하는 것이다. 다만 문자열을 입력 받을 때에는 & 연산자를 사용하지 않는데, 그 이유는 문자열이 그 자체로 배열의 주소 값이기 때문이다.


포인터 대상의 const 선언

포인터 변수에 const 키워드를 사용할 수 있으며, 사용할 경우 해당 포인터 변수가 가리키는 변수에 저장된 값이 변경 불가능해 진다. 그러나 변수가 상수화되는 것은 아니기 때문에 변수의 값을 직접 변경하는 것은 가능하다. 단지 값을 변경하는 방법에 제한을 두는 것이다.

1
2
3
4
5
6
int main() {
  int num = 20;
  const int * ptr = &num;
  *ptr = 30; // 컴파일 에러
  num = 40; // 컴파일 성공
}


const 키워드를 맨 앞이 아닌 포인터 변수명 앞에 선언할 경우 해당 포인터 변수에 한번 주소 값이 저장되면 더이상 다른 주소 값으로 변경할 수 없게 된다. 즉 한번 가리키기 시작한 변수를 끝까지 가리켜야 한다. 단 해당 포인터변수가 가리키는 대상에 저장된 값을 변경하는 것은 가능하다.

1
2
3
4
5
6
7
int main() {
  int num1 = 20;
  int num2 = 30;
  int * const ptr = &num1;
  *ptr = &num2; // 컴파일 에러
  *ptr = 30; // 컴파일 성공
}


물론 다음과 같이 하나의 포인터 변수를 대상으로 const 선언을 동시에 할수도 있다.

1
2
3
4
5
int main() {
  const int * const ptr = &num1;
  *ptr = &num2; // 컴파일 에러
  *ptr = 30; // 컴파일 에러
}
This post is licensed under CC BY 4.0 by the author.