여치의 프로그래밍 강좌 #4

조회 수 39350 추천 수 205 2005.07.27 23:42:22
/*
과거 모 잡지에 연재했던 프로그래밍 강좌입니다.
퍼가실땐 사전에 동의를 얻으시기 바랍니다.
-유영천
*/

지난 달 강좌를 보고 ‘이게 뭔 소리고...하늘만 바라본 독자들도 여럿 계시리라 생각한다. 필자의 생각으로는 도무지 알기 쉽게 쓰고 자시고 할 건덕지가 없는 부분이다. C/C++문법이야 원래 그 정도면 시작하는데 무리가 없고 컴 구조와 어셈블리 기초를 알고 들어가면 포인터야 따로 볼 필요도 없고. 그래서 필요하다 생각되는 것들을 추려서 강좌로 구성했던것이다. 그러나 역시나 프로그래밍 처음 하는 사람에겐 어려운 내용이었을거라는 생각.
남은 강좌를 읽다보면 처음에 이해할 수 없던 내용도 이해할 수 있을 것이다. 그러니 걱정말고 남은 내용들을 읽어보시라.

함수(function)의 개념
이 함수란 말을 처음 접한게 중학교 수학 교과서에서였다. 교과서에서 뭐라고 정의를 했는진 기억이 나지 않는다. 그러나 함수가 뭔지는 알고 있다고 생각한다.
프로그래밍에서 말하는 함수도 수학에서의 함수와 별반 다를게 없다. 애초에 이 용어 자체는 수학의 그것에 기반을 두고 있을것이라 본다.
자 그럼 함수의 특성을 살펴보자. 물론 프로그래밍의 관점에서...

y = f(x)
중학교 때 수학과는 담쌓고 산 사람이라도 이 정도는 직관적으로 알 수 있을 것이다. y는 함수 f()에 x를 대입한 결과값이란 얘기다.
예를 들어 함수 f(x) = x*x 라고 정의했다면 y값은 항상 x의 제곱이 된다. f(1)=1이고 f(2)=4겠지.
매우 쉽다. 함수란 놈은 이런 개념이다.
왜 이런걸 쓰면 복잡한 처리를 단순화 시킬 수 있기 때문이다.
꼭 프로그래밍이 아니라도 당신이 어떤 노트에 수학 문제를 풀어야한다 치자.

y = A(x+b)^2 + c +A(x+b)^2 + d +A(x+b)^2 + e +A(x+b)^2 +f

이와같은 수학식이 있다고 하면 자세히 보라. 빨간 글씨로 표시된 부분은 계속해서 반복되는 부분이다.
이를 다음과 같이 고쳐보자

f(x)=A(x+b)^2
y = f(x) + c + f(x) + d + f(x) + e + f(x) +f

이해가 되는가? 훨씬 보기 편하다. f(x) 값이 옳다면 y의 결과를 계산할때 훨씬 간소화된 방법으로 문제를 해결할 수 있다. 또한 척 보면 알겠지만 수식의 길이가 짧아졌다. 노트에 적어야 할 문자 수가 줄었다. 짧게 써서 뭐 하겠냐..라고 반문하겠지만 이건 엄청난 의미를 가진다. 노트, 즉 작업공간은 컴퓨터에서 메모리이다. 즉 메모리를 절약할 수 있는 것이다.
(VC++화면에 표시된 문자가 짧아진다고 컴파일된 코드까지 작아지는것은 아니다. 하지만 저 경우는 명백하게 함수화 시키기 전보다 작은 코드를 만들어낸다.)
감이 오는가? 함수를 왜 사용하는지? 반복되는 처리루틴은 재활용함으로서 인간의 노동력을 절감시시키고 컴퓨터의 메모리 자원을 아끼기 위함이다.

프로그래밍에 있어서 함수의 적용
C에서의 규칙
1.코드는 함수 안에서만 실행될 수 있다.
C언어는 함수를 기본으로 하는 언어이다. 그래서 함수들의 집합으로 이루어진다. 어떤 코드도 함수를 벗어나서 실행될 수는 없다. 함수 안에서만 실행된다.
함수 바깥에 코드를 써넣으면 컴파일시 다음과 같은 에러를 볼 수 있다. 이 규칙은 컴파일러가 처리한다.

func_error_00.jpg



2. 함수의 리턴값
기본적으로 함수는 리턴값을 가진다.
y = f(x)꼴에서 y에 집어넣을 값이 리턴값이다. 그러나 리턴값이 없는 경우도 있고 컴파일러에서도 이를 허용한다. 수학에서 결과값을 되돌리지 않는 함수란 의미가 없겠지만 프로그래밍에선 어떤 계산결과를 얻기 위함 외에도 반복되는 코드를 재활용하기 위함이 있기 때문이다. 또한 규칙상 함수 바깥에선 코드를 사용할 수 없기 때문에 이러한 리턴값이 없는 함수는 필요하다.
리턴값이 없는 함수는 void형으로 표시하거나 그냥 괄호 안을 공백을 채워둔다.

func_void.jpg



3.인자(argument)의 전달
인자란 y = f(x)꼴에서 x에 해당한다. 물론 y = f(a,b,c,d)의 꼴도 수학적으로 가능하고 프로그래밍에서도 가능하다.
인자의 전달은 일반적으로 스택을 통해 이루어진다. 4바이트 이하는 스택에 푸쉬, 초과는 mov등으로 카피하고 함수에 진입하여 스택으로부터 pop하거나 mov등으로 카피된 데이타를 억세스 하게 된다.
참고로 C에서 포인터를 많이 쓰는 이유가 여기에 있다. 덩치큰 메모리 블럭을 바로 인자로 전달하려 하면 몽땅 스택에 카피해야 하므로 함수 진입시에 속도가 떨어지게 된다. 그래서 보통 포인터로 전달한다. 포인터는 4바이트짜리이므로 push 한번이면 되니까.


4.함수의 지역 변수
함수 안에서 선언해서 사용한 지역변수는 함수를 벗어날때 소멸한다. 메카니즘으로 설명하자면 원래 함수에 진입할때는 그 함수에서 사용하는 지역변수의 총 사이즈를 계산해서 그만큼 스택 공간을 확보하게 된다. 단순히 스택포인터(esp레지스터)를 사이즈만큼 빼주는 것이다. 스택은 미리 확보되어있는 메모리니까. 그리고 함수를 나갈때 스텍포인터를 원래대로 돌린다. 스택포인터가 원래대로 되돌려지고나면 스택을 사용하는 다른 코드들에 의해서 방금까지 사용하던 스택 메모리 영역은 overwrite될 수 있다. 그러니까 정확히는 소멸한다기보다는, 함수에서 벗어난 후에는 지역변수에 기입된 내용이 멀쩡하다고 보장할 수 없는 것이다.

5.일반적인 프로토타입
함수의 프로토타입의 예는 다음과 같다.

func_proto.jpg



함수 사용의 예. 소스와 그 결과

func_add.jpg



함수의 메카니즘
1.함수 호출의 원리
현대 CPU는 기본적으로 분기하고 다시 돌아오는 기능을 가지고 있다.x86CPU의 경우 int,iret와 call,ret 명령을 가지고 있다. 일반적으로 말하는 함수는 call과 ret를 사용한다.

func_call.jpg



앞에 보인 두 수의 합을 구하는 프로그램의  VC++디스어셈블 화면이다. C코드와 어셈블리 코드가 어게 대응하는지 잘 보여주고 있다. 보면 함수를 호출하는 부분이 두 군데 있다. call명령을 쉽게 찾을 수 있다. 그 다음이 호출할 함수 코드가 위치한 메모리 주소이다.괄호 안에 함수 이름이 들어있다. 즉 함수 이름은 컴파일 단계에서 주소 표시하기 위한 일종의 심볼이다. 인라인 어셈블리로 call Add 를 해도 동일하게 작동한다.
C의 함수 호출 체계는 사실상 어셈블리어와 1:1 대응이라고 봐도 된다.


2.스택 프레임

func_stack_leavel.jpg




위에서 함수 지역 변수에 대해서 언급했었다.
스택 프레임을 설정한다 함은 이 지역변수를 사용하기 위한 환경을 설정하는 것이라 할 수 있다.
int         iResult;
요 라인에다 브레이크 포인트를 찍어보면 알겠지만 브레이크 포인트는 찍히지 않는다. 왜냐하면 이건 코드가 아니고 이런 형식의 변수 선언 구문을 종합해서 함수의 스택 프레임을 잡는 코드가 단 한개로 만들어져서 함수 진입부에 붙기 때문이다.
스택 프레임을 설정할때는 함수에서 사용하는 지역 변수의 총 사이즈만큼의 스택 공간을 확보한다. 또한 지역변수와 전달받은 인자를 억세스할 수 있도록 ebp레지스터를 세팅한다.(단 릴리즈 모드에선 컴파일러가 코드를 최적화하여 컴파일하기 때문에 ebp레지스터를 사용하지 않고 esp레지스터로 바로 스택을 억세스하기도 한다).
스택 프레임과 직접적인 상관은 없지만 이 부분의 코드에서 대개 함수에서 사용할 레지스터들을 스택에 백업한다. 그렇지 않으면 함수를 종료하고 원래의 코드로 돌아갔을때 레지스터 내용이 엉망이 되어있을것이다.
C코드만으로는 이 스택 프레임 생성 코드를 볼 수 없다. 디스어셈블 해야만 볼 수 있다. 그러나 이 루틴은 컴파일된 코드 상에 분명히 존재하고 함수 호출이 빈번할수록 속도를 떨어뜨리는 요인이다.

스샷의 아래쪽을 보면 함수를 벗어나는 코드가 있다. 사실 함수를 벗어나서 원래의 흐름으로 복귀하는데는 ret명령 하나면 충분하다. 그럼에도 불구하고 ret명령 위에 이것저것 붙어있는것을 볼 수 있다. 이는 함수 진입시에 push명령으로 백업한 레지스터를 원래 코드가 사용하는데 문제가 없더록 복구하는 것이다. 또한 인자와 지역변수 사용을 위해 사용했던 ebp 레지스터를 복구한다.
이 함수는 C스타일의 _cdecl 호출방식이므로 ret만으로 끝냈지만, 윈도우즈 등에서 표준으로 사용하는 _stdcall 방식인 경우에는 ret [상수] 꼴로 스택을 직접 비운다. 바꿔말하면 스택 포인터를 함수 진입할때의 상태로 복구시킨다.
_cdecl에서는 함수 종료 후에 원래 코드로 돌아가서 스택포인터를 복구시키므로 함수 안에서 직접 이 일을 하지는 않는다.








Standard C Runtilm Library
MSVCRT.DLL에 들어있는 런타임 라이브러리는 C++의 그것까지 포함하고 있지만, C++런타임 라이브러리에 대해 언급하려면 C++특성과 별도의 문법까지 모두 설명해야하므로 C런타임라이브러리에 대해서만 설명하겠다.

복잡한 처리 루틴을 반복 사용할 수 있다는것이 함수의 장점이라 말했었다. 그래서 C는 강력하다. 이를 뒷받침해주는 것이 이미 만들어진 수많은 라이브러리들이 있다는 것이다. C언어는 수많은 함수들을 표준으로 지원한다. 이를 Standard C Runtilm Library라고 한다.
기본적으로 C 런타임라이브러리는 .lib와 .h파일로 제공된다. 사용할때는 .h를 인클루드한다.원래는 .lib를 프로젝트에 포함시켜야 하지만 VC++툴에서 런타임라이브러리의 .lib는 자동으로 포함시켜준다.

몇 가지 많이 사용하는 CRT 함수들에 대해서 설명하겠다. 자세한 사용법에 대해선 MSDN(VC++도움말)에서 찾아보시기 바란다. 귀찮아서가 아니라 그 편이 훨씬 유용하다. 색인에서 함수 이름을 치면 금방 찾아준다.
현업에서 작업할때도 MSDN에서 그때 그때 찾아본다.아주 밥먹듯 사용하는 함수들은 보통 외워쓰지만 그렇게 자주 사용하지 않는 함수는 이름을 추측해서 MSDN에서 찾은 후 문서에서 프로토타입과 유의사항 등을 보면서 코딩한다.


void*        malloc(size_t size);
-메모리 할당 함수.사이즈를 직접 지정해서 메모리를 할당 받을 수 있다.
C++에서는 new로 포장되어있다. 고정 배열이나 변수만으로는 메모리 사이즈를 그때 그때 필요한만큼만 할당하는게 불가능하기 때문에 이런 함수가 필요하다.

void        free(void*memblock);
-malloc()으로 할당한 메모리를 해제한다. 해제하지 않고 무한정 할당만 한다면 언젠가 메모리가 부족해질 것이다. 컴퓨터의 메모리 자원은 한정되어있으니까 말이다. 필요치 않을때 이와같은 해제 함수로 메모리를 해제한다.

void*        memcpy(void* dest, constvoid* src, size_t count);
-C프로그래머들이 가장 많이 사용하는 함수가 아닐까 싶다. 컴퓨터가 하는 일의 절반 이상은 아마도 데이타 이동일테니까. 메모리에서 메모리로 데이타를 전송한다. 단위는 바이트. 사이즈를 지정하여 카피하는 것이다. 목적지 주소가 앞에 오는데 유의하라. 이해안될지 모르지만 메모리 전송 함수들은 대부분 이런 식이다. 아마도 x86 cpu에서 목적지 오퍼랜드가 앞에 오기 때문에 그러한 관행을 따른게 아닐까..라고 생각해본다.

int memcmp(constvoid* buf1, constvoid* buf2, size_t count);
-두 메모리 블럭을 비교한다. count만큼의 바이트 수를 비교하여 같으면 0, 다르면 0이 아닌 값을 리턴한다.

char*        strcpy(char* strDestination, constchar* strSource);
-문자열을 카피한다. memcpy와 비슷하다. 차이 점이라면 길이를 지정하지 않는다는것. 왜냐하면 C에서는 문자열의 끝을 0으로 보기 때문이다. 터미널 문자라고도 한다.즉 0이 나올때까지 카피하는 것이다.

int strcmp(constchar* string1, constchar* string2);
-두 문자열을 비교한다. 내부적으로 memcmp와 거의 같다. 길이를 지정하는 대신에 0이 나올때까지 비교한다고 보면 된다.(물론 두 문자열의 길이부터 비교하겠지만)

size_t        strlen(constchar* string);
-문자열의 길이를 리턴한다. 이것 인자로 던져준 주소로부터 0이 나올때까지 메모리의 각 바이트들의 수를 세는 것이다.

int printf(constchar*format [,argument]...);
-콘솔모드 프로그래밍에서 이보다 많이 쓰는 함수는 찾기 힘들것이다. 화면에 뭐라도 찍어야 결과를 볼테니까.AllocConsole()따위의 윈도우즈 전용 콘솔이 아닌 스탠다드 콘솔 출력에 대해 텍스트를 출력한다.
printf는 사용법이 복잡하다. MSDN의 도움을 많이 받아야 하는 함수가 바로 이것이다.
간단하게 사용법의 예를 들어보겠다. 소스와 그 결과이다.

printf_sample.jpg



마치며
어셈블리 코드가 많이 나와서 다소 어렵다고 느꼈을지 모르겠다. 대충 call,ret정도만 눈여겨 봐두면 될 것이다. 이렇듯 C와 어셈블리가 매칭된다는 거 정도 알면 된다. 어셈블리를 설명하자는게 아니고 C 코드가 이런식으로 CPU를 제어한다는 점을 설명하자는 것이다.
직접 코드를 짜보며 궁금한 부분에 f9로 브레이크 포인트를 찍고 디스어셈블 창으로 확인해보면 실력이 굉장히 많이 향상된다.
복잡하고 큰 프로그램을 짜기보단 작은 프로그램들을 짜면서 메카니즘을 자세히 알아두는게 좋다. 메카니즘을 확실히 알고 있으면 아무리 큰 규모의 프로그램이라도 문제없이 작성할 수 있다. 규모가 큰 프로그램이라고 짜는게 어려운게 아니고 디버깅 하는게 어려운거니까. 메카니즘을 확실히 알고 있으면 디버깅에선 압도적으로 유리하다.
그런 점에 유의한다면 당장은 쉽게 이해할 수 없다해도 시간이 흐름에 따라 프로그래밍에 익숙해질 수 있을 것이다.
파일 첨부

여기에 파일을 끌어 놓거나 파일 첨부 버튼을 클릭하세요.

파일 크기 제한 : 0MB (허용 확장자 : *.*)

0개 첨부 됨 ( / )
List of Articles
번호 제목 글쓴이 날짜 조회 수
83 여치의 프로그래밍 강좌 #6 file [1] 여치 2005-07-27 29846
82 여치의 프로그래밍 강좌 #5 file 여치 2005-07-27 42032
» 여치의 프로그래밍 강좌 #4 file 여치 2005-07-27 39350
80 여치의 프로그래밍 강좌 #3 file 여치 2005-07-27 35348
79 여치의 프로그래밍 강좌 #2 file 여치 2005-07-27 31837
78 여치의 프로그래밍 강좌 #1 file 여치 2005-07-27 30037
77 지형도 BSP를 사용하시나요? [1] 초보 2005-07-06 26492
76 DirectX 입문용으로 적절한 책이 있을지요? [2] MiR 2005-03-01 41287
75 [re] DirectX 입문용으로 적절한 책이 있을지요? [1] 여치 2005-03-02 30297
74 BSP/Portal/PVS에 대한... guest 2005-02-18 24490
73 [re] BSP/Portal/PVS에 대한... file 여치 2005-02-19 23534
72 3D가속에 관한 질문 [3] 바하무트 2004-12-29 37321
71 [re] 3D가속에 관한 질문 여치 2004-12-29 27613
70 [질문] HeightMap의 퍼포먼스... [1] croove 2004-11-01 28868
69 [상담] 어떻게 해야할까요? . 2004-08-20 26046
68 [re] [상담] 어떻게 해야할까요? [1] 여치 2004-08-21 29021
67 [질문] 힙메모리 라이브러리에대해서. clever98 2004-08-08 32744
66 [re] [질문] 힙메모리 라이브러리에대해서. [1] 여치 2004-08-08 30295
65 [re] [질문] 단편화의 제거는 어떤식으로 하시나요? [1] 웰치스포도주스 2004-11-23 27815
64 개발 인력에 대한 질문 [1] 방문객 108 2004-07-20 23064



XE Login

天安門大屠殺 六四天安門事件 反右派鬥爭 大躍進政策 文化大革命 六四天安門事件 The Tiananmen Square protests of 1989 天安門大屠殺 The Tiananmen Square Massacre 反右派鬥爭 The Anti-Rightist Struggle 大躍進政策 The Great Leap Forward 文化大革命 The Great Proletarian Cultural Revolution 人權 Human Rights 民運 Democratization 自由 Freedom 獨立 Independence 多黨制 Multi-party system 民主 言論 思想 反共 反革命 抗議 運動 騷亂 暴亂 騷擾 擾亂 抗暴 平反 維權 示威游行 法輪功 Falun Dafa 李洪志 法輪大法 大法弟子 強制斷種 強制堕胎 民族淨化 人體實驗 胡耀邦 趙紫陽 魏京生 王丹 還政於民 和平演變 激流中國 北京之春 大紀元時報 九評論共産黨 獨裁 專制 壓制 統一 監視 鎮壓 迫害 侵略 掠奪 破壞 拷問 屠殺 肅清 活摘器官 障テ社會 誘拐 買賣人口 遊進 走私 毒品 賣淫 春畫 賭博 六合彩 台灣 臺灣 Taiwan Formosa 中華民國 Republic of China 西藏 土伯特 唐古特 Tibet 達償ワ喇嘛 Dalai Lama 新疆維吾爾自治區 The Xinjiang Uyghur Autonomous Region free tibet