프로그래밍/C

[C언어 강좌-11] 포인터 (Pointer)

Peter Ahn 2018. 5. 23. 22:19
반응형

 

 

개요

 

안녕하세요 피터입니다.

오늘은 C언어의 포인터(Pointer)에 대해 알려드리겠습니다.

 

포인터는 C언어를 배우는 많은 사람들이 어려워 하는 개념 중에 하나입니다.

동시에 C언어에서 가장 중요한 개념 중 한가지이기도 합니다.

 

사실 포인터(Pointer)라는 것은 하나의 데이터 타입(DataType)일 뿐이며 실제 값(Value)를 저장하는 대신에

값이 저장되어 있는 변수의 주소(Address)가 저장된다는 것만 기억하면 어렵지 않습니다.

 

 

문법(Syntax)

 

포인터 타입 정의 (Definition)

 

포인터 타입을 정의할 때는 기존에 정의된 데이터 타입 뒤에 * (Asterisk) 를 붙이면 됩니다. (애스터리스크 라고 발음합니다)

예를들면 다음과 같이 정의할 수 있습니다.

 

int*
char*
float*
double*
struct node*

 

기본형 데이터 타입 외에도 파생형 타입도 포인터 타입으로 만들 수 있습니다.

[C언어 강좌-4] 자료형 (DataType)

 

모든 포인터 타입의 변수에는 주소값이 저장되기 때문에 포인터 변수가 차지하는 메모리 크기는 모두 같습니다.

그렇기 때문에 원래 1 Bytechar 형의 데이터 타입도 char* 포인터 타입이 되면 4 Bytes의 크기를 갖게 되는 것입니다.

마찬가지로 8 Bytes의 크기를 차지하는 double의 경우에도 double* 포인터 타입이 되면 4 Bytes가 됩니다.

굉장히 중요하고 헷갈리기 쉬운 부분이니 잘 기억해두세요!

 

 

포인터타입으로 변수 선언 (Declaration)

 

포인터 타입으로 변수를 선언하는 방법은 기존의 변수 선언하는 방법과 동일합니다.

int* a; // or int *a;

 

이렇게 선언된 포인터 변수 a 는 4 Bytes 만큼 메모리 공간을 할당받게 되고 이 공간에 주소값을 저장할 수 있습니다.

일반적으로 변수를 할당하고 아무런 값을 셋팅하지 않으면 가비지(Garbage)값이 들어있기 때문에 초기화를 해줘야 합니다.

 

포인터 변수를 초기화 할 때는 보통 NULL (널) 값으로 초기화 합니다.

이것은 나중에 포인터 변수에 주소값이 할당되었는지 체크하기 위해서 아무런 주소값이 할당되지 않았다는 의미를 갖게됩니다.

따라서 포인터 변수에 접근(access)하기 위해서는 반드시 NULL 인지 체크하는 루틴이 필요합니다.

 

int* a = NULL;
if(a != NULL)
  printf("%d", *a);

NULL이 들어있는 포인터 변수에 접근하게 되면 NullptrException 이라는 무시무시한 에러를 만나실 수 있으니 이 부분은 습관처럼 익숙해지셔야 합니다.

 

NULL은 C에서 일반적으로 0으로 define 되어 있는데 메모리 주소 체계에서 0번지는 시스템 영역이므로 이러한 접근을 차단하기 위해서 Exception(예외)을 발생시키는 것입니다.

 

포인터 변수에 접근할 때에는 당연하게도 주소를 다루기 때문에 일반적인 변수와는 조금 다르게 취급해야 되겠죠?

포인터 변수에 값을 저장하거나 불러올 때는 아래와 같이 특별한 연산자가 사용됩니다.

 

 

& 연산자 (Operator &)

 

int b = 100;
a = &b;

& (Ampersand) 연산자는 변수 앞에 붙는 단항연산자입니다.

(앰퍼샌드라고 발음합니다)

이 연산자는 뒤에오는 변수의 주소값을 반환합니다.

따라서 위 코드의 의미는 int 형 변수 b 의 주소를 얻어서 포인터 변수 a 에 저장한다는 의미입니다.

 

 

* 연산자 (Operator *)

 

printf("address: %d, value: %d\n", a, *a);

* 연산자는 & 연산자와 마찬가지로 단항연산자이지만 정반대의 동작을 수행합니다.

뒤에 오는 포인터변수가 가리키고 있는 주소에 저장되어 있는 실제 값(Value)를 반환합니다.

 

따라서 포인터 변수 a에 int 형 변수 b의 주소가 저장되어 있다면 *a 는 b에 저장되어 있는 값을 의미합니다.

 

ex) 포인터 변수 a 에 int 형 변수 b의 주소를 저장한 뒤 주소값과 실제값을 출력하는 코드

int* a = NULL; // or int *a;
int b = 100;
a = &b;

printf("address: %d, value: %d\n", a, *a);

 

참고

 

Call by reference

 

이후 함수(function) 포스팅에서 자세히 다루게 될 내용이지만 간략하게 소개드리겠습니다.

함수의 매개변수(Parameter)를 포인터 타입으로 사용하게 되면 해당 함수의 외부에서 선언된 변수에 직접 접근하여 값을 저장할 수 있습니다.

따라서 Call by value 방식과는 다르게 함수 호출이 끝나고 return 되어도 포인터 타입으로 선언된 매개변수를 이용해 저장된 값은 그대로 유지가 된다는 의미입니다.

 

배열과 포인터 (Pointer and Array)

 

[C언어 강좌-10] 배열 (Array)

이전 포스트에서 배열의 이름만 쓰게 되면 첫 번째 요소의 주소의 의미가 된다고 말씀드린 적이 있습니다.

 

int cost[10];

즉, 위와 같이 선언된 배열이 있다고 한다면 cost 라고만 쓰면 &cost[0] 과 의미가 같다는 뜻입니다.

내부적으로 배열의 인덱스 연산자 [ ] 도 첫 번째 요소의 포인터 값에 [ ] 안에 들어있는 숫자 만큼 더한다는 의미이기 때문에 배열과 포인터는 매우 밀접한 관련이 있다고 할 수 있습니다.

 

 

-Peter의 우아한 프로그래밍

여러분의 공감과 댓글은 저에게 크나큰 힘이 됩니다. 오류 및 의견 주시면 감사하겠습니다.

 

 

반응형