본문 바로가기
C언어/포인터

[C언어] 상수 포인터와 포인터의 연산(포인터 - 2)

by 프링글's 2022. 7. 5.

Ⅰ.상수포인터

우리는 절대 값이 변하지 않을 것 같은 변수상수로 만들어 주기 위해 const를 사용한다. const를 사용하면 변수를 상수로 만들어서 중간에 해당 변수의 값을 바꿀 수 없도록 만들어 줄 수 있다.

예를 들어

const int a = 3;
a = 4;

이 경우 오류가 발생한다. a는 상수라서 바꿀 수 없는 값인데 4로 바꾸려 시도했기 때문에 오류가 발생하는 것이다.

const int a = 3;
a = 3;

이 경우에도 물론 오류가 발생한다. a의 값은 바뀌지 않았음에도 바뀔 가능성이 있었기 때문에 오류가 생기게 되는 것이다. a에 무슨 값이 들어가는지는 상관이 없이 바뀔 가능성이 있다면 오류가 생긴다. 때문에 절대로 바뀌지 않을 값에는 항상 const를 붙여서 상수로 만들어주는 습관을 길러야 한다.

 

다시 돌아와서, 이러한 const를 우리는 포인터에도 붙여줄 수 있다. 이를 우리는 상수 포인터라고 한다. 상수포인터는 다음과 같이 사용할 수 있다.

#include <stdio.h>
int main() {
  int a;
  int b;
  const int* pa = &a;

  *pa = 3;  // 불가능
  pa = &b;  // 가능
  
  return 0;
}

const int* pa라는 부분은 *pa, 즉 pa가 가리키는 데이터의 값은 변해선 안된다는 의미이다. 그러면 pa가 가리키는 데이터는 a이니까 a를 바꾸려고 하면 오류가 뜰까?

신기하게도 오류가 뜨지 않는다. 그 이유는 a가 const int형이 아닌 그냥 int형으로 선언되었기 때문이다. 쉽게 말해, *pa는 상수이지만 a는 상수가 아니라는 말이다. 또한 *pa는 상수이지만 pa는 상수가 아니기 때문에 pa가 무엇을 가리키는지는 충분히 변할 수 있다. 말이 좀 어려워보일 수 있지만.... 정리하자면

int a = 1;
int b = 2;
const int *pa = &a;

a = 3	//가능! pa가 a를 가리키므로 *pa도 3
*pa = 2 //불가능! *pa는 상수이므로 오류가 뜸
pa = &b //가능! 여기부턴 pa가 b를 가리킴. *pa도 b의 값인 2
*pa = 3 //불가능! *pa는 상수이므로 오류가 뜸

한마디로 상수로 선언되지 않은 a, b, pa들을 충분히 바꿀 수 있지만 상수로 선언된 *pa를 이용해 값을 바꾸려하면 오류가 생긴다는 의미이다.

 

그럼 이번에는 위의 코드를 살짝 바꾸어보자.

#include <stdio.h>
int main() {
  int a;
  int b;
  int* const pa = &a;
  
  *pa = 3;		//가능
  pa = &b;		//불가능
  
  return 0;
}

이번엔 const가 맨 앞이 아닌 int*과 pa 사이에 위치하고 있다. 이 경우는 상수로 선언하고 싶은 변수가 *pa가 아니라 pa인 상황인 것이다. 즉, pa가 가리키는 데이터의 값은 바꿔도 무방하지만 pa가 무엇을 가리키는지는 바꿀 수 없다.

int a = 1;
int b = 2;
int* const pa = &a;

a = 3;		//가능! *pa도 3
*pa = 2;	//가능! a도 2
pa = &b;	//불가능! pa는 상수

위의 내용을 하나로 합쳐보면

const int* const pa = &a;

위 경우에는 pa도 *pa도 바꿀 수 없다. 물론 a는 int로 선언됐기 때문에 바꿀 수 있다.

 

 


Ⅱ.포인터의 연산

신기하게도 포인터도 덧셈, 뺄셈과 같은 연산이 가능하다. 우선 아래 코드를 보자

#include <stdio.h>
int main(){
    int a;
    int *pa = &a;
    
    printf("%p\n", pa);
    printf("%p", pa+1);
    
    return 0;
}

이를 실행 시키면 다음과 같은 결과가 나온다.

000000000062FE14
000000000062FE18

결과값이 뭔가 이상하다! 분명 난 pa에 1을 더했는데 왜 결과를 확인해보니 4가 더해져 있을까?

그러고 보니 4라는 숫자가 뭔가 익숙하다. 분명 int형이 4바이트였고, int형 데이터를 가리키는 포인터에 1을 더했더니 4가 더해져서 출력이 된다. 이것이 1을 더했음에도 결과는 4가 더해진 이유이다.

포인터의 덧셈, 뺄셈을 할 때에는 더하거나 빼려는 숫자 그대로가 더해지는 것이 아닌 포인터가 가리키는 데이터의 크기만큼 곱해서 연산된다. 즉, 따라서 위 코드에서 pa+2를 출력하면 000000000062FE14+(4*2)가 된 000000000062FE1C가 출력된다.

그럼 int가 아닌 다른 형태의 포인터들도 이런 식으로 실행해보자

#include <stdio.h>
int main() {
  int a;
  char b;
  double c;
  int* pa = &a;
  char* pb = &b;
  double* pc = &c;

  printf("%p %p\n", pa, pa+1);
  printf("%p %p\n", pb, pb+1);
  printf("%p %p\n", pc, pc+1);

  return 0;
}

실행하면 다음과 같은 결과가 나온다.

000000000062FE04 000000000062FE08
000000000062FE03 000000000062FE04
000000000062FDF8 000000000062FE00

보이는 것과 같이 int 포인터는 4가 더해졌고, char 포인터는 1, double 포인터는 8이 더해진 걸 확인할 수 있다.

 

그럼 다음과 같이 포인터와 포인터를 더하면 어떻게 될까?

#include <stdio.h>
int main(){
    int a;
    int b;
    int *pa = &a;
    int *pb = &b;
    
    printf("%p", pa+pb);
    
    return 0;
}

아쉽게도 위와 같이 포인터와 포인터를 더하려하면 오류가 뜬다. 두 포인터를 더해봤자 아무런 의미가 없는 메모리의 지점이 나오게 되기 때문이다.

 

그럼 여기서 하나 더, 포인터와 포인터를 빼는 것은 어떨까? 덧셈도 안되는데 어떻게 뺄셈이 되겠냐고 생각할 수 있지만 놀랍게도 포인터끼리의 뺄셈은 가능하다.

#include <stdio.h>
int main(){
    int a[3] = {1,2,3};
    int *pa0 = &a[0];
    int *pa2 = &a[2];
    
    printf("%d", pa2-pa0);
    
    return 0;
}

위 코드를 실행하면 어떤 결과가 나올까? 배열의 각 원소들은 메모리 상 붙어있으므로 a[0]와 a[2]는 4바이트*2인 8바이트 차이가 날 것이다. 그래서 아마도 8이 출력될 거야! 라고 생각하고 실행을 시키면 다음과 같은 결과가 나온다.

2

왜일까? 그 이유는 포인터의 뺄셈두 주소 사이에 데이터가 몇개 있는지 알아내는 연산이기 때문이다.

물론 pa2와 pa0의 차는 8이 맞지만 pa2-pa0은 (pa2-pa0)/sizeof(int)로 처리된다. 따라서 8/4인 2가 출력된 것이다.

또한 pa2-pa0는 더이상 주소값이 아닌 정수이므로 정수형 변수에 담아주어야 한다.

int *pa0 = &a[0];
int *pa2 = &a[2];

int count = pa2-pa0;	//포인터-포인터는 정수

 

추가로 a=b;와 같은 연산이 가능한 것처럼 당연하게도 포인터 pa = pb;와 같이 pb에 있는 주소값을 pa에 넣어주는 것도 가능하다. 단, 두 포인터의 형(type)이 같아야 함을 주의해야한다.

int a;
int b = 3;
a = b; 		//가능!

int *pa;
int *pb = &b;
pa = pb;	//가능!

double *pc;
pc = pb;	//불가능!

 

 

(위 포스트는 https://modoocode.com/24를 참고하여 제가 이해한대로 작성하였습니다. 틀린 내용은 지적바랍니다.)

 

씹어먹는 C 언어 - <12 - 2. 포인터는 영희이다! (포인터)>

 

modoocode.com

 

댓글