그동안 C언어를 포스팅 해 오면서 키워드와 식별자, 변수와 상수, 제어문과 반복문, 배열을 차례대로 알아보았습니다. 그리고 많아 분들이 포기하는 부분중 하나인 포인터를 포스팅하려 합니다. 포인터는 C언어에서 빠질 수 없는 가장 중요한 부분 중 하나입니다. 하지만 매우 어려워들 하는 부분중 하나이기도 합니다.
이번 포스팅에서는 포인터의 기본 원리와 어떤식으로 사용되는지 알아보도록 하겠습니다.
포인터(Pointer)란 무엇인가
포인터는 메모리의 시작 주소입니다. 그동안 메모리라는 말을 대신해서 저장공간이라는 말을 사용해 왔습니다. 하지만 지금부터는 메모리 라는 말로 대체 해볼게요. 포인터의 개념을 표현하기 위해서는 메모리를 이해하고 메모리의 주소값에 대한 개념을 알아야 합니다.
예를들어서 다음과 같이 num이라는 변수를 정수형 자료형으로 선언했다고 가정하겠습니다.
int num;
지금까지 변수를 선언한 가장 기본적인 방법입니다. 이렇게 선언한 변수에는 4bytes 크기의 정수를 저장할 수 있습니다. 이렇게 저장되는 공간이 메모리입니다.
num 변수는 정수형 자료형인 int 로 선언되는 순간 4bytes 라는것에 결정되고 메모리 공간에 4bytes 크기의 공간이 "할당" 됩니다. 이렇게 할당된 메모리에 접근할수 있는 시작 주소를 우리가 선언한 변수 이름인 num에 연결시켜 줍니다.
여기서 이 주소를 우리는 "포인터" 라고 부르는 것입니다.
위 그림처럼 만약 할당된 주소가 100, 101, 102, 103, 104 라고 했을때 총 4bytes 크기가 되고 여기서 시작주소인 100이 num에 할당됩니다. 그래서 변수에 값을 넣어줄 때, num = 10; 라는 프로그램을 만들어주면 num이 가리키는 주소(포인터)를 찾아가서 그 곳에 10 이라는 값을 저장하는 것입니다. 여기서는 주소 100 을 찾아가는 것입니다.
주소 연산자 &
그럼 이러한 주소 100을 알수 있는 방법이 있을까요? 모르셨겠지만 앞선 C언어에서 이미 사용을 해 본적이 있습니다. 바로 & 연산자입니다. 기억나시나요? 표준입력함수인 scanf_s() 함수를 사용할 때 주소를 가리키는 & 연산자를 사용했었습니다. 다시 그림으로 보겠습니다.
정수형 변수인 num의 포인터는 주소 100이라 말씀드렸습니다. 그리고 이것을 포인터라고 했어요. 이때 포인터의 주소를 나타내는 방법은 &num 입니다. 실제로는 100 이라는 값은 아닙니다. 메모리에 할당된 주소가 나오게 되서 훨씬 큰 숫자가 나오게 되요. 이 부분은 소스코드를 통해서 알아볼 예정입니다.
위 그림 우측에 보시면 char ch; 라고 선언한 내용입니다. ch 는 문자 1개를 저장할 수 있는 자료형입니다. 그리고 이 역시 &ch 라고 포인터변수로 표현할 수 있습니다.
참조 연산자 *
참조 연산자는 포인터 변수임을 알려주는 연산자 기호입니다. 기호는 * 로 나타냅니다. 그리고 이 기호가 붙은 포인터 변수는 주소값을 저장하는데 사용합니다. 예를 들어보겠습니다.
* &num
주소 연산자 앞에 참조 연산자를 붙일 경우에는 이 주소가 가리키는 값을 의미합니다. 이제부터 & 연산자와 * 연산자를 혼동하는 경우가 발생합니다. & 연산자는 주소, * 연산자는 값을 나타내는 연산자입니다. 아래 포인터 변수에서 다시 한 번 더 자세하게 다뤄보도록 하겠습니다.
포인터 변수
주소 연산자와 참조 연산자를 알아보았기 때문에 이제는 프로그램에서 실제로 사용할 수 있어야 겠죠? 그래서 등장한 것이 "포인터 변수" 입니다. 포인터 변수는 위의 참조 연산자를 이용해서 만든 변수이며 항상 주소값을 저장하는데 사용되는 변수입니다. 포인터 변수는 아래와 같이 선언합니다.
int* pnum;
포인터 변수를 선언할 때에는 자료형과 변수명 사이에 * 를 사용합니다. 이는 Asterisk(애스터리스크)라고 부릅니다. 위와같이 포인터변수를 선언하면 pnum 포인터 변수는 주소를 저장할 수 있습니다. 그럼 여기에 주소를 연동해 보겠습니다.
int* pnum;
int num = 3;
pnum = #
포인터 변수 pnum과 정수형 변수 num을 선언하고 변수 num에는 3을 저장하도록 초기화 했습니다. 그리고 마지막에 변수 num의 주소를 나타내는 포인터 &num 을 pnum에 대입하였습니다.
이제 pnum 을 이용해서 값을 변경하고 저장할 수 있습니다. 포인터 변수를 이용해서 값을 출력하기 위해서는 참조 연산자인 * 을 이용하면 됩니다.
printf(*pnum);
포인터 변수 앞에 참조 연산자인 * 을 붙여서 *pnum 으로 표현하면 * &num 과 같은 의미입니다. 따라서 num에 저장된 값 3을 의미합니다.
포인터 변수를 선언하는 방법 3가지
포인터 변수는 위에서 자료형과 변수 사이에 참조 연산자 * 을 붙여서 사용한다고 했습니다. 이때 다음과 같이 띄어쓰기 문제가 발생합니다.
int* pointer;
int * pointer;
int *pointer;
결론은 세 가지 방법 모두 사용이 가능합니다. 어떻게 사용하든 모두 정상적으로 컴파일되고 실행됩니다. 다만 저의 경우는 자료형에 포인터 참조 연산자를 붙여서 보통 사용합니다. int* pointer; 와 같이 말이죠.
그럼 다음과 같이 선언하면 어떻게 될까요?
int* pointer, num;
정답은 pointer는 포인터 변수가 되고 num은 정수형 변수로 선언한 것입니다. 자칫 혼동될 수 있으니 주의하시기 바랍니다.
소소코드1 pointer.c
#include <stdio.h>
int main(int argc, char** argv)
{
int* pnum1;
int* pnum2;
int num1 = 10;
int num2 = 3;
pnum1 = &num1;
pnum2 = &num2;
printf("*pnum1 = %d\n", *pnum1);
printf("*pnum2 = %d\n", *pnum2);
num2 = 8;
printf("*pnum1 = %d\n", *pnum1);
printf("*pnum2 = %d\n", *pnum2);
};
결과1
*pnum1 = 10
*pnum2 = 3
*pnum1 = 10
*pnum2 = 8
정수형 포인터 변수 pnum1, pnum2 를 선언하고 정수형 변수 num1, num2 를 선언하였습니다. 그리고 정수형 변수는 각각 10, 3 으로 초기화를 해주었습니다.
pnum1 은 num1의 주소를, pnum2 는 num2 의 주소를 각각 할당해 주었습니다.
값을 출력하기 위해서 참조 연산자 * 을 이용해서 화면에 각각의 값을 출력하였고 값을 변경해도 정상적으로 출력되는지 확인해 보았습니다.
이번 포스팅에서는 포인터의 기본만 알아보았습니다. 포인터는 변수를 설정할 뿐만 아니라 배열과 함수, 그리고 가장 많이 사용되는 메모리를 동적으로 할 당하는 방법에 이용됩니다. 이 부분은 별도 추가적인 포스팅으로 설명드리도록 하겠습니다.
2020/10/26 - [쿤즈 Dev/C] - [C언어] 배열(Arrays) 사용 하는 방법
2020/09/25 - [쿤즈 Dev/C] - [C언어] 함수(Function) (1) 함수의 의미와 종류
2020/08/25 - [쿤즈 Dev/C] - [C언어] 반복문의 시작! - for 구문
2020/08/14 - [쿤즈 Dev/C] - [C언어] 조건문을 사용해서 분기시켜보자(2) - switch case 조건문!
2020/08/06 - [쿤즈 Dev/C] - [C언어] 표준입력 표준출력 사용하기 (첫 프로그래밍 시작!)
'쿤즈 Dev > C' 카테고리의 다른 글
[C언어] 포인터를 이용한 함수 사용 방법 (0) | 2020.12.16 |
---|---|
[C언어] 포인터를 이용해서 배열 사용하기 (2) | 2020.11.10 |
[C언어] 다차원 배열 (Multi-Demetional Arrays) 사용 방법 (0) | 2020.10.30 |
[C언어] 배열(Arrays) 사용 하는 방법 (0) | 2020.10.26 |
[C언어] 재귀함수! 반복문 없이 반복되는 함수 만들기 (0) | 2020.10.12 |
댓글