본문으로 바로가기
homeimage
  1. Home
  2. 컴퓨터/프로그래밍
  3. C언어 포인터 쉽게 이해하기 2부

C언어 포인터 쉽게 이해하기 2부

· 댓글개 · 바다야크

C언어 포인터 이해하기 1부에 이어 2부 글입니다. 이전에는 배열과 비교하여 포인터에 대해 알아 보았는데요, 이번에는 C언어의 포인터에서 아리까리 헷깔리는 부분에 대해서 알아보겠습니다. 이 글도 포인터를 학습 수준에서 알기는 아는데 정확히 모르는 분을 위한 내용입니다.

C언어 포인터 변수 타입별 차이

C언어의 포인터는 주소를 갖는 정수형 변수이지만, 다른 변수처럼 char, short, int, long 등으로 선언합니다. 그렇다면 char *ptr1하고 int *ptr2하고 어떤 점이 다를까요? ptr1보다 ptr2의 변수 크기가 더 클까요?

C언어는 변수 사용이 다른 언어에 비해 매우 자유로운데요, C언어로 코딩하다가 다른 언어로 바꾸면 한동안 어려움을 느낄 정도입니다. 다른 언어는 문자 변수로 선언하면 문자만 대입해야 합니다. 숫자를 바로 대입할 수 없지요. 그러나 C언어는 가능합니다.

   char c_value = 5;
   int  n_value = 10;
   printf( "%d %d\n", c_value, n_value);

컴파일하면 경고 메시지도 없고 실행도 잘 됩니다. int 변수에 문자 대입도 가능합니다.

   char c_value = 5;
   int  n_value = 'A';
   printf( "%d %c\n", c_value, n_value);

역시 경고 메시지 없이 잘 컴파일되고 실행도 됩니다.

C언어 변수 타입별 차이

그렇다면 C언어에서 char, short, int, long은 왜 만들었을까요? C언어에서는 각 변수별로 문자 변수, 숫자 변수로 나누기 보다는 변수가 점유하는 바이트 크기를 결정한다고 생각하는 것이 좋습니다.

char은 1 byte 크기 변수, short는 2byte 크기 변수, int는 4 byte 크기 변수, long은 64bit 시스템에서는 8byte 크기 변수입니다.

   char c_value = 256;       // 0x100
   int  n_value = 4294967296;  // 0x100000000
   printf( "%d %d\n", c_value, n_value);

그래서 위 프로그램을 실행하면 둘 다 0으로 출력됩니다. 

포인터 변수는 어떻게 될까요? char *ptr1으로 선언하면 ptr1의 변수 크기는 1byte이고 int *ptr2라고 하면 ptr2의 변수 크기는 4일까요? 아닙니다. char *ptr1이든 int *ptr2, long *ptr3 모두 32bit에서는 4바이트 크기, 64bit에서는 8바이트 크기의 변수입니다.

C언어 변수 타입별 포인터 변수

char 변수는 char *로, int 변수는 int *로 처리하는 것처럼 변수 타입에 따라 char, short, int, long으로 선언한다고 단순하게 이해하셨을지 모르겠습니다. 맞는 말이지만, 조금 더 깊이있게 알아 보겠습니다.

char ary[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char *ptr  = ary;
printf( "%c\n", *(ptr +0));
printf( "%c\n", *(ptr +1));
printf( "%c\n", *(ptr +2));
printf( "%c\n", *(ptr +3));

위 프로그램을 실행하면 아래와 같이 출력됩니다.

A
B
C
D

ptr 변수에는 ary[0]의 주소 값이 들어가고, 이 주소 값에 0, 1, 2, 3을 차례로 더한 후 그 위치의 값을 출력한 것입니다. 당연히 'A', 'B', 'C', 'D'가 출력됩니다.

char ary[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int *ptr  = ary;
printf( "%c\n", *(ptr +0));
printf( "%c\n", *(ptr +1));
printf( "%c\n", *(ptr +2));
printf( "%c\n", *(ptr +3));

char *ptr을 int *prt로만 바꾸었을 뿐입니다. 실행하면 어떤 값이 출력될까요? 이번에도 'A', 'B', 'C', 'D'가 출력될까요?

A
E
I
M

'A', 'E', 'I', 'M'이 출력됩니다. ptr 주소 값에 0, 1, 2, 3 차례로 더했는데 왜 이런 차이가 날까요? 그것은 ptr 포인터 변수는 int형으로 선언했기 때문에 ptr 변수와의 계산은 int 형 변수의 크기 즉, 4 크기로 계산하게 됩니다. 그러므로 ary[0]의 주소가 100이었고, ptr에 100 값이 대입되었다면, (ptr+1) 값은 1004가 됩니다. (ptr+1) 값은? 1008이 되고요.

char ary[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
long *ptr  = ary;
printf( "%c\n", *(ptr +0));
printf( "%c\n", *(ptr +1));
printf( "%c\n", *(ptr +2));
printf( "%c\n", *(ptr +3));

int *ptr을 long *ptr로 바꾸었다면, 그리고 64bit 시스템에서 실행하면, 'A', 'I', 'Q', 'Y'가 출력됩니다. 1씩 증가하는 연산자 ++를 사용해도 마찬가지입니다.

char ary[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
long *ptr  = ary;
int  ndx;

for ( ndx = 0; ndx < 4; ndx++){
   printf( "%c\n", *ptr++);
}

역시 'A', 'I', 'Q', 'Y'가 출력됩니다.

 

C언어 컴파일 에러보다 경고가 더 무서운 이유

이렇게 변수 형에 따라 크기에 맞추어 포인터의 값이 계산되는 것은 C언어의 배려입니다. 만일 C언어에서 이렇게 처리해 주지 않는다면 주소 계산에서 변수의 크기를 직접 계산해 주어야 합니다.

int   ary[] = {100, 101, 102, 103, 104, 105, 106};
int *ptr  = ary;
int  ndx;

for ( ndx = 0; ndx < 4; ndx++){
   printf( "%d\n", *ptr);
   ptr += sizeof( int);   // 만일 포인터 계산을 무조건 1 단위로 계산한다면
      또는
   ptr++;
   ptr++;
   ptr++;
   ptr++;
}

이렇게 말이죠.  그러나 C언어는 포인터의 변수 형에 따라 그 크기에 맞추어 계산합니다.

  • char *는 +1 이 +1
  • short *는 +1이 실제로는 +2로
  • int *는 +1이 실제로는 +4로 말이죠.
  • long *는 64bit 시스템에서는 +1이 실제로는 +8로 계산됩니다.
void print_value( int *ptr, int sz_data){

   int ndx;

   for ( ndx= 0; ndx < sz_data; ndx++){
      printf( "%d\n", *ptr++);
   }
}

int main( void)
{
   char   ary[] = {100, 101, 102, 103, 104, 105, 106};

   print_value( ary, sizeof( ary));

위 프로그램을 보면 char 배열을 인수로 넘겨 주었는데, print_value() 함수는 int 포인터로 받았습니다. 변수 타입이 맞지 않지만, 경고(warning)가 떠도 컴파일되고 실행도 됩니다. 실행하면 당연히 엉뚱한 결과를 내놓습니다.

아이~ 누가 이렇게 작성하나? 하시겠지만, 이런 실수가 적지 않습니다. 위의 예제는 짧은 프로그램에다가 int와 char가 다르다는 점이 눈에 확연하고 컴파일러가 warning으로 알려 주기까지 하니 이런 실수를 하지 않겠지만, struct로 여러 가지 자료형을 만들고, 프로그램 소스를 여러 개의 모듈 파일로 나누면서 좀 더 큰 프로그램을 작성하다 보니 warning이 여기 저기서  주르룩 나오는데, 거기다가 피곤함에 지치다 보면 제대로 확인하지 못하고 엉뚱한 곳에서 헤맬 수 있습니다.

이런 이유로 컴파일 에러보다 경고 메시지를 더 무서워하는 개발자가 많습니다. 무시해도 되는 경고 메시지도 반드시 제거해야 속이 편한 개발자도 있습니다.

void print_value( int ary[10]){

   int ndx;

   for ( ndx= 0; ndx < 10; ndx++){
      printf( "%d\n", ary[ndx]);
   }
}

int main( void)
{
   int  ary[10] = {100, 101, 102, 103, 104, 105, 106};

   print_value( ary);

이번 글에서는 char *ptr과 int *ptr의 차이점에 대해서 알아 보았습니다. 다음 글에서는 print_value( int ary[10]) 에서 숫자 10이 아무런 의미가 없는 이유를 알아 보겠습니다.

SNS 공유하기
💬 댓글 개
최근글
이모티콘창 닫기
울음
안녕
감사해요
당황
피폐

이모티콘을 클릭하면 댓글창에 입력됩니다.