Blog | Tag | Local | Guest | Login | Write |  RSS
분류 전체보기에 해당되는 글 110건
2008.11.15 :: LookUp Table 연산 1
2008.11.14 :: Naming Conventions
2008.11.11 :: WPF UIElement Clone
2008.11.10 :: GTK+ "Hello World"
2008.11.10 :: GCC Introduction 2
하하 한번 밀리기 시작하니깐 정신이 없군요.... 요즘 학교공부를 하면서 많이 느끼는 거지만 이론의 중요성을 많이 느낍니다. 이번학기에 컴구조, 운영체제, 데이터통신, 데이터구조와 선형대수까지 한꺼번에 듣고 있거든요(데구말고는 절대 재수강이 아닙니다.;;;;;;) 그래서 그런지 코팅과는 좀 거리가 생긴것 같아요 ㅋㅋㅋ 아무튼 요즘 이론강의를 들으면서 참 많이 배우는것 같습니다. 여러분들은 어떠신지요? 실습강의가 더 좋으신가요? 뭐 어느쪽에 치우치지 않는 강의가 좋겠지만요

 아무튼 이번에는 5번째 이야기 기본 컴퓨터의 구조와 설계중 2번째 이야기 컴퓨터 레지스터에 대해서 이야기 하고자 합니다. 

 컴퓨터 명령어는 보통 연속적인 메모리상에 위치하고 이것들이 한 번에 하나씩 순차적으로 수행이 됩니다. 따라서 다음 수행될 명령의 주소를 알아낼 수 있는 카운터같은 장치가 필요하겠죠 또한 제어 장치내에는 메모리에서 읽어온 명령어 코드를 저장할 수 있는 레지스터와 데이터를 조작하기 위한 프로세서 레지스터, 그리고 메모리의 주소를 갖고 있는 레지스터가 필요할 것입니다.  


위에 그림과 표에서 레지스터 구서오가 각 레지스터의 기능, 비트수를 확인할 수 있습니다.
 메모리 장치는 4096워드로 구성되어 있으며, 각 워드는 16비트입닏. 즉 피연산자의 주소를 위해 12비트가 필요하고 3비트는 명령어 코드를 나타내며 나머지 1비트가 직접 주소, 간접 주소를 구별하는 데 사용됩니다. 데이타 레지스터(DR)는 메모리에서 읽어온 피연산자를 저장하며, 누산기(AC) 레지스터는 범용 처리 레지스터로서 사용이 됩니다. 메모리에서 읽어온 명령어는 명령어 레지스터(IR)에 저장되고 임시 레지스터(TR)는 계산 도중의 임시 데이타를 저장합니다. 메모리 주소 레지스터(AR)와 프로그램 카운터(PC)는 메모리의 주소를 나타내어야 하므로 12비트로 구성이 되어 있습니다. PC의 내용이 카운트 순서에 따라 증가함에 따라 명령어들은 분기 명령어를 만날 때까지 순차적으로 수행됩니다. 분기 명령가 수행될 때에는 주소 부분이 PC로 전송되어 다음 수행될 명령어의 주소를 지정하게 됩니다. 이 밖에  입출력 장치로부터 8비트 문자 정보를 송수신하기 위하여 입력 레지스터(INPR)와 출력 레지스터(OUTR)가 사용됩니다. 
 그렇다면 레지스터들 사이나 레지스터와 메모리 사이에 정보 전송을 어떻게 하는지 궁금해 지기 시작해지지요 기본 컴퓨터에는 정보 전송을 하기 위한 경로를 버스 시스템으로 구성을 합니다.
위의 그림은 간단히 그린 그림으로서 공통버스에 연결된 기본 컴퓨터의 레지스터들을 보여주고 있습니다.  7개의 레지스터와 메모리 출력이 공통 버스에 연결이 되어 있고 선택입력 S2S1S0를 통해 버스 위에 놓일 출력을 선택합니다. 예를 들어 선택 입력 S2S1S0=011이 면 3번으로 표신된 DR의 출력이 버스에 놓이도록 선택이 됩니다. 또한 공통 버스는 각 레지스터의 입력과 메모리의 데이터 입력에 연결되어 있는데 LD입력이 인에이블되어 있는 레지스터가 다음 클럭 펄스에서 버스의 데이터를 받아서 저장합니다. 메모리는 쓰기 입력이 활성화되었을 때 버스의 내용을 받을 수 있으며 S2S1S0=111이고 읽기 입력이 활성화되었을 때 16비트 출력을 버스에 올려 놓습니다. 12비트인 AR과 PC의 내용이 버스에 전송될 때는 상위 4비트가 0으로 채워지며, 버스의 내용이 AR이나 PC로 전송될 때에는 하위 12비트만 전송이 됩니다. 또한 8비트인 입력 레지스터 INPR와 출력 레지스터 OUTR는 하위 8비트만으로 버스와 데이터를 주고 받는데 INPR는 입력 장치로 부터 한 문자를 읽어와 AC로 전송하며, OUTR는 AC로 부터 한 문자를 읽어와 출력 장치로 전송합니다. 

 
 


토요일, 일요일 앓아 누워 있어서 일요일에 블로그를 올리지 못했네여...감기들 조심하세요..(이래서 미리미리 올렸어야 했나...하지만 월요일에 옥상서...) 아무튼 지난 시간에도 이야기 했듯이 궂이 디지털 부속품과 데이터의 표현, 레지스터 전송과 마이크로 연산 부분은 건너뗘야 할거 같습니다. 일단은 너무 전자 회로쪽에 치우쳐도 있고 이대로 가서는 시그가 완료될 때까지 못 맡칠거 같기도 하고요 그럼 기본 컴퓨터의 구조와 설계로 넘어가도록 하겠습니다.

기본 컴퓨터의 구조와 설계(첫번째 명령어 코드)

컴퓨터의 구조는
 1. 내부 레지스터
 2. 타이밍과 제어구조
 3. 명령어 집합
으로 되어지고 앞으로 얘기할 컴퓨터는 우리가 알고있는 pc에 비해 규모가 작지만 설계 과정을 단순하게 보여줄 수 있는 이점이 있습니다. ㅋㅋ(16비트 컴퓨터 정도지요)

디지털 시스템의 내부 조직은 레지스터 안에 저장된 데이처를 가지고 수행되는 마이크로 연산(레지스터에 저장된 데이타를 가지고 실행되는 동작 : 시프트, 카운트, 클리어, 로드)의 시퀀스에 의해 정의 되어집니다. 그렇다면 컴퓨터는 일반적인 용도의 시스템으로 다양한 마이크로 연산을 실행할 수 있고 수행할 연산의 특수한 시퀀스를 명령할 수 도 있습니다.  그리고 시스템의 사용자는 원하는 연산과 피연산자, 처리되는 순서를 기술한 명령어의 집합인 프로그램에 의해서 처리 과정을 제어할 수 있습니다.
 
 그렇다면 명령어의 집합인 프로그램에서의 명령어 정확히 말하면 컴퓨터 명령어란 무엇일까요? 바로 컴퓨터에 대한 일련의 마이크로 연산을 기술한 이진 코드라고 할 수 있습니다. 또한 데이터와 함께 메모리에 저장이 되어있고 제어신호에 의해 제어 레지스터에 옮겨지고 해석되어 제어 함수를 발생함으로서 실행이 됩니다. 이와 같이 명령어를 저장하여 실행하는 개념을 내장 프로그램 이라고 하며 범용 컴퓨터의 가장 중요한 특성이기도 합니다.

 앞서 설명한 내용으로 인해서 명령어란 곧 명령어 코드라는 것을 알게 되었습니다. 명령어 코드는 계속 반복되는 이야기지만 컴퓨터에게 어떤 특별한 동작을 수행할 것을 알리는 비트들의 집합으로서 여러 개의 부분으로 나뉘어지는데, 그 중 가장 기본적인 부분은 연산코드 부분입니다.
 연산코드는 가감승제나 시프트, 보수 등과 같은 동작을 저의한 비트들의 집합으로서 이 연산 코드 부분이 n 비트로 구성되면 최대한 2^n개의 서로 다른 연산을 실행할 수 있습니다. 또한 하나의 연산코드는 마이크로 연산의 집합으로 볼 수 있기 때문에 때때로 매크로 연산이라고 불리기도 합니다.(햇갈리기 시작...)
 명령어코드 이야기를 계속 하자면 명령어코드의 연산부는 실행될 연산을 기술하고 있는데 이러한 연산은 메모리 또는 프로세서 레지스터 안에 저장된 데이차를 가지고 실행되므로 명령어코드는 연산 뿐만 아니라 피연산자가 저장된 레지스터나 메모리 워드, 또한 연산 결과가 저장될 장소를 기술해야 합니다. 이와 같이 여러 개의 부분으로 구성된 명령어 코드의 구성형식은 컴퓨터의 구조 설계자에 의해 결정이 되어 집니다.

저장 프로그램 구조에 대해서 살표볼까요

 컴퓨터의 가장 간단한 구성은 단 한 개의 프로세서 레지스터를 가짐으로서 두 개의 부분으로 구성된  명령어 코드를 사용하는 것입니다. 이때 한 부분은 실행할 연산을 그리고 다른 한 부분은 피연산자가 저장된 메모리내의 주소를 기술하게 되는데 메모리로 부터 읽혀진 피연산자 부분은 레지스터에 저장된 데이터와 연산을 실행하게 됩니다. 위의 그림에서 보듯이 4096워드를 가진 기억 장치에 대해서 12비트 주소가(2^12=4096)필요합니다. 16비트 워드를 사용한다면 4비트가 남음으로 총 16가지의 서로 다른 연산을 하게 됩니다. 여기서  한 개의 프로세스 레지스러를 누산기(accumulator  또는 AC)라고 하며 명령어 코드 구성 형식은 4비트로 이루어진 opcode(실행할 연산)와 address(피연산자가 저장된 메모리 주소)로 이루어져 있어서 Address에 의해 지정된 메모리에 데이터와 AC에 저장된 데이터사에에 opcode가 지정한 연산을 수행하게 됩니다.
여기서 명령어 형식에 Address부분에 직접 피연산자의 내용이 들어가면 Immediate라고 하며 명령어 코드의 주소 부분이 피연산자가 저장된 메모리 주소를 나타내면 직접주소 명령어코드의 주소 부분이 피연산자가 저장된 메모리 주소를 담고 있는 주소를 나타내면 간접주소라고 부릅니다. 보통의 경우 한비트(I라는 부분)를 이용하여 주소부분이 직접인지 간접인지를 구별 합니다.
 여기서 유효주소라는 개념이 등장하게 되는데 아주 중요하죠 유효주소란 계산형 명령어에서의 피연산자의 주소와 분기형 명령어에서 목적주소를 뜻합니다.

다음에는 5번째 이야기 part2 컴퓨터 레지스터에 대해서 이야기 하도록 하겠습니다.

LookUp Table 연산

지금까지 포인트 단위의 산술연산에 관한 포스팅을 하였습니다.
어렵지 않은 내용이라 모두들 쉽게 이해하셨으리라 생각합니다.
실제로도 많이 사용되는 내용이기도 하고 구현 내용도 어렵지 않기 때문에,,
사람이 생각하기에는 크게 느리지 않은 연산이라고 생각하기 쉽지만..
사실 컴퓨터 입장에서는 각 포인트마다 연산을 다 해주어야 하기 때문에,, 많은 연산을 거쳐야 하는 함수임에 틀림 없습니다.

물론 지금 구현된 내용처럼 이미지가 작거나 (저번포스팅들에서 사용된 이미지는 512*512,256*256 이였습니다.)
하면 크게 속도차이가 보이지 않는것은 사실이지만,,,
사람이 살다보면 작은 해상도의 이미지만을 다루는 것이 아니기 때문에 좀 더 고속으로 연산할 수 있는 방법이 필요합니다.

이를 위해 나온것이 바로 LookUp Table연산 방식입니다.
이번포스팅에서는 저번 포스팅에서 다솜돌이 님께서 언급하셨던 고속 연산방식인 룩업테이블에 대하여 이야기하겠습니다.
(다솜돌이님 ㅋ 포스팅 순서가 있었어요 ㅋㅋㅋ 일부로 안쓴거라고요 ㅋㅋ)

1. 기존의 연산방법

 
   그렇게 느리다고 생각되지 않습니다만,,  이는 이미지의 크기가 작아서 라고 보시면 됩니다. (때마다 달랐지만..13ms)
 코드 보기
       public void Mul(int Const)
        {
            int TempNum;
            for (int X = 0; X < 512; X++)
            {
                for (int Y = 0; Y < 512; Y++)
                {
                    TempNum = TestData[X][Y] * Const;

                    TempNum = TempNum < 0 ? 0 : TempNum;
                    TempNum = TempNum > 255 ? 255 : TempNum;

                    TestData[X][Y] = (Byte)TempNum;
                }
            }
        }



2. LookUpTable을 사용한 경우
 

LUT를 사용한 연산

  같은 내용의 결과를 내는데 걸린 시간이 거의 1/4까지 줄어든 모습을 확인 할 수 있습니다.
  코드 보기
        public void MulUsingLookUpTable(int Const)
        {
            int TempNum;
            Byte[] LUT = new Byte[256];

            // LookUp Table을 완성합니다.
            for (int i = 0; i < 256; i++)
            {
                TempNum = i * Const;
                TempNum = TempNum > 255 ? 255 : TempNum;
                LUT[i] = (Byte) TempNum;
            }

            // LUT를 이용하여 연산을 수행합니다.
            for (int X = 0; X < 512; X++)
            {
                for (int Y = 0; Y < 512; Y++)
                {
                    TestData[X][Y] = LUT[ImageData[X][Y]];
                }
            }
        }



 뭐가 뭔지 모르겠다.
 위나 아래나 for문이 들어갔는데,, 오히려 LookUpTable방식에서는 256번의 for문이 있지않느냐?
 그럼 더 느린것이 아닌가?! 라고 하신다면.. 비밀인 여기입니다.

 연산수를 따져봅시다.
 기존의 방식 LUT의 방식 
 for (int X = 0; X < 512; X++)
            {
                for (int Y = 0; Y < 512; Y++)
                {
                    TempNum = TestData[X][Y] * Const;

                    TempNum = TempNum < 0 ?
                                       0 : TempNum;
                    TempNum = TempNum > 255 ?
                                       255 : TempNum;

                    TestData[X][Y] = (Byte)TempNum;
                }
            }

            for (int i = 0; i < 256; i++)
            {
                TempNum = i * Const;
                TempNum = TempNum > 255 ?
                                   255 : TempNum;
                LUT[i] = (Byte) TempNum;

            }       
      for (int X = 0; X < 512; X++)
            {
                for (int Y = 0; Y < 512; Y++)
                {
                    TestData[X][Y] = LUT[ImageData[X][Y]];
                }
            }
                            곱하기 연산의 수 512 * 512 = 262144
             Saturation을 위한 연산의 수 512 * 512 = 262144
    +                                               대입연산의 수 512
                      ===================================
                                                                     524800
                                                 곱하기 연산의 수 256
                                  Saturation을 위한 연산의 수 256
     +                                               대입연산의 수 512
            ================================
                                                                        1024
결과 : 13ms 결과 : 4ms 

 LUT방식은 미리 연산해야 하는 내용을 미리 연산 한 후 ,
 연산의 결과에 해당하는 값만을 대입하는 방식이므로 위의 결과와 같이 연산의 수를 대폭 줄일 수 있습니다.
 이를 통하여 좀 더 빠른 연산을 할 수 있는것이지요 ^^*
 (그림을 표현해서 설명하고 싶었는데,, 살짝귀차니즘이 ㄷㄷㄷㄷ;;;; 추후에 수정하겠습니다.;)

 사실 구현할때는 귀찮을 수 도 있지만,,
 좀더 빠른 결과를 위해서라면 귀차니즘을 극복하고 사용할 만한 방식인것은 충분합니다..

 이번 포스팅은 이만 마칩니다.

Naming Conventions
1. Naming conventions

1) Name은 의미를 쉽게 파악할 수 있도록 한다.
- Underscores를 이용해 단어를 구분한다.

2) 각 Name은 식별이 보장되도록 한다.

3) 혼동될 문자는 가급적 사용하지 않도록 한다.

4) 발음 상 혼동이 되는 Name의 사용을 피한다.

5) Global 변수는 등록한 후 임의 갱신을 피한다.

6) 약어(abbreviations)의 사용을 자제한다.
- 빈번한 사용시에는 의미를 설명한뒤 사용한다.
- 불분명한 약어는 사용하지 않는다. 
※ Naming convention은 project의 특성상 사용하는 tool, 다른 library와의 호환성, 표준 API등에 의해 영향을 받는다.



2. Underscore 와 Field naming convention

멤버 Field의 네이밍 가이드 라인에서 언더스코어(_, underscore)의 사용에 대한 의견이 분분하다.

언더스코어를 사용하는 개발자는 대/소문자로 구별하는 field/property가 익숙하지 못하다.
언더스코어를 싫어하는 개발자는 _가 사족 같다라는 느낌을 지울 수 없다.











가령

1. 언더스코어 사용 규칙

class Person{
private int _age;
public int Age
{
get{return _age;}
set{_age=value;}
}
}


2. 마이크로 소프트 프레임워크 규칙

class Person{
private int age;
public int Age
{
get{return this.age;}
set{this.age=value;}
}
}


1번과 2번의 규칙 어떤것이 좋냐에 대한 의견이다.

1번은 C, C++ 또는 MFC에서 헝가리안 표기법의 멤버를 나타내는 m_ 에서 m을 제거하고 표기하는 규칙으로,  옹호론자들은 명백하게 노출하지 않을 멤버 필드임을 코딩시에 알 수 있다 이다.

2번의 옹호란자들은 Visual Studio IDE를 사용할때 , 직관적으로 나열되므로 코딩에 편하다. 이다.

1번의 단점은 코딩시에 의미 없는 언더스코어의 나열을 봐야 하는 것이다. 2번의 단점을 멤버임을 대소문자로 구분하기가 약간 힘들다는데 있다. 또한 C#코드에서 대소문자를 구별하지 않는 VB등으로 변환시에 문제가 발생할 수 있다는 점이다.

1번 2번 모두 장단점에 대해 분명하게 말하기는 힘들지만,  2번 스타일을 권하고 싶다.

Field의 이용시에는 명확하게 this 키워드를 이용하며, 인텔리센스의 도움을 더 잘 받기 때문이다.


안녕하세요 조일룡입니다.

오늘은 C++에서 표준 라이브러리로 채택된 STL에 대해 간략하게 알아보겠습니다.

STL은 Standard Template Library의 약자로 템플릿을 이용한 표준 라이브러리.. 뭐 이정도의 의미를 가지고 있습니다.

아래의 주소에서 STL에 대한 모든것을 알 수 있습니다.

http://www.cplusplus.com/reference/stl/


이번 포스팅에서는 템플릿에대해 잠깐 짚어보고, STL에서 제공하는 컨테이너 중 가장 흔히 사용하는 'vector'에 대해  알아보겠습니다.


Template

템플릿을 건너뛰고 싶었지만 역시 언급을 해야할 것 같습니다.

템플릿은 컴파일타임에 자료형을 확정하여 그 자료형에 맞는 코드를 생성하는 C++에서 제공하는 기능입니다.

두개의 정수형 변수를 받아 값을 맞바꾸는 아래의 함수를 봅시다.

void swap(int & a, int & b)
{
if (a != b) a ^= b ^= a ^= b;
}

만약 실수형 변수를 받아 똑같은 일을 하는 함수가 필요하다면 우리는 아래와 같은 함수를 재정의 해야합니다.
void swap(double & a, double & b)
{
if (a != b) a ^= b ^= a ^= b;
}

단지 자료형만 다를 뿐 함수의 몸체는 동일한 구조를 가진 함수를 따로 구현하려니 여간 귀찮은 일이 아닙니다. 그래도 함수를 구현하는 일이니 좀 참아줄만 하네요.. 근데 클레스에서 이 짓을 해야한다면 참을 수 있을까요?

한 프로그램에서 int 형의 자료를 저장하는 스택과 double 형의 자료를 저장하는 스택이 필요한 경우를 가정해봅시다. 이 때 두 가지 형태의 스택을 각각 구현하려 생각하는 짜증이 밀려 오는군요... 하지만 어쩔수 없이 노가다 근성으로 코딩을 하겠지요.
일단 int형 스택을 구현하고 control+C -> contro+V 콤보를 작렬한 후 replace all(int->double)을 감행합니다.

휴.. 그나마 붙여넣기, 모두바꾸기 신공으로 그렇게 큰 노력을 들이지 않고 각각의 클레스를 만들긴 했습니다. ^^;

그런데 프로그램을 실행하다 보니 이상하게 프로그램이 죽는군요.. 어디가 잘못됐는지 찾아보니 스택이 문제입니다..
열심히 디버깅을 해서 오류가 난 지점을 찾아 제대로 작동하게 고쳤습니다.
이제 다시 프로그램을 실행하는데.. 또 죽는군요.. 이건 또 뭘까요.. 디버그를 해서 찾아보니...
아.. 아까전에 int형 스택만 고치고 double형 스택은 안고쳤네요 ㅠㅠ


템플릿을 사용하여 swap을 구현해봅시다.
template<class ItemType>
void swap(ItemType & a, ItemType & b)
{
ItemType temp = a;
a = b;
b = temp;
}

위의 swap과 모양이 약간 다른데요.. 위의 swap에서는 비트연산을 통해 두 변수의 값을 맞바꾸었지만 아래에서는 temp변수를 사용해서 바꿨습니다.
template을 통해 넘어오는 변수의 자료형이 비트연산으로 값을 맞바꿀수 없는 경우가 있을 수 있기 때문인데요.. 예를 들어 string같은 자료형은 비트연산으로 그 값을 맞바꿀 수 없지요.. 대신 저렇게 해야합니다. 물론 대입연산자 재정의는 필수입니다.


Template의 원리

제 경험상 많은 친구들이 template의 사용은 잘 하지만 이게 어떻게 동작하는지는 잘 모릅니다. 어떤 친구는 런타임시에 어떤 메커니즘이 작동하는거 아니냐고 하더군요 그렇기 때문에 template을 쓰면 속도가 느려진다고 하면서...

그 친구에게 유감스럽지만 template은 런타임시에 자료형을 보고 적절한 처리를 하는 것이 아닙니다. C++ 컴파일러가 그렇게까지 똑똑하진 않나봅니다. 사실은 그렇게까지 똑똑해질 필요는 없습니다.

C++은 단지 컴파일시간에 template으로 작성된 함수나 클래스를 사용하는 클라이언트를 보고 필요한 자료형으로 확장된 함수나 클레스를 각각 만들어 줍니다. 그러니깐 붙여넣기 + 모두바꾸기 신공을 컴파일러가 해주는 샘이지요 ㅎㅎ.. 컴파일이 약간 느려질수는 있겠습니다만 템플릿 때문에 실행속도가 느려졌다는 말은 어불성설이겠네요..




vector

vector는 STL에서 제공하는 컨테이너중 하나로 제 생각엔 가장많이 사용되는 컨테이너가 아닐까 생각됩니다.

vector는 배열과 같은 일을 합니다. 연속적인 공간에 템플릿으로 정의된 자료형의 배열을 할당하여 그 후에는 배열처럼 사용할 수 있습니다. 그리고 여러 STL에서 제공되는 algorithm 함수와 함께 강력한 기능을 제공합니다.


vector vs array

c++에선 이미 배열을 기본 데이터타입으로 제공을 합니다. 그런데 vector를 쓰는 이유는 무엇일까요?
가장 큰 이유는 동적할당 때문일 겁니다.

c++에서 배열을 잡을 때 컴파일시간에 배열의 크기를 모르면 배열을 잡을 수 없습니다. 즉 배열의 크기는 변수가 될 수 없고 오로지 상수만이 가능합니다. 이를 극복하기 위해 동적할당이 제공됩니다. c에서는 malloc 함수를 쓰고 c++에서는 new 객체를 사용합니다.
위의 기능을 활용하여 런타임에 적절한 배열의 크기를 알아내서 적절하게 배열을 할당할 수 있습니다.

그럼 왜 vector일까요?

런타임시에도 배열을 얼마나 크게 잡아야 할지 모르는 경우를 생각해봅시다. 예를 들어 장기게임을 하는데 한 수 한 수를 배열에 저장하고 싶습니다. 그런데 장기게임이 몇 수 만에 끝날지는 아무도 모르기 때문에 배열을 잡기가 쉽지 않습니다.
방법중 하나로 배열을 그냥 대충 엄하게 크게 할당하고 시작할 수는 있습니다. 왠만하면 그 배열크기를 초과할 만큼 게임이 진행되지는 않게 크게 잡습니다. 대부분의 게임은 배열을 오버플로우를 발생시키지 않고 종료됩니다. 하지만 여기에는 두가지의 문제점이 있습니다.

첫번재 문제점은 메모리의 낭비입니다. 대부분의 경우 잡아놓은 배열을 다 쓰지 않고 버려진채 게임이 끝납니다.
두번째 문제점은 메모리 오버플로우의 가능성이 있다는 것입니다. 두 기사가 너무 방어적이라 좀처럼 게임이 끝나지 않고 계속 진행되다 보면 잡아놓은 배열을 모두 쓰고도 모자란 상황이 발생할 수 있습니다. 이때 게임은 외마디 오류박스를 띄우고 사라지겠지요.

위의 문제를 해결하기 위해서 일단 배열을 적당히 작게 잡고 필요할때마다 더 큰 배열을 할당하는 방법이 있습니다. 장기를 예로 보면 대부분 장기를 하면 50수까지는 둔다는 정보가 있다고 가정합시다. 그럼 첫 번째 배열은 크기를 50으로 잡습니다. 게임이 진행되다가 50수가 넘으면 100개짜리 배열을 새로 잡고 50수까지의 기록을 100개 짜리 배열에 복사를 합니다. 그후 50개짜리 배열은 메모리에서 해제를 하고 이제부턴 100개짜리 배열을 씁니다. 이런식으로 배열의 크기가 더 커질 필요가 있을 때 마다 새로 메모리를 할당하는 방법이 있습니다만. 귀차니즘이 텍사스 소때처럼 몰려옵니다.

만약 한 프로그램내에서 저런 배열이 여러개 있어야 한다면 각각 구현해야 하는데 정말 귀찮아 죽겠습니다. 그렇다면 객체지향프로그래밍 페러다임을 도입해 클래스로 저런 배열을 구현해 놓고 가져다 쓰는게 그나마 좀 영리한 선택이 되겠지요? 거기에다가 템플릿의 편리함 까지 추가해서 언제어디서나 쓸 수 있게 만들어 놓읍시다.. ㅋㅋㅋ 클래스를 구현할땐 좀 귀찮지만 나중을 생각하면 기분좋은 일이내요

그런데 C++을 사용하는 프로그래머들에게 사막 한 가운데의 오아시스 같은 존재가 있었으니 그것이 바로 STL입니다.

STL에선 바로 저런 배열 클레스를 vector라는 이름으로 제공을 하고 있습니다. 저 같은 귀차니즘에 감염된 종자들에게 구원의 손길과 같게 느껴지네요.


vector를 사용해보자

우선 vector의 멤버함수를 살펴봅시다.

Member functions

(constructor) Construct vector (public member function)
(destructor) Vector destructor (public member function)
operator= Copy vector content (public member function)

Iterators:

begin Return iterator to beginning (public member type)
end Return iterator to end (public member function)
rbegin Return reverse iterator to reverse beginning (public member function)
rend Return reverse iterator to reverse end (public member function)

Capacity:

size Return size (public member function)
max_size Return maximum size (public member function)
resize Change size (public member function)
capacity Return size of allocated storage capacity (public member function)
empty Test whether vector is empty (public member function)
reserve Request a change in capacity (public member function)

Element access:

operator[] Access element (public member function)
at Access element (public member function)
front Access first element (public member function)
back Access last element (public member function)

Modifiers:

assign Assign vector content (public member function)
push_back Add element at the end (public member function)
pop_back Delete last element (public member function)
insert Insert elements (public member function)
erase Erase elements (public member function)
swap Swap content (public member function)
clear Clear content (public member function)

Allocator:

get_allocator Get allocator (public member function)


http://www.cplusplus.com/reference/stl/vector/

100개의 숫자를 임의로 생성하고 비내림차순으로 소팅하여 출력하는 아래의 프로그램을 봅시다.

  1. #include <cstdlib>   
  2. #include <ctime>   
  3. #include <iostream>   
  4. #include <vector>   
  5. #include <algorithm>   
  6. using namespace std;   
  7.   
  8. int main()   
  9. {   
  10.     int i;   
  11.     srand(time(NULL));   
  12.   
  13.     // 벡터를 선언합니다   
  14.     vector<int> nums;   
  15.        
  16.     // 100개의 난수를 생성하여 벡터에 추가합니다   
  17.     for (i = 0; i < 100; i++)   
  18.         nums.push_back(rand());   
  19.   
  20.     // 비내림차순으로 정렬   
  21.     sort(nums.begin(), nums.end());   
  22.   
  23.     // 화면에 출력   
  24.     for (i = 0; i < 100; i++)    
  25.         cout << nums[i] << endl;   
  26.   
  27.     return 0;   
  28. }  


위의 프로그램에서는 push_back() 함수를 사용하였습니다만 vector에 추가될 원소의 갯수를 미리 알고 있다면 생성자에서 미리 배열의 크기를 지정하여 시간과 메모리를 아낄 수 있습니다.
  1. int main()   
  2. {   
  3.     int i;   
  4.     srand(time(NULL));   
  5.   
  6.     int N = 100;   
  7.     vector<int> nums(N, 0);   
  8.   
  9.     for (i = 0; i < N; i++)   
  10.         nums[i] = rand();   
  11.   
  12.     sort(nums.begin(), nums.end());   
  13.   
  14.     for (i = 0; i < N; i++)    
  15.         cout << nums[i] << endl;   
  16.   
  17.     return 0;   
  18. }  



마지막으로 vector<int> 형 배열을 전달받아 합과 평균을 리턴하는 함수를 작성해보겠습니다. 평균은 반올림합니다.
  1. vector<int> SumAvg(vector<int> array)   
  2. {   
  3.     vector<int> ans;   
  4.     int sum = 0;   
  5.     for (int i = 0; i < array.size(); i++)   
  6.     {   
  7.         sum += array[i];   
  8.     }   
  9.     ans.push_back(sum);   
  10.     ans.push_back((double)ans / array.size() + 0.5);   
  11.   
  12.     return ans;   
  13. }  



이것으로 이번 포스팅은 마치겠습니다. 힘드네요 ^^;

다음 포스팅은 비트연산(bit operation)을 이용한 Brute Force Search 방법에 대해서 쓸까합니다. 간략히 설명하자면 Brute Force는 해의 후보가 될 수 있는 모든 문제 공간을 탑색하여 최적을 찾는 방법입니다. 문제를 해결하는 가장 확실하면서도 쉬운 방법인 반면에 수행 시간이 긴 단점이 있죠.. 주로 모든 문제 공간을 탐색해야 하는 경우에 사용됩니다.

WPF UIElement Clone

안녕하세요. 이번시간에는 기존의 WinForm에서 사용하던 Clone메서드를 WPF에서는 어떻게 사용하는지에 대해 알아보도록 하겠습니다. WPF에서는 특별하게 Clone 메서드를 제공하고 있지 않기때문에 Clone 메서드를 직접 구현 해야 합니다. 프로젝트를 하다가 생각나서 구현해놨던건데 알려드리면 유용할것 같아 포스팅합니다..
 
저같은 경우는 방법이야 여러가지가 있겠지만, 저같은 경우는 간단하게 UIElement객체를 XAML로 Export하고 다시 해당 객체를 Import하는 방식으로 사용합니다. 생각보다 부하가 심할 수 도 있는 부분이긴하나, 간단하게 한두번 정도 사용하기에는 큰 무리는 없을 것으로 보입니다.

아래는 제가 사용하는 CloneElement 메서드 소스코드입니다.

  1. public static UIElement CloneElement(UIElement Source)   
  2. {   
  3.   
  4.     if (Source == nullreturn null;   
  5.   
  6.     string XAML = System.Windows.Markup.XamlWriter.Save(Source);   
  7.   
  8.     System.IO.StringReader StringReader = new System.IO.StringReader(XAML);   
  9.     System.Xml.XmlReader xmlReader = System.Xml.XmlTextReader.Create(StringReader);   
  10.   
  11.     return (UIElement)System.Windows.Markup.XamlReader.Load(xmlReader);   
  12.   
  13. }  
사용은 아래와 같이 사용하시면 됩니다.
  1. Button OriginalButton = new Button();   
  2. Button CloneButton = CloneElement(OriginalButton) as Button;  



.............

연달아 글 쓰는건 좀...

귀찮은듯??-ㅁ-a



이번 시간(?)은 사용자 에게서 입력을 받아서, 그 변수를 가지고 활용해 보도록 하겠습니다.

입력을 받는 방법은 이때 까지 출력으로 써왔던 SimpleDialog를 이용하시면 됩니다. 훗. -_-a

먼저 SimpleDialog와 Calculate Activity를 추가하여 줍니다.

그리고 그 둘을 연결 하여 줍니다. 그러면 다음과 같은 Connections 창이 뜹니다.


그러면 위와 같이 PromptDialog - Success를 선택하고 OK를 하여줍니다.


현재 까지 상태 입니다.

여기 까지 하셨으면 SimpleDialog를 하나 더 추가 하여 줍니다. 그러면 Add Activity라는 창이 하나 더 뜹니다.

현재 Diagram에 SimpleDialog가 있기 때문에 뜨는데, 원래 있는 SimpleDialog와 별반 다르지 않기 때문에 아래의 SimpleDialog를 선택하시고 OK를 눌러줍니다.


그리고 Calculate Activity 와 방금 추가한 SimpleDialog Activity를 연결합니다. 그러면 또 Connections 창이 뜨는데 AlertDialog를 선택하고,

Value는 value를 선택하고 OK합니다.


다 하셨으면 다음과 같은 모습일 것입니다.

그러면 실행을 하여 주시면~~!

입력을 받을 Prompr Dialog창이 뜹니다.


그러면 입력 창에 원하는 문자열 아무거나 적으시고 OK를 클릭하시면~


위와 같이 적은 문자열이 그대로 출력이 됩니다.

지난 글과 연계하여 Calculate Activity 를 수정하여서, 또 다른 상태로 출력을 하여 보세요~

참 쉽죠~?

.............ㅜㅜㅜㅜㅜㅜ

..........

저번주 한번 날렸네요... -_-;;

시사회 간다고 바쁘게 그날 하루 날려 주시고~

이번주에 두개 올라 갑니다~~!


VPL 기본 1 에서는 Hello World! 를 출력 하는 것을 해보았습니다.

그냥 단순히 변수 값 만 날리는거 말고, 변수 값을 받아서 처리 하여 출력하여 보도록 하겠습니다.

저번의 Hello World! 하는 것에서 약간 변형을 해서 Variable과 Calculate 라는 Activity를 하나 씩 추가 해 줍니다.


그러면 위와 같은 상태가 됩니다.

Data 에 string형 변수로 "홍길동"을 넣어주면 "당신은 홍길동 입니다" 로 메세지를 출력 해 보도록 하겠습니다.

그러면 Data에 string형으로 변환하여 홍길동을 적어주시고, Variable 에는 your name으로 지정해줍니다.


위와 같이 하시면 되는데 Data와 Variable을 연결 하시면 다음과 같은 창이 뜨는데 거기서 SetValue를 선택하시고 OK누르시면 됩니다.

그리고 Variable Activity에서 오른쪽 아래의 ... 라는 추가 버튼을 누르시면 다음과 같은 창이 뜹니다.


여기에서는 Add 버튼을 누르신 후 yourname이라는 변수로 설정해 줍니다. 물론 데이터 형은 string으로 하여야 겠죠.


그리고 Calculate에서는 yourname이라는 변수를 선택하여 주면 됩니다.


현재 까지 하셨으면 위와 같은 상태일꺼고 simpleDialog랑 Calculate를 연결 하시면 됩니다. 여기서는 지난 글의 방법과 같이 AlertDialog를 선택 하시면 되구요.


그 다음에 Data Connections 창이 뜨는데 거기선 value에서 value를 선택하시고 ok하시면 됩니다.



이 상태에서 실행을 하시면 변수를 처리 하지 않은 변수 상태 그대로 홍길동 이 출력이 됩니다.


변수 처리를 할려면~!! Calculate Activity에서 수정을 해 주어야 합니다.

string은 " " 로 닫아주시고, 그담에 + 하시면 두 변수 사이가 더해지는(??)구조?-ㅁ-a;;;; 다음의 그림과 같이 Calculate Activity를 수정하여 주시고 실행하시면.


다음과 같이 원하는 모습대로 출력이 됩니다.




뭐 이거 날로 먹는 느낌도 강하긴 한데.. 기초라 뭐 설명을 어찌.. 흡...-_-a


GTK+ "Hello World"


안녕하세요. 한주동안 잘지내셨는지요?^ ^;
오늘은 Hello World를 출력하는(?) 화면을 만들어 볼 생각입니다. ^ ^

모든 Programming 언어의 책의 첫장을 보면 화면에 Hello World를 띄우는 것부터 연습를 하죠? ^ ^; 그래서 저도 오늘은 Hello World를 화면에 보여주려고 합니다. 가장 기초적인 부분부터 뼈대를 만들어 가볼까요? ㅋㅋㅋ

이번에는 Source를 분석할때는 시그널(이벤트) 함수와 시그널 핸들러 함수에 대해서 집중적으로 설명해 볼까 합니다.

1. Hello World를 들어가기에 앞서..

이번에는 여태까지 내용과는 달리 중요한 부분이 몇가지 나오는 것 같아요. 중요한 부분이라면, gtk프로그래밍에서 interface를 다루는 부분이죠. interface란 사용자와 컴퓨터(or 윈도우) 사이의 관계를 말하며, 사용자가 요구하는 대로 반응하는 환경을 구현하는 것이 interface 프로그래밍이라고 합니다.

어떤 식으로 Hello World를 출력해 볼까요? 일단, 버튼과 label을 하나 만들어 볼까해요~ 버튼을 누르면 label의 내용이 "Hello World Open"으로 바뀌었다가 다시 한번 더 누르면 "Hello World Closed"로 바꾸도록 만들어 보겠습니다. 그럼 여기서도 알 수 있듯이 오늘은 버튼을 누름에 따라 변화를 구현하는부분이 핵심이라는 것을 알 수 있죠?

2. Hello World


#include <gtk/gtk.h>

GtkWidget *label;

void button_clicked ( gtkwidget *widget, gpointer data)
{

          static int toggle = 1; 

          if (toggle == 1) 
          { 
                    gtk_label_set_text ( gtk_label(label) , "hello world opened"); 
                    toggle = 0; 
          } 
          else 
          { 
                    gtk_label_set_text ( gtk_label(label) , "hello world closed"); 
                    toggle = 1; 
          }
}

void delete_event ( gtkwidget *widget, gdkevent *event, gpointer data)

          gtk_main_quit ();
}

int main( int argc,char *argv[] )

          gtkwidget *window, *button; 
          gtkwidget *vbox; 
          gtk_init (&argc, &argv); 
          window = gtk_window_new (gtk_window_toplevel); 
          gtk_window_set_title(gtk_window (window), "upgrade hello world");

          gtk_signal_connect ( gtk_object(window), "delete_event", 
          gtk_signal_func ( delete_event), null); 
          gtk_container_set_border_width ( gtk_container ( window), 10 ); 
          vbox = gtk_vbox_new ( false,0); 
          gtk_container_add ( gtk_container ( window ), vbox); 

          label = gtk_label_new ("hello world closed");

          gtk_box_pack_start (gtk_box(vbox), label, true, true, 0); 
          gtk_widget_show ( label);

          button = gtk_button_new_with_label ("Click");

          gtk_signal_connect ( gtk_object (button), "clicked");
          gtk_signal_func ( button_clicked ), null); 
          gtk_box_pack_start (gtk_box(vbox), button, true, true, 0); 
          gtk_widget_show (button); 
          gtk_widget_show (vbox); 
          gtk_widget_show (window); 

          gtk_main (); 
          return(0);
}


3. Hello World Source 설명

main()을 보면 보지못했던 새로운 함수들이 보이죠?

                    gtk_window_set_title(); 
                    gtk_signal_connect(); 
                    gtk_container_set_border_width(); 
                    gtk_vbox_new(); 
                    gtk_button_new_with_label(); 
                    gtk_box_pack_start();

gtk_window_set_title();
위의 함수는 메인 윈도우의 제목을 정해 주는 부분입니다. title!! 이 글자만 봐도 대충 감이 잡히죠? ^ ^; 

gtk_container_set_border_width();
이 함수는 window 의 border 의 크기를 정의합니다.

gtk_vbox_new();
gtk에서 윈도우 안의 여러가지 위젯들을 배열하기 위해서 box라는 위젯을 많이 씁니다. gtk는 window 안에 window를 삽입 할 수 가 없어서 이러한 도구를 앞으로도 많이 사용할 것입니다. table과 frame이 주로 사용되는 도구들 중의 하나이죠 ^ ^
box는 실제로 보여지는 위젯은 아니지만 다른 widget들을 배열하기 위해 사용하지요. box인데 vbox라고 사용한 것에 대해 의심이 가지 않아요? box는 두가지 종류가 있습니다. vbox와 hbox가 사용됩니다. 이것은 수직박스와 수평박스를 나타내지요. 물론 gtk_*_new()으로 정의할 수 있습니다. hbox와 vbox의 조합으로 위젯의 위치를 정해서 좀더 직관화된 인터페이스를 만들 수 있죠.
위의 "Hello World" source는 vbox를 정의하고 그곳에 레이블과 버튼을 올려놓습니다(packing). packing이라는 말은 widget들을 쌓아 넣는다고 보면됩니다. 예를들어 커다란 여행 가방에 여러가지 물품들을 차곡차곡 정리해야 한다면 vbox로 나눈뒤 수직 방향의 박스에 차곡차곡 widget을 넣어서 보기좋게 정돈되는 과정이라고 생각하시면 되죠 ^ ^ 머리속으로 그림을 그리면 좀더 쉽게 이해가 되실 겁니다.

gtk_button_new_with_label();
button을 정의하는데 label을 넣고 싶을 때 쓰는 함수입니다. 버튼에는 일반적으로 label이 보여서 사용자에게 어떤 역할을 하는지 알려주는 역할을 하죠.  

gtk_box_pack_start();
gtk_container_add와 비슷한데.(기억나시죠? container에 무언가 추가하는^ ^;) 여기서는 박스에 쌓기(packing) 위해서 위 함수를 쓴다. gtk_box_pack_start()는 hbox 위젯의 왼쪽에서 오른쪽, vbox부분에서는 위쪽에서 아래쪽의 순으로 쌓는다는 의미이며, 반대 역할을 하는 gtk_box_pack_end()함수도 있습니다. 뒤에 붙는 인자들은 expand, fill할 것인가에 대한 선택이며, 마지막이 padding의 크기를 정의합니다. 

이제부터.. 가장 중요한 부분을 설명해 보겠습니다. 제가 처음에 말씀드렸죠. 오늘은 가~장 중요한 무언가가 나올것이라구요.
이제 나타날 때가 되었습니다. 짜~~~잔!!!^ ^* 

gtk_signal_connect(   gtkobject *object, 
                                gchar *name, 
                                gtksignalfunc func, 
                                gpointer func_data
                             );

위 함수는 interface , 즉 사용자가 뭔가를 행하였을(signal) 때 반응 하는 함수에 대해서 연결 (connect ) 시켜주는 역할을 합니다.


위의 예제(Hello World)중에서..
gtk_signal_connect ( gtk_object (button), 
                                 "clicked", 
                                 gtk_signal_func ( button_clicked ), 
                                 null); 

예로 들자면, 사용자가 button이라는 위젯을 눌렀(click)을 경우 'clicked' 라는 이벤트가 발생하며, button_clicked라는 함수를 불러 실행하라는 의미가 됩니다. 실제 button_clicked는 소스의 윗부분에 정의하였습니다.

두 번째 인수인 "clicked"는 임의로 정해지는 것이 아닙니다. 이것은 각각의 위젯에 대해서 사용자가 행한 행동 (event라고도 할수 있다)을 말하며, 버튼이라면 누르는 것이 있을 것이며, 메뉴에서는 선택하는 부분등이 예가 될 수 있습니다. 이 event는 각각의 위젯에 따라 다르며, 그 이름또한 틀립니다. 그 이름은 미리 정의되어 있습니다. 물론 사용자가 임의의 위젯을 만들고 임의의 이벤트를 만들수는 있지만, 그것은 좀더 고차원적인 기술이기에 다음 기회에 해보아야겠습니다.
사용자가 버튼을 눌렀더니, button_clicked이라는 함수가 불러집니다. 실제 위 소스에서는 버튼이 눌러지면 label 의 내용을 토글시키는 작용을 하게 해놓았습니다. c 프로그래밍을 접한 우리들은 쉽게 접근할 수 있겠죠. 여기에서 마지막 인수는 함수에 넘겨질 데이터입니다. 이 데이터는 대부분 프로그래밍에서는 null이지만 사용자가 필요에 의해 임의의 값을 넘겨주어야 할 때 적어주면 됩니다.
위에 또다른 gtk_signal_connect가 있죠? 'delete_event' 이며 이는 시스템 버튼 (오른쪽 위의 x모양의 버튼) 이 눌러졌을 경우 gtk 내부에 미리 정의된 기본 함수, gtk_main_quit을 부르게 되어 있습니다. 단순히 윈도우를 종료시키는 함수죠. 사용자가 만들지 않아도 되는 (일반적인 이벤트 함수) 함수들은 미리 정의된 것들을 쓰기도 합니다.


그럼. 정리를 해볼까요? ^ ^; 

(1) 우선 window을 생성하고, 박스를 만듭니다.
(2) label을 생성하고 초기 이름은 'hello world closed'이고 이것을 박스에 쌓아봅니다(packing).
(3) button을 만들고 초기 label은 '클릭해 주세요'이며, 버튼이 눌러질 경우 행하여질 이벤트 함수를 'button_clicked'라고 정의하였고 이벤트와 연결하였습니다. 버튼 또한 box에 packing시킵니다.
(4) 각각의 위젯을 보이게 합니다.

4. 결과 화면
 


오늘 저의 설명은 어떠하였는지요? ^ ^; 궁금한 사항이 있으면 댓글을 달아주세요 ^ ^
무언가 하나씩 해나아가는 듯한 느낌이 들지 않아요? ㅋㅋ 오늘은 무언가 설명 좀 한것 같아서 나름 뿌듯한걸요? ^ ^;
함수를 좀더 쉽게 설명하기 위해 책과 블로그들을 참고하였는데, 아직 많이 미흡-_-+ 이쁘게 봐주세요 ^ ^

GCC Introduction
** 앞으로 작성되는 모든 글은 GCC 4.x 버전대의 소스와, gcc internels문서, 각종 인터넷 분석자료( 정원교씨 분석자료, 그외 기타 등등), 책, 그리고 www.iamroot.org gcc 스터디 그룹에서 공부한 내용을 바탕으로 작성되어 집니다 **

GCC는 (GNU Compiler Collection)의 약자이다. GNU C Compiler라고 부르기도 하는데 GCC가  C++, JAva, Fortran, Ada등 많은 프로그래밍 언어를 지원하면서 부터 단순히 C Compiler라고 부르기보다는 전자의 경우가 맞을 것이라 생각한다.

GCC의 가장 큰 장점은 현존하는 수많은 Architecture의 지원이다. x86부터 시작하여 ARM, MIPS, PowerPC, Sparc등등 현존하는 대부분의 아키텍쳐를 지원한다. 이렇게 많은 아키텍쳐를 지원할 수 있었던 것은 각 아키텍쳐로의 포팅이 용이하게 구성되어져 있기 때문이다. 따라서 새로운 아키텍쳐를 GCC에 포팅하는 일도 간단하지는 않겠지만 새로운 컴파일러를 제작하는것에 비해 상당한 수고를 덜 수 있을 것이다.

그럼 간단히 GCC의 컴파일 과정을 알아보자.

소스파일(.c)  ->>  전처리후 파일(.i)  ->> 어셈블리 파일(.s)  ->> 오브젝트 파일 (.o)  ->> 최종 실행파일(ELF)
               (전처리과정)              (컴파일과정)             (어셈블러과정)              (링킹과정)

이것이 보통 gcc가 컴파일 되는 과정이다.

gcc로 보통 -o 옵션을 주게 되면 각 단계별로 생성되는 파일을 최종 ELF실행을 만들어 낸후 다 삭제 해 버린다. 중간 생성 파일들을 보기위해서는

gcc -E 옵션을 주면 .i파일을 볼 수 있고 -S 옵션을 주면 .s파일을 생성하며, -c 옵션을 주면 .o파일을 만들어내게 된다. 이것 모두를 다 보고 싶다면 --save-temps 옵션을 주게 되면 중간생성 파일들을 보두 볼 수 있다.

컴파일러는 단순히 고레벨 수준의 언어를 최종 아키텍쳐의 목적코드로 만들어내는데에 그 역할이 있지 않다. 현대 컴파일러에서 가장 중요한 부분은 최적화 부분이다. 사용자가 작성한 코드가 해당 아키텍쳐에서 최적으로 동작하게끔 목적코드를 생성해 주어야 한다. 그래야 프로그램의 성능을 극대화 시킬 수 있다. 그 과정이 .i 에서 .s로의 컴파일과정에 일어나게 된다.

이과정은 크게 4가지로 나뉘어 지는데

GENERIC tree -> GIMPLE tree -> SSA -> RTL

이런순서대로 GCC는 해당 소스파일을 여러단계에 걸쳐 최적화를 이루어내게 된다.

크게 최적화는 두가지 부류로 나누어 볼 수 있는데 컴퓨터 아키텍쳐에 종속적인 최적화와 그렇지 않은 최적화로 나누어 볼 수 있다. RTL전 까지는 아키텍쳐에 비종속적인 최적화가 일어나고 RTL변환후에는 아키텍쳐 종속적인 최적화가 이루어지게 된다.

보통 학부 컴파일러 과정에서는 파싱(토큰, 문법체크)과 의미분석(타입체크 등등)과정까지만 배우고 만다. 이런 부분을 front-end라고 부르고 그 뒤의 최적화 부분을 back-end라고 부른다. front-end는 이미 많은 해결책과 거의 최적화된 솔루션이 존재해 현재 연구분야에서는 거의 제외되고 있다고들 하고, 최적화 부분인 back-end가 주요 관심사라고 한다.( 생각해보면 컴퓨터 아키텍쳐의 발전에 따라 계속 고려되어야 하는 부분이니 계속 연구의 수요는 일어날 것이라 생각된다. )

여튼 학부과정에서 배운 파싱과정을 거쳐 파싱트리가 만들어지면 이것을 프로그래밍 언어에 독립적인 general한 트리로 만들게 된다. 이것이 바로 GENERIC 트리이다. 이후에 아키텍쳐 비종속적인 최적화를 이루기 위해 트리를 좀더 변경시키는데 이것이 GIMPLE 트리에 해당한다. GIMPLE 트리에서 한단계 더 변형을 가하는데 이것이 바로 SSA(Static Single Assignment)형태이다.
이렇게 아키텍쳐 비종속적인 최적화를 수행한후에 거의 어셈블리 형태와 유사한 RTL( Register Transfer Language)로 변환한후 여기서 아키텍쳐 종속적인 최적화를 수행하게 된다. ->각 트리에 대한 얘기는 추후에....