- 포인터에서부터 사람들이 C언어를 어려워하는데 전혀 어려운 내용이 아니다!
인터넷에도 포인터에 대한 자료도 풍부하다. 향후 훌륭한 프로그래머가 되려면 이 '포인터' 공부를 제대로 해놓으면 빛을 볼 수가 있다. 웹개발이나 앱개발 쪽에서는 굳이 몰라도 큰 상관이 없긴 한데, 코어 모듈 개발도 우리는 할 수 있어야 한다! 자 그럼 시작해보장 v^___^
7. 포인터
- 지금까지의 변수는 "그 자체로 자신의 자료형에 맞는 값"을 저장한다.
ex) int,double 등등
- 포인터(Pointer)변수는 “특이한 변수”로, “메모리 주소”를 저장한다.
ex) int형 변수를 만들 때, 이 변수의 위치값을 가르키는 int형 포인터가 존재할 수 있는 것이다. 포인터는 *(별표시)로 이용해서 나타낼 수 있다. (int*)
- 포인터는 메모리의 주소값을 가진다고 보면 된다.
(예제 1)
(해설) 기존에 a를 이용해서라도 5라는 값을 찾을 수 있지만 포인터 변수인 b를 이용해서도 5라는 값을 찾을 수 있다. 즉, 포인터를 통해 값을 처리하는 방법이 다양해 졌다고 할 수 있다. 여기서 포인터 변수 b도 변수이기 때문에 특정한 주소를 가진다.
- int *b=&a 처럼 “선언”할 때 쓰는 * (포인터)는 포인터 변수임을 알려준다.
- 선언 한 이후에 *b(여기 * 은 간접 참조 연산자)라고 쓰게 되면, 이것은 포인터 변수 b가 가리키는 주소값의 “진짜값”을 의미한다.
- 다시말해, 5라는 값 자체가 된다.
(간접참조: 현재 포인터가 가리키고 있는 주소에 들어 있는 값을 참조한다는 뜻)
<포인터 관련 연산자>
- 주소연산자 (&) : 변수 앞에 붙어서 변수의 메모리 시작 “주소” 값을 구한다.
- 포인터 (*): 포인터 변수를 선언할 때 사용한다.
- 간접 참조 연산자(*) : 선언된 포인터 변수가 가리키는 변수를 구한다.
(예제2)
#include <stdio.h>
int main(void) {
int a = 5;
int *b = &a;
printf("%d\n", *b);
system("pause");
return 0;
}
(해설) 선언할 때의 앞선 *b에서의 *과 나중에 사용할 때의 *b는 생긴 건 동일하지만 위쪽에 있는 것은 “포인터 연산자”이고 아래 쪽에 있는 것은 “간접 참조 연산자”라는 것을 기억하자!
여기서 포인터 변수 b에 a의 주소값을 가진 값을 할당하는 것이므로 printf문에서도 b가 아닌 *b를 써야한다. 다시말해서, 포인터 변수 b가 가리키고 있는 주소값의 변수값을 출력했다고 할 수 있다.
(예제3) 배열 각 원소의 주소값 출력하기
#include <stdio.h>
int main(void) {
int a[] = { 1,2,3,4,5,6,7,8,9,10 };
int i;
for (i = 0; i < 10; i++) {
printf("%d\n", &a[i]);
}
system("pause");
return 0;
}
(해설) 출력해보면, 배열 각 원소의 주소값이 출력된다. ‘&’는 이처럼 특정 원소의 메모리 주소값을 불러온다. 여기서 흥미로운점은, 출력해보면 출력된 주소값이 4씩 차이가 나는 것을 볼 수 있는데, int형은 4바이트를 차지하므로 메모리 주소를 1바이트씩 표현할 때 4칸을 차지한다. 즉, 하나의 정수마다 4bytes의 메모리를 차지하기 때문에 1씩 증가되면서 주소값은 4씩 차이나는게 당연하다고 할 수 있다.배열 a는 40bytes를 차지하는 배열임을 알 수 있다
<포인터의 강력한 기능>
- 포인터는 컴퓨터 시스템의 특정한 메모리에 바로 접근가능
- 따라서, 기존에 존재하던 중요한 메모리 영역에 접근하지 않도록 해야한다.
ex)int *a=0x33484735; 이것은 굉장히 위험한 코드이다.
특정한 주소값을 바로 포인터 변수가 *a=0;가리키도록 한 다음에 그 값을 0으로 바꿔버리는 코드다. 특정한 주소값에 함부로 접근해서 다른 값을 쓰면 안된다.
- 포인터는 다중으로 사용할 수가 있다.
(예제4) 다중 포인터
#include <stdio.h>
int main(void) {
int a = 5;
int *b = &a;
int** c = &b;
printf("%d\n",**c);
system("pause");
return 0;
}
(해설) 포인터는 단지 특정한 메모리 주소를 담고 있는 변수일 뿐이다. 따라서, 포인터는 다중으로 사용할 수 있다. 여기서 주의할 점은 *b=&a 와 **c=&b 처럼 ‘&’다음엔 포인터 없이 그냥 변수 알파벳만 오면 된다.
마지막 printf문에서는 그냥 변수 c가 아닌 **c를 출력해줘야 그 주소값이 가리키고 있는 값, 즉 변수a의값을 출력 할 수가 있다.
포인터의 포인터, 포인터의 포인터의 포인터 다 가능하다. 실제로 ***d=&c도 해봤는데 똑같이 5가 출력되는 것을 확인했다.
- 배열과 포인터는 사실 동일하다. 배열을 선언한 이후에는 그 이름자체가 포인터 변수와 동일하다.
(예제5)
#include <stdio.h>
int main(void) {
int a[] = { 1,2,3,4,5,6,7,8,9,10 };
int* b = a;
printf("%d\n",b[2]);
system("pause");
return 0;
}
(해설) 배열을 선언한 이후에는 그 이름 자체를 포인터 변수처럼 쓸 수 있다. 여기서 주의할 점은 int *b= a; 처럼 a는 배열이므로 주소값을 따오는 &a 가 아닌 그냥 a라고 적어줘야 한다.
다시말해, 포인트 변수를 만들어서 a를 가리키도록 만들자.(int *b=a;) 여기서는 주소연산자(&)가 안 붙었지? a라는 일종의 “배열 자체”를 지금 “주소값으로 사용”하고 있다는 것이다. 사실 내부적으로 이 배열의 이름자체는 주소값 자체를 가지고 있다. 그렇기 때문에 이렇게 사용한다.
그럼, 왜 *b[2]가 아니라 b[2] 일까? 다시한번 말해준다! 포인터 변수에 배열a를 할당했으니 배열 자체가 주소값을 가지므로 *b가 아니라 b가 와야지! 배열과 포인터는 상호치환이 가능하잖냐! (마지막 페이지 그림 참조)
(정리)
1) 포인터는 특정한 변수가 메모리 상에 존재하는 위치주소를 저장한다.
2) 포인터는 특정한 메모리 주소에 바로 접근 할 수 있으므로 조심스럽게 사용해야한다.
3) 배열이 정해지면 그 배열의 이름자체는 주소값 자체를 가지고 있다.
'컴퓨터 공부 > © C' 카테고리의 다른 글
[C언어] 9. 문자열 (0) | 2021.02.13 |
---|---|
[C언어] 8. 문자 (0) | 2021.02.13 |
[C언어] 6. 배열 (0) | 2021.01.28 |
[C언어] 5. 함수 (0) | 2021.01.27 |
[C언어] 4. 반복문 (0) | 2021.01.27 |