한국어

tc_backup

//////////////////////////////////////////////////////
#include <stdio.h>
int main()
{
    int a = 10, b = 20;
    int *pa;
    pa=&a;
    printf("%d\n%d\n", a, *pa);
    printf("%d\n%d\n", pa, &pa);
    return 0;
}
//////////////////////////////////////////////////////

c로 된 간단한 소스를 기준으로 설명을 하겠습니다. c언어를 배우게 되면서 많은 분들이 pointer를 배우다 좌절하는 경우가 많은데 그러한 이유는 아마도 메모리 구조를 이해하지 못하고 있기 때문에 일어나는 문제 같습니다. 메모리 구조를 이해하게 된다면 pointer는 아주 당연하게 받아들일 수 있습니다.

스택(stack), 힙(heap), Data, 코드(Code)라는 메모리 영역에 대해서 들어보신 적이 없다면, adioshun님의 글을 먼저 읽어 보시기 바랍니다.

지역변수인 a와 b와 pa는 stack에 선언이 됩니다. 전역변수의 경우에는 데이터 영역에 선언이 된다는 것은 이제 기본적으로 알고 있을 것이라 생각이 됩니다.

그러면 위와 같은 프로그램이 실행이 되게 되면 메모리 구조에서는 어떻게 나타날지 stack의 모습을 그려봤습니다.

그림의 두 스택은 같은 내용입니다. 다만 왼쪽의 내용을 보기 편하게 구분하기 위하여 오른쪽에 하나 더 그려 색깔을 칠해놓은 것입니다.

x86 cpu는 32비트입니다. 32비트는 4Byte를 의미하고 메모리 공간의 주소도 16진수로 최대 FFFF FFFF로 나타낼 수 있습니다.(이론상)주소값이 4Byte 단위 이므로 해당 주소값의 데이터 저장 공간도 4Byte로 최대 FFFF FFFF까지 저장이 가능해집니다.(int의 경우이고 long이나 double의 경우에는 8byte로 두 공간을 사용하게 됩니다)

그 중 특정 부분을 Stack의 공간으로 설정하여 사용하게 됩니다. 그러므로 현재 위의 그림에서는 0012F** 지점이 스택의 영역임을 전제하에 설명합니다. 실제 내부적으로는 주소값은 달라질 수 있지만 위와 같은 형은 그대로 적용이 됩니다.

위 그림에서는 보기 편하게 4byte 단위로 표현이 되고 있습니다. 실제로도 보통 4byte 단위로 처리가 됩니다. 주소가 거꾸로 나와 있다고 잘못된게 아니냐고 하시는 분이 계실지 모르겠는데, Intel CPU는 Full Descending Stack으로 큰 주소에서 작은 주소로 데이터가 쌓이게 됩니다.

RET Address :
Return Address를 의미하며, 테스트에 사용한 c 프로그램을 호출하기 이전에도 무언가 내부적으로 함수가 사용되고 있었고, 그러던 중 프로그램을 호출하게 된 것이므로 프로그램이 종료되면 다시 해당 프로그램을 호출했던 지점으로 돌아가서 기존에 내부적으로 돌아가던 곳으로 돌아가 계속 실행을 해야 합니다. 그러기 위해 돌아갈 곳의 주소를 기억해놓게 되는 주소입니다.

SFP(Stack Frame Pointer) :
스택 포인터 프레임를 의미합니다. 스택은 고정적이지 않고 항상 유동적으로 변하는 저장 공간입니다. 그러므로 기준점(EBP)을 세워서 스택의 위치를 계산해서 해당 번지에 있는 값이나 주소를 찾아야 하는데, 만일 스택의 시작을 기준으로 한다면 처음에는 스택에 쌓인 데이터가 별로 없을 것이므로 시작과 쌓인 위치로 가서 값을 구하는데 무리가 없을 것입니다. 하지만 점점 스택에 데이터가 쌓이게 되면 시작 위치와는 점점 거리가 멀어지게 되므로 데이터의 값을 구하거나 저장하는데 있어서 상당한 오버헤드가 발생하게 됩니다. 그래서 사용하게 되는 것이 SPF입니다.
SPF는 기준점을 함수의 호출이 된 지점으로부터 계산하게 되는 것입니다. 해당 함수 내에서 사용하는 데이터들은 근처에 쌓여 있을 것이므로 값을 구하거나 저장하는데 있어서 오버헤드가 적어지게 됩니다.
예를 들면 main함수를 돌다가 func이라는 함수를 호출하게 되면 기존 main함수의 기준점(EBP) 백업해놓고 새로운 기준점을 세워야 합니다. 그러기 위해 main함수의 기준점(EBP)을 주소를 스택에 저장하게 됩니다.(즉 포인터처럼 주소값이 저장되게 됩니다.)

즉, 현재 main 함수는 SPF에서부터 시작입니다.(함수 시작이 될땐 반드시 이전 함수의 기준점(EBP)를 백업하므로)

그리고 int a, b; 가 되어 있으므로 가장 먼저 지역 변수를 사용하기 위해 스택에 공간을 할당하게 됩니다. int형 변수가 총 3개이므로 4 byte * 3 하여 공간이 할당되어지게 됩니다. 그 후 데이터가 할당되어진 공간에 들어가게 되는데 int는 4byte의 자료형이고 스택에서 기본적으로 4byte단위로 계산이 되기 때문에 위에처럼 쌓이게 됩니다. 참고할 점은 int a, b; 가 있으면 right->left의 순서로 스택에 쌓이므로 b가 먼저 쌓이고 a의 데이터가 쌓이게 됩니다.

그리하여 메모리 주소 0012FFE8에는 0x14(10진수 20), 0012FFE4에는 0x0a(10진수 10)가 들어가서 위의 그림과 같이 되게 됩니다.

int *pa; 는 포인터입니다. 포인터는 주소값을 저장할 수 있는 공간입니다. 주소값 체계는 4byte(32비트)로 구성이 되어 있기 때문에 포인터의 저장 공간 역시 4byte가 되어지게 됩니다. 프로그램 실행 초기 단계에서 이미 할당해 놓았기 때문에 0012FFE0의 메모리 주소가 포인터 변수의 주소가 됩니다.

그리고 포인터 변수 pa에 변수 a의 &(주소)를 =(대입)시키게 됩니다. 위 그림에서 보다시피 a의  시작 주소는 0012FFE4가 이므로 포인터 변수 pa의 공간은 4byte이므로 주소체계인 4byte의 크기와 동일하기에 손실이나 변화 없이 삽입이 가능합니다.

참고할 점은 long *paa; 라는 변수가 있다면 해당 포인터 변수는 8byte가 아닌 4byte의 공간을 갖게 되고 long형 데이터가 존재하는 변수의 시작 위치를 기억하게 할 수 있게 되는 것입니다.

추가적으로 파란색으로 하이라이트된 부분은 아직 할당되어 쓰이고 있지 않은 공간을 의미합니다. 해당 영역에는 0이 들어가 있을 수 있고 그 외의 값이 들어가 있을 수 있습니다. 하지만 그러한 값은 쓰레기 값으로 현재에는 할당되어진 값이 아니기에 필요하지 않은 값들입니다.(이전에 해당 주소가 사용된 흔적의 값일뿐입니다.)

이러한 내용이 이해가 된다면 포인터의 원리를 이해할 수 있게 될 것입니다. 말을 잘 못 하고 글을 잘 못쓰기에 의도하는 바가 제대로 표현이 안됐을 수 있지만, 포인터와 스택을 이해하시는데 도움 되시는 분 계셨으면 좋겠습니다.

조회 수 :
9126
등록일 :
2009.04.17
04:14:44 (*.234.246.12)
엮인글 :
http://www.rain9.com/xe/tc_backup/3259/db9/trackback
게시글 주소 :
http://www.rain9.com/xe/3259

Sun2Day

2009.04.18
09:24:39
(*.233.192.245)
엔신님하~ 스택이 먼가혐?~ ㅋㅋ
저도 촘 알려 주세혐 ㅋㅋㅋㅋ

엔신

2009.04.18
13:16:10
(*.212.249.159)
여기와서 행패 부리면 메신져로 괴롭힐껍니당.....
List of Articles
번호 제목 글쓴이 날짜 조회 수
180 CCNA netmask 별 CIDR [2] 엔신 2009-10-07 9709
179 Malware Conficker Worm 바이러스 감염시 조치 방안 엔신 2009-10-07 11310
178 Ambiguous 수정함 [6] file 엔신 2009-07-02 8145
177 Security WeakNet Linux Version 2.0 Final 소개 file 엔신 2009-07-02 18159
176 Security [Kon-Boot] Windows & Linux - Reset Password 엔신 2009-06-30 27926
175 Malware 악성코드에 대하여 알면 위험으로부터 벗어날 수 있다 [4] 엔신 2009-06-25 11226
174 Linux ubuntu 9.04에 laptop mode 활성화(Laptop Mode Tools) [2] 엔신 2009-06-12 12874
173 Linux ubuntu 9.04에 Intel Graphics driver 2.7.0 설치하기 엔신 2009-06-11 16547
172 RCE Windows Anti-Debug Reference [7] file 엔신 2009-06-09 24959
171 DBMS [Oracle 10g] java.sql.SQLException: IO 예외 상황: The Network Adapter could not establish the connection file 엔신 2009-06-09 21045
170 Security SSH Port Forwarding(SSH Tunneling) [2] file 엔신 2009-05-29 26998
169 Programming Tomcat 5.5.27 설치 엔신 2009-05-28 17321
168 Security Kismet.conf 설정 [2] file 엔신 2009-05-26 15494
167 Linux ubuntu 9.04에 IE6으로 AxtiveX 이용하기 [2] file 엔신 2009-05-21 14870
166 Linux ubuntu 9.04에 conky 설치 file 엔신 2009-05-20 17360
165 Ambiguous SK브로드밴드 사용자의 정당한 권리 file 엔신 2009-05-13 12524
164 Malware Koobface.worm을 통해 사용자 보안 마인드 개선되었으면... file 엔신 2009-04-23 8657
163 ETC 약속 엔신 2009-04-23 9843
162 Incident Response NTFS 파일 복구하기 [2] file 엔신 2009-04-18 9846
» RCE 스택(Stack)과 포인터(Pointer) 간단한 설명 [2] file 엔신 2009-04-17 9126