반응형

C언어를 공부하면서 가장 어려웠던 3가지 개념을 꼽으라면  동적메모리할당, 포인터, 구조체였던거 같습니다.

오늘 작성할 글은 포인터입니다. 저도 많이 부족하지만 포인터에 대한 간략한 이해를 도와보겠습니다.

 

포인터는 어떤 변수가 메모리 공간을 할당받고 저장되어있을 때, 그 변수가 살고있는 메모리 주소값을 가리키는 것입니다. 말 그대로 진짜 'Point!'인 셈이죠.  레이저 포인터할 때 그 포인터입니다.

 

우리가 어떤 변수의 초기값을 선언할 때는 항상 그 값들이 가지는 고유주소를 배정받게 됩니다. 이 주소의 값을 보는 방법은 printf를 통해 형식 지정자 "%p"를 사용하면 됩니다.

5라는 값은 현재 000000000062FE1C 라는 주소에서 살고 있군요.

 

* 주의사항 : 앰퍼샌드 & 기호는 두개를 쓰면 'AND'를 의미하는 논리연산자지만 하나만 쓴다면 해당 변수의 고유주소값을 호출하는 기능을 합니다. 위 소스코드를 보면 저는 number라는 변수의 주소값을 알기 위해 &number라 표현했습니다. 여기까진 이해될겁니다.

 

그럼 반대로 생각해볼 수도 있습니다. &가 변수가 가지는 고유주소를 호출하는 기능을 한다면 반대로 해당 주소에 살고 있는 변수값을 호출하는 것도 되지않나?

맞습니다. 그게 바로 * 연산자 입니다.

포인터 변수 pointer를 선언하고 &number로 초기화시켰습니다. 그리고 이에 대한 주소와 변수값을 출력했더니

위에 number의 주소와 변수값과 동일하게 나옵니다.

 

여기서 주목할 것은 4개의 printf들입니다.

일반 변수는 값을 호출할 때는 ("%d", 변수명) , 주소값을 호출할 때는 ("%p",&변수명)입니다.

근데 포인터 변수일 때는 반대입니다.

맨 위에 설명했듯이 포인터는 메모리 공간을 할당받은 변수가 살고있는 '주소값'을 가리키기 때문입니다.

따라서 주소값을 호출할 때는 ("%p",포인터변수명) , 값을 호출할 때는 ("%d" , *포인터변수명) 입니다.

 

&가 주소값을 알고싶을 때 쓰는거라면 *는 변수값을 알고싶을 때 쓰는 것이라 했으니까요.

그러면 위의 소스코드 결과를 한번 봅시다. 주소값 동일하구요. 같은 주소에 살지만 변수가 다르게 나왔습니다.

왜냐?

#include <stdio.h>

int main()
{
	int number = 5;  // 1. 처음에 number 변수에 5로 초기화했구요.
	printf("%p\n",&number);  // 2. number의 메모리 주소값을 호출
	printf("%d\n",number); // 3. number의 변수값 5를 호출
	
	int *pointer = &number;  // 4. 포인터변수 *pointer를 선언한 뒤 number의 
                             // 메모리 주소값으로 초기화
	*pointer = 3;  // 5. 포인터변수 값을 3으로 초기화
	printf("%p\n",pointer); // 6. pointer가 가리키는 메모리 주소는 number의 메모리 주소와 동일
	printf("%d",*pointer);  // 7. 4번이 실행되었으니 포인터변수를 통해 기존의 값 5에서 3으로 
                            // 변경한 것이 출력됨.
	
	return 0;
}

이렇기 때문입니다.

 

그럼 이쯤에서 포인터를 왜 배우며, 왜 쓰는지 생각해볼때가 온거 같습니다.

예시를 들겠습니다.

같은 수업을 듣는 학생 3명 A,B,C가 있다 합시다. 이 중 A는 공부를 잘해서 수업 중에 배운 내용을 잘 요약정리했어요. 그래서 친구인 B,C를 도와주기 위해 본인이 공부한 내용을 다시 또 종이에 써서 각각 나눠줬습니다.

그냥 교재의 몇페이지, 몇페이지 부분이 중요하니 그 부분들을 자세히 읽어봐라. 하면 될걸 말이죠.

 

컴퓨터도 똑같습니다. 일일이 함수를 만들어주면서 거기에 매개변수를 전달해서 실행하게 하면, 직관적으로는 괜찮아보이지만 함수가 사용하는 메모리도 발생하게 되고  프로그램을 수행하는데 있어서도 처리속도가 증가할 수 밖에 없습니다. 즉, 위 A학생이 종이를 쓴 것은 메모리 사용을 의미하구요. 공부한 내용을 종이에다 쓰는 행동은 프로그램을 수행하는 것이라 보면 됩니다. 딱봐도 시간 & 공간 다 불필요한 낭비죠.

교재의 N페이지를 읽어봐라. 한 마디면 시간절약 공간절약이니까요.

 

그래서 포인터를 쓰는 겁니다.  여기까진 이해 잘 되었으리라 생각합니다.

뒤에 문자열을 설명할 때도, 동적메모리를 설명할 때도 쭉 포인터를 같이 설명할테니  여기선 마지막으로 1차원 배열과 포인터의 관계에 대해 적고 끝내겠습니다.

 

배열은 변수들이 모여있는 하나의 아파트라 보면 되었습니다.

int num[6] = {0,1,2,3,4,5}는  num이라는 아파트에 0 , 1 , 2 , 3 , 4 , 5 라는 사람들이 살고 있습니다. 근데 다 같이 사는게 아니라 각 호수에 1명씩 들어가 살고 있다 보시면 됩니다.

왜냐하면, 배열은 변수의 수에 따라 메모리 공간을 할당해주는 것이기 때문에 이들은 모두 다 다른 메모리주소값을 갖고 있습니다.

다 다르죠? 근데 신기한게 뒷자리 숫자가 4씩 증가하는거 같아보입니다. 따라서 추측하건데 num은 int 자료형으로 선언되었기에 4Byte씩 할당되었음으로 파악됩니다.

이걸 주목해야해요.

첫번째 인덱스의 주소만 알면 그 뒤의 인덱스들의 주소는 자연스럽게 파악이 되거든요.

즉, 포인터 연산을 통해 배열의 데이터 주소들로 접근이 굉장히 용이해진다는 점. 이 개념까지 갖고 가시면 되겠습니다.

 

좀 더 이어서 쓰면 글이 길어질 것 같아 여기까지로 끊고 문자열이나 배열 예제들 풀 때 계속해서 덧붙여 설명하겠습니다.

반응형

+ Recent posts