본문 바로가기
Computer Science

[C언어] 배열과 포인터의 관계

by DuncanKim 2022. 7. 4.
728x90

[C언어] 배열과 포인터의 관계

 

배열과 포인터는 매우 긴밀한 관계를 맺고 있다. 어떤 부분에서는 서로를 대체할 수도 있다.

일반적으로 다른 언어를 배우면 배열이 다음과 같다는 것을 이미 알고 있을 것이다.

 

<자바>

int arr[5] = {1, 2, 3, 4, 5};

int b = arr[1]; // b는 2라는 값을 가지게 된다.

 

변수들이 모여 있는 것이 배열이 아닌가? 라고 생각할 것이다. 맞다. 그것도 옳은 말이다.

그런데 그 근본에는 '포인터'가 있다. 배열에 인덱스로 접근하는 자바의 문법도 '포인터'를 쉽게 사용하게끔 바꾸어 놓은 것에 불과하다.

 

아래에서는 포인터 상수 개념과 포인터로 배열에 접근하는 방법에 대해 알아볼 것이다.

 

 

1. 포인터 상수(배열의 이름)

 

포인터 상수(Constant Pointer)란 포인터 변수가 가리키고 있는 주소값을 변경할 수 있는 포인터를 의미한다.

배열의 이름은 그 값을 변경할 수 없는 상수라는 점을 제외하면 포인터와 같다. (위에서의 'arr')

 

 

// 문제 : 고객에게 숫자를 10개 입력받아서 배열에 넣기
// 조건 : 변수를 2개만 사용해주세요.
// 조건 : 포인터 참조 문법을 쓸 수 없습니다.


#include <stdio.h>

int main(void) {
	// arr은 포인터 상수이다.
	int arr[10];
 	
	for (int i = 0; i < 10; i++ ) {
		scanf("%d", &arr[i]);
	}
	
}

 

arr은 포인터 상수라고 이야기했는데, 그러면 arr이 포인터가 될 수 있다는 것이다.

만약, *arr = 1; 이라고 하면, arr이 첫번째로 가리키고 있는 arr[0]의 값이 변경되는 것이다.

마찬가지로 *(arr + 1) = 2; 라고 하면, arr[0] 그 다음에 있는 arr[1]의 값이 변경되는 것이다.

 

 

2. 포인터로 배열에 접근하는 방법

 

위에서 알아본 코드를 따와서 적용해보면 아래와 같다.

 

<포인터 and 배열>

arr[0] = *(arr + 0)
&arr[2] = arr + 2

**arr+i == &arr[i]
*(arr+i) == arr[i]

arr == &arr[0] //arr[0]의 메모리 주소를 뜻한다.
arr[0] == *arr == *(arr + 0)
arr[1] == *(arr + 1) 
&arr[0] == arr
&arr[1] == arr + 1  //arr[0]에서 다음 주소값, arr[1]의 메모리 주소

 

 

<활용 : 배열을 훼손하는 change 함수>

// 문제 : 배열을 훼손하는 change 함수를 만들어주세요.(배열의 포인터)

#include <stdio.h>

void change(int** p) {
  **p = 200;
  *(*p + 1) = 400;
}

int main(void) {
  int x[2] = {100, 200};
  // x : 배열변수
  // x의 값은 자동으로 x[0]의 주소값을 가진다.
  // x == &x[0]
  // x의 타입은 int* 이다.

  printf("change 함수를 호출하기 전, x[0] : %d, x[1] : %d\n", x[0], x[1]);

  int* p = x;
  change(&p);

  printf("change 함수를 호출한 후, x[0] : %d, x[1] : %d\n", x[0], x[1]);

  return 0;
}
출력
change 함수를 호출하기 전, x[0] : 100, x[1] : 200
change 함수를 호출한 후, x[0] : 200, x[1] : 400

 

<이중포인터를 사용한 사례>

// 문제 : 배열을 훼손하는 change 함수를 만들어주세요.(배열의 포인터)

#include <stdio.h>

void change(int** l) {
  // v1
  (*l)[0] = 200;
  // p[0] = 200;
  // x[0] = 200;
  
  (*l)[1] = 400;
  // p[1] = 200;
  // x[1] = 200;

  // v2
  *(*l + 0) = 200;
  // *(p + 0) = 200;
  // *(x + 0) = 200;

  *(*l + 1) = 400;
  // *(p + 1) = 200;
  // *(x + 1) = 200;

  // v3
  l[0][0] = 200;
  l[0][1] = 400;

  // v4
  *(l[0]) = 200;
  *(l[0] + 1) = 400;
}

int main(void) {
  int x[2] = {100, 200};
  // x : 배열변수
  // x의 값은 자동으로 x[0]의 주소값을 가진다.
  // x == &x[0]
  // x의 타입은 int* 이다.

  printf("change 함수를 호출하기 전, x[0] : %d, x[1] : %d\n", x[0], x[1]);

  int* p = x;

  printf("== 참고 ==\n");
  printf("x : %ld\n", (long)x);
  printf("p : %ld\n", (long)p);
  printf("&p : %ld\n", (long)&p);

  change(&p);

  printf("change 함수를 호출하기 전, x[0] : %d, x[1] : %d\n", x[0], x[1]);
  // 출력 => change 함수를 호출하기 전, x[0] : 200, x[1] : 400

  return 0;
}

 

<위의 코드 에러 발생 정리>

void change(int** arr) {
  *arr[0] = 200; // 정상 동작
   *arr[1] = 400; // **에러 발생
  (*arr)[1] = 400; // 정상 동작
}

**※에러 발생 이유**
(*arr)[1] -> arr가 가리키는 곳으로 먼저 간 후에 1칸 이동해라

*arr[1] -> 8바이트짜리를 가리키는 변수
			한칸 떨어뜨린 곳으로 이동 --> 8칸 이동

 

728x90

댓글