포인터를 공부하다 보면 '무엇을 가르키는 변수' 라는 표현을 자주듣는다.
나는 이러한 표현을 좋아하지 않는데, 무엇을 가르킨다고 해서 특별한 취급을 받는것처럼 보이지만 실로는 그렇지 않다고 생각하기 때문이다.
어디까지나 특수한 목적을 위해 사용되는 '변수'일 뿐이지, 결국 '변수'라는 본질에서 벗어나지는 않는다고 생각한다.
따라서 이를 보여주기 위해서 간단한 프로그램을 작성하고 결과를 보이려고 한다.
hello.c
#include <stdio.h>
int main() {
char str[20] = "AAAAAAAAAAAAAAAA";
int a = 10;
int *p = &a;
// 변수 a의 값 출력: 10
printf("%d\n", a);
// 변수 str의 값 출력: A x 16
printf("%s\n", str);
// 변수 p의 값(= 변수 a의 주소) 출력
printf("p(0x%x)의 값: %x\n", &p, p);
return 0;
}
해당 코드는 char배열, int, int *포인터 세가지의 변수를 선언하고 내용을 출력하는 코드이다.
gcc -g -m32 hello.c -fno-stack-protector -z execstack -o test
canary, ASLR을 해제해야만 분석이 용이하다. 추가로 -g
옵션을 통해 디버깅 정보를 바이너리에 삽입하도록 했다.
list
명령으로 소스코드를 확인하고 b 17
명령어를 사용해 17번째 'return 0' 코드에 브레이크 포인트를 설정했다.
r
옵션으로 브레이크 포인트까지 진행했고 17번줄 'return 0' 코드가 실행되기 직전이다
포인터 p
는 값 0xffffd1a8
을 가지고 있고 0xffffd1a4
주소를 사용하고 있다.
여기서 p가 가진 값은 사실 변수 a
가 사용하는 공간(주소)의 값이다.
즉 p
에 저장된 값은 주소값이며, 이를 역참조하면 변수 a
의 공간에 도달할 수 있다는 것이다.
우리가 변수에 저장하는 값은 큰 의미를 두지 않지만, 포인터 변수에 저장하는 값은 '주소값' 이라면서 큰 의미를 부여하곤 한다.
하지만 둘 다 그저 임시로 저장한 '값'에 불과하며 포인터 변수에 저장하는 '값'은 실제 주소와 매칭되는 '값'
일 뿐 특별하다고 별개로 생각할 문제는 아니라고 생각한다.
댕글링 포인터
유효하지 않은 메모리나 이미 할당이 해제된 주소를 가르키고 있을 경우 이렇게 부른다.
주로 free()를 진행한 포인터를 다시 접근하려고 시도할 경우 문제가 발생하는데
가끔가다가 free()를 진행한 포인터에 접근을 시도해도 되는 경우가 있다.
센티넬 노드는 어떠한 역할을 하는가? rbTree에서의 NIL, 트리나 연결리스트의 마지막을 의미하는 노드이다.
보통 linked list 혹은 binary search tree 등 트리형태의 구조를 사용할 경우
다음노드가 존재하지 않으면 NULL을 사용한다.
하지만 Sentinel Node를 NULL 대신에 사용하는 방법이 있다.
이경우 코드의 구현이 비교적 복잡해질 수 있지만 조금의 성능향상을 꾀할 수 있다.
👍