본문 바로가기

C Language

1. 포인터의 기본

//////KOCW에서 김승태 교수님의 16년도 1학기 "고급 C 프로그래밍" 강의를 참고하였습니다.//////

더보기

다가오는 1학기에 알고리즘 수업을 듣기 위해 수강하지 못했던 자료구조를 독학하기로 했다.

하지만 자료구조를 제대로 이해하기 위해선 C언어에서 포인터와 구조체에 관해 확실히 이해해야 하기 때문에 C부터 다시 공부를 시작한다.

또한 평소에동적할당같은 개념도 대충만 알고 넘어가서 이번 기회에 제대로 배워보고자 한다.

서두가 길고 바로 포인터의 기본에 대해서 공부해보자.


폰 노이만 구조

포인터를 제대로 이해하기 위해선 기본적인 폰노이만 구조 컴퓨터에서 메모리를 이해해야 한다.

간단하게 폰 노이만 구조를 설명하자면,

"메모리에서 명령어와 데이터를 불러온 후, CPU에서 명령을 수행하고 다시 그 값을 메모리에 저장하는 것"

<폰 노이만 구조에서 주목해야 할 점>

  1. 컴퓨터 구조(Low Level)에서 메모리의 한 공간은 C언어 측면(High Level)에서 "변수"라는 개념으로 설명하는 것이다.
  2. 메모리는 바이트 단위로 주소가 정해져 있다

포인터란?

포인터란? --> "메모리의 주소이다."

폰 노이만 구조를 설명한 이유가 여기에 있다. Low Level에서 CPU는 모두 주소값을 이용해서 명령어(instructions)와 데이터에 접근한다. 따라서 C언어(High Level)에서 포인터를 다룬다는 것은 비교적 Low Level의 관점에서 프로그래밍 하겠다는 의미이다.


포인터 변수의 선언

변수를 선언한다는 것은 메모리에 어떤 data를 저장하기 위한 공간을 할당하는 것을 의미한다.

int *ptr; // 포인터를 저장하는 변수 ptr 선언

다음과 같이 포인터 변수 ptr을 선언하면 메모리 공간에 포인터를 저장하기 위한 공간을 할당한 것이다.

 

앞에 int 는 포인터를 따라가면 그 주소에 저장된 data가 int형이라고 알리는 것

 

언제나 data가 가장 중요한 것이란 점을 잊어선 안된다.


포인터 변수의 크기(몇 바이트?)

포인터 변수는 주소값

따라서 주소가 몇 bit로 이루어졌나? = 포인터 변수의 크기

 

32bit 컴퓨터는 32bit instruction size (주소 bit수)

64bit 컴퓨터는 64bit instruction size

위의 구조에서 cpu와 메모리 모두 32bit이거나 64bit라는 말

 

더보기

요즘 우리가 사용하는 컴퓨터는 거의 64bit 컴퓨터와 OS를 사용하지만 visual studio가 32bit 빌드 플랫폼으로 동작하는 것이 일반적

(32bit 시스템은 64bit 컴퓨터에서 돌아가지만 64bit 시스템은 32bit에서 돌아가지 않기 때문)

더보기

32bit instruction --> 2^32 개(byte)의 주소공간을 표현 가능 -->

                                                  --> 2^32byte = 4G (기가) --> "최대 메모리 4GB"


64bit instruction --> 2^64 개(byte)의 주소공간을 표현 가능 -->

                                                 --> 2^64byte = 16T (테라) --> "최대 메모리 16TB"


& (주소 연산자)

  • & 뒤에는 변수만 올 수 있다
  • & 뒤에 오는 변수의 메모리 주소를 의미한다
  • 자료형은 "int 포인터 형"

* (역참조 연산자)

  • * 뒤에는 포인터 변수만 올 수 있다. (주소를 직접 입력도 하지만 일반적이지 않음)
  • * 뒤에 오는 포인터 변수의 주소를 보고 그 주소의 data를 가져온다.
#include <stdio.h>

int main(void) {

	float f;
    float *ptr = &f;
    
    printf("%f\n", *ptr);
    return 0;
}

 

 코드 내용을 그림으로 표현하면 다음과 같다.

따라서 1.234를 출력하게 된다.

 

주의해야 할 점은 포인터 변수를 선언할 때 자료형을 맞추어서 선언해야 한다는 것이다. (이 코드 예제에서 float에 해당)

 

예를 들어 int형 과 float형은 하드웨어적으로(?) data를 저장하는 방식이 완전히 다르므로 혼동하여 사용할 경우 무조건 경고가 나온다.


포인터 연산자 (*,&)를 이용한 코딩 예제

#include <stdio.h>
int main(void)
{
	int value = 1234;
	int *ptr;

	ptr= &value;
	printf("%d\n", *ptr);

	value++;
	(*ptr)++;

	printf("%d\n", *ptr);
	return 0;
}

변수 value를 선언하고 1234로 초기화 한 후.

증감 연산자를 이용하여 1235가 된다.

다음에 (*ptr)++로 인해 value는 "1236"이 된다. 

이곳에서 알 수 있다시피, value에서 사용하는 연산을 모두 *ptr에서 사용할 수 있다.

더보기

(*ptr)++ 에서 괄호()를 사용한 이유는 다음과 같다.

C언어 연산자의 우선순위(priority)

"후치 증감연산자"는 "포인터 연산자 *"보다 우선 순위가 높기 때문에

괄호 없이 *ptr++ 처럼 사용한다면 *(ptr++)과 같이 동작한다. 

 

data값(*ptr)이 아니라 주소값(ptr)을 증가 시키기 때문에 완전히 다른 결과를 출력한다.

 

다음으로,

#include <stdio.h>

int main( )
{
   int *ptr= 0 ;
   int a = 0 ;

   a = *ptr;
   printf("%d\n", *ptr);
    
   return 0;
}

컴파일하면 오류가 발생한다.

 

이유는 이 프로그램이 사용하지 않는 메모리 영역 (0번지)을 사용했기 때문이다.

 

따라서 포인터 변수를 초기화 할 때는 주의해야 한다. 우리가 사용하는 메모리 영역만을 이용해야 하기 때문이다.