블로그 이미지
Every unexpected event is a path to learning for you. blueasa

카테고리

분류 전체보기 (2797)
Unity3D (853)
Programming (479)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (185)
협업 (61)
3DS Max (3)
Game (12)
Utility (68)
Etc (98)
Link (32)
Portfolio (19)
Subject (90)
iOS,OSX (55)
Android (14)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (3)
Memories (20)
Interest (38)
Thinking (38)
한글 (30)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (18)
Exercise (3)
나만의 맛집 (3)
냥이 (10)
육아 (16)
Total
Today
Yesterday
반응형

'Programming > Math' 카테고리의 다른 글

투영 벡터 ( Projection Vector )  (0) 2012.05.16
동차좌표 ( 同次座標, Homogeneous coordinate )  (0) 2012.05.16
[펌] 3D공간구조 기본충돌  (0) 2011.09.08
좌표계  (0) 2010.07.01
행렬식(Determinant)  (0) 2010.03.17
Posted by blueasa
, |

C++ 컴파일러는 가상 함수를 호출할때 자료형을 참조하는 것이 아니라, 객체 포인터를 이용해 함수를 호출하게 되는데, 이를 구현하기 위해 C++ 컴파일러는 컴파일 단계에서 가상 함수를 가지는 모든 클래스에 대해 가상 함수 테이블을 생성한다. 이 가상 함수 테이블 내의 각 항목은 정의된 가상 함수를 가리킨다. C++ 컴파일러는 가상 함수를 가지는 클래스의 객체에 대해 객체가 할당된 메모리의 처음 4바이트에 가상 함수 테이블의 주소를 저장한다..

 가상함수 테이블 포인터(vptr)을 이용해 가상함수를 호출하기 때문에 객체의 처음 4바이트가 어떤 클래스의 가상 함수 테이블을 가리키는지에 따라 다른 가상 함수를 테이블이 구체적으로 어떻게 생성되고 객체는 자신의 처음 4바이트를 어떻게 초기화시키는지 알아보자

 가상 함수 테이블이 클래스 단위로 생성된다고 했는데, C++ 컴파일러가 가상함수 테이블을 클래스 단위로 생성하는 것은 객체의 메모리 크기를 줄이기 위해서 이다. 만약 객체 메모리 영역에 직접 가상 함수 포인터를 생성한다면 가상 함수 개수에 비례하여 객체의 크기는 늘어날 것이다.

어차피 동일한 클래스로 생성된 객체들은 동일 가상 함수를 호출해야 하기 때문에 이것은 메모리 낭비가 아닐수 없다.

 그래서 C++ 컴파일러는 가상함수를 가리키는 포인터를 테이블로 구성하고 이것을 클래스 단위로 생성한 다음 이 테이블에 대한 포인터를 객체 맨 앞에 4바이트에 설정함으로서 간단하게 객체에 가상함수 테이블을 포함하는 효과를 가질수 있도록 한다. 이렇게 C++ 컴파일러가 가상함수 테이블을 관리하기 때문에 가상함수를 가지는 클래스는 가상함수의 개수에 상관없이 객체의 크기는 항상 멤버 변수의 총 크기에 4바이트를 더한 크기로 생성될수 있게 된다.

 가상 함수에 대한 모든 원리는 테이블과 관련이 있기 때문에 이것에 대해 좀더 알아보자

가상 함수 테이블의 크기는 해당 클래스에 정의된 가상함수 개수에 비례한다.

여기서 정의된 가상함수란 상위 클래스에 정의된 가상함수도 포함한다.

그래서 상위 클래스에 두개의 가상함수가 정의되고, 자신의 클래스에 하나의 가상 함수가 정의 된다면 가상 함수 테이블 내의 항목은 상위 클래스의 가상 함수를 가리킨다. 너무나 당연한 이야기지만 가상 함수 테이블 내의 항목은 재정의 된 가상 함수를 가리킨다. 가상 함수 테이블의 이런 특징 때문에 어떤 클래스가 가상 함수를 상속 받아 가상 함수를 재정의 하지 않고 객체를 생성하여 해당 가상 함수를 호출하면 상위 클래스에 정의된 가상 함수가 호출된다. 


반응형

'Programming > C/C++' 카테고리의 다른 글

String  (0) 2010.03.22
enum 보다 나은 enum  (0) 2010.03.21
[펌] C++, 가상함수,순수가상함수  (2) 2010.03.14
[펌] 프로그램, 프로세스, 스레드  (0) 2010.03.13
[펌] 메모리 할당자의 단편화 문제점  (0) 2010.03.13
Posted by blueasa
, |
반응형

'Programming > Math' 카테고리의 다른 글

투영 벡터 ( Projection Vector )  (0) 2012.05.16
동차좌표 ( 同次座標, Homogeneous coordinate )  (0) 2012.05.16
[펌] 3D공간구조 기본충돌  (0) 2011.09.08
좌표계  (0) 2010.07.01
역행렬(Inverse Matrix)  (0) 2010.03.17
Posted by blueasa
, |

[ 가상함수(Virtual Function) ]

1. 가상함수 개요

■ 가상함수(Virtual Function)란?

- 실행시 사용된 객체에 의해 실행 코드가 결정되는 함수

- 지정자 virtual로 선언됨 지정자 virtual은 선언문에 존재함.

class Animal {

  public :

    virtual void breathe();

};

 

class Fish : public Animal {

   public :

    virtual void breathe();

};

 

■ 가상 함수의 호출

- 기반클래스의 포인터가 파생클래스의 객체를 가리킬 때, 그 포인터를 통해 가상함수를 호출하면 파생 클래스에서 재정의한 함수가 호출됨.

Animal* a = new Fish;

a -> breathe();    // Fish::breathe 호출

→ a->breathe(); 는 Fish::breathe 를 호출함. 즉, a->breathe(); 는 함수명으로는 Animal::breathe를 호출하지만, 내용으로는 Fish::breathe 가 호출되는 것임.

 

■ 기반클래스의 멤버함수가 virtual 을 갖고 있을 때

- 파생클래스에서 재정의한 함수는 virtual 을 생략해도 저절로 가상함수가 됨.

- 그러나 기반클래스의 가상 함수를 파생클래스에서 재정의할  때도 virtual 을 명시해주는 것이 관례임. (소스코드 이해가 수월하기 위해서...)

 

2. 가상함수의 특징

■ 동적바인딩(Dynamic Binding)

- 가상함수는 동적결합(Dynamic Binding)을 함. (코드만 봐서는 어떤 코드가 실행될지 정해지지 않은 상태. 즉, 객체를 생성하는 시점에 생성조건에 따라 실행코드가 결정된다.)

- 일반함수는 정적 결합을 하는 반면, 가상 함수는 동적 결합을 함.

 

■ 가상함수(동적 바인딩)의 장단점

- 장점 : 실행시 다형성(Runtime Polymorphism) 이라는 융통성을 갖게 됨. (상황에 생성하는 조건에 따라 실행 코드를 변경할 수 있다.)

- 단점 : 가상함수를 사용하면 실행속도에서는 손해

  

3. 가상 함수 테이블 (Virtual Function Table)

■ vtable (Virtual Function Table) 이란?

- 가상 함수에 대한 포인터 배열

- 가상 함수를 사용하는 클래스의 각 객체는 vtable을 가리키는 vptr이라는 숨겨진 포인터를 갖고 있음.

- 가상함수를 포함한 클래스의 객체를 생성할 때 vtable이 만들어짐.

- 객체에서 가상함수를 호출하면 vptr를 통해 vtable에 있는 해당함수를 찾아 수행시킴.

 

■ 파생클래스에서의 vtable

- 기반클래스의 객체로부터 vtable을 물려 받아 수정 및 확장함.

- 파생클래스에서 재정의한 가상함수가 있다면 기반클래스의 가상 함수에 대한 포인터가 들어 있던 곳을 파생클래스에서 재정의한 가상함수에 대한 포인터로 변경함.

- 기반클래스의 포인터가 파생클래스의 오브젝트를 가리킬 때 그 포인터를 통해 가상 함수를 호출하면 파생클래스에서 수정한 vtable 을 사용하게 되므로 파생 클래스에서 재정의한 함수가 호출됨.

 

4. 가상 소멸자

■ 상속 관계에서 기반클래스의 포인터가 파생클래스의 동적 객체를 가리킬 때 그 포인터를 통해 동적 객체를 제거하면...?

기본적으로 기반 클래스의 소멸자만 호출되고 파생 클래스의 소멸자는 호출되지 않음.

class Animal {

};

 

class Fish : public Animal {

};

 

Animal* a = new Fish;

delete a;

 

■ 기반클래스의 포인터가 파생클래스의 동적 객체를 가리키고, 그 포인터를 통해 동적 객체를 제거했을 때 파생클래스의 소멸자까지 호출되도록 하려면...?

기반 클래스의 소멸자를 다음과 같이 가상 소멸자(Virtual Destructor)로 만들어야 함.

class Animal {

  public :

    Animal();

    virtual ~Animal();

};

 

class Fish : public Animal {

  public :

    Fish();

    virtual ~Fish();

};

 

Animal* a = new Fish;

delete a;

→ delete a;는 Animal::~Animal 뿐만 아니라 Fish::~Fish 도 호출함.

 

[ 순수가상함수(Pure Virtual Function) ]

1. 순수 가상 함수

■ 순수 가상 함수 (Pure Virtual Function) 란?

- 선언만 있고 정의가 없는 가상 함수

- 파생 클래스에서 재정의할 것으로 예상되는 함숭에 대해 미리 호출 계획을 세워 두기 위해 정의.

 

■ 가상함수를 순수 가상함수로 만드는 방법

- 다음의 예와 같이 선언시 0을 지정하면 됨.

- 여기서 '=0'은 Pure Specifier임.

class Animal {

  public :

    virtual void breathe() = 0;

};

 

class Fish : public Animal {

  public :

    Fish();

    virtual void breathe();

};

→ Animal::breathe 는 순수 가상함수임. Animal::breathe 는 함수명만 제공하고, 내용은 Fish::breathe 에서 제공하게 됨.

 

2. 추상클래스 (Abstract Class)

■ 추상클래스란?

- 순수 가상함수를 포함하고 있는 클래스.

- 파생클래스의 행동양식을 미리 설계하기 위한 기반클래스임.

- 일반적으로 순수 가상함수를 통해 인터페이스를 제공하고 파생클래스에서 그 인터페이스를 구현하게 됨.

class Animal { // 추상클래스 Animal

  public :

    virtual void breathe() = 0;

};

 

■ 추상클래스의 특징

- 추상클래스의 객체를 생성하는 것은 불가능. ∵ 정의되지 않은 함수를 가지고 있기 때문...

- 추상클래스는 파생클래스를 정의하여 순수가상함수를 구현하였을 때 객체를 생성할 수 있다.

- 추상클래스의 포인터를 선언하는 것은 가능함.

- 추상클래스의 포인터는 흔히 파생 클래스의 객체를 가리키게 되는데, 추상클래스의 포인터가 파생클래스의 객체를 가리킬 때 그 포인터를 통해 가상함수를 호출하면 파생클래스에서 재정의한 함수가 호출됨.

Animal* a = new Fish;

a -> breathe(); // 추상클래스 Animal

delete a;

 

2. 순수 가상함수의 재정의

■ 순수 가상함수의 재정의

- 순수 가상함수는 사용하기 전에 반드시 재정의해야 함.

- 만일 기반 클래스가 순수 가상 함수를 포함하고 있는데 파생 클래스에서 그것을 재정의하지 않는다면 그 파생 클래스도 추상 클래스가 됨.

→ 기반클래스 뿐만 아니라, 그 파생클래스의 객체를 생성하는 것도 불가능 함.

class Animal { // 추상클래스 Animal

  public :

    virtual void breathe() = 0;

};

 

class Fish : public Animal {

  public :

    void swim();

};

 

Animal* a = new Animal; // error

Animal* f = new Fish;  // error

 

출처 : http://blog.daum.net/embedded-seo/449971


반응형
Posted by blueasa
, |

프로그램, 프로세스, 스레드의 차이를 알아보자.

일반적으로 프로그램(Program)은 메모장과 같은 하나의 실행 가능한 단위를 의미한다.

그러나 아시다시피 메모장은 여러개를 동시에 실행할 수 있다. 이때 각각의 메모장은 서로 다른 프로세스(Process)에 의해서 실행이 된다.

즉, 프로세스는 프로그램을 객체화 시킨 것.

각 프로세스는 4GB의 개별 주소 공간과 파일, 메모리, 스레드를 소유하게 된다. 이 말에서도 나온것 처럼 스레드는 다시 프로세스에 속한다.

하나의 프로세스는 여러개의 스레드(Thread)를 소유할 수 있으며 프로세스 자체는 껍데기일 뿐이고 사실은 스레드가 모든 명령을 수행한다.

프로세스가 최초로 생성될때 각종 관련 변수와 메모리 등을 생성하면서 메인 스레드를 생성하게 되는데 이 메인 스레드가 사실 모든 명령들을 처리한다.

그러나 사용자가 원하면 멀티 스레드 (Multi-Thread)가 가능한데 즉, 하나의 프로세스가 여러개의 스레드를 가지고서 다양한 작업을 분산 시켜 처리하도록 하는 것이다.

사용자가 임의로 타이머를 만들어서 규칙적인 시간에 일정한 작업을 처리하는 것보다 (또는 PeekMessage를 이용한 방법보다) 운영체제가 알아서 시간을 쪼개에 스레드에게 작업을 나누어 주는 것이 훠어어얼씬 효율적이고 빠르단다.

다만, 멀티 스레드가 가지는 문제점은 하나의 프로세스안에 존재하는 스레드들은 같은 코드와 주소공간, 그리고 전역 변수를 '공유'하게 됨으로써 생기는 '자원 경쟁' 혹은 '비동기 문제'이다.

하나의 프로세스 안에 있는 1번 스레드가 전역 변수를 변경하고 있는 동안 다른 2번 스레드가 그 값을 참조하거나 다시 변경하게되면 사용자가 예상치 못한 이상한 값들로 바뀌어 버리는 문제가 생기게 된다.

그래서 이것을 해결하기 위해서는 가장 좋은 것이 스레드들이 공유하는 전역 변수 자체를 안만들거나, 혹은 스레드 지역 기억장소 (Thread Local Storage) 공간을 사용해야 한다.

TLS는 다른 말로 쓰자면 '하나의 스레드에 대해서만 전역 변수인 공간'이다.

이렇게 복잡하게 설정을 해야 하는 이유는, C의 런타임 라이브러리가 애당초 Multi-Thread개념이 생기기 전에 만들어 졌기 때문이다.

기존 C 런타임 라이브러리는 함수들이 주로 Static 을 많이 사용하고 있는데 (특정 함수에서) 이 Static 변수들이 스레드 간에 '자원 경쟁' 혹은 '비동기' 때문에 엉뚱한 결과를 내는 현상을 가져왔다.

그래서 이걸 뜯어고치기되 기존 C 런타임 라이브러리를 동시에 유지하기 위해서 MLS가 도입된 것이다.

출처 : http://diehard98.tistory.com/entry/프로그램-프로세스-스레드

반응형
Posted by blueasa
, |

메모리를 할당받기 위해 사용하는 malloc() 함수는 일반적으로 glibc에 포함된 메모리 할당자에서 구현이 되어있다. 현재 glibc에서 사용하는 메모리 할당자는 ptmalloc(http://www.malloc.de/en/) 이다.

메모리 할당자의 역할은 brk/sbrk/mmap 등을 사용해서 시스템으로부터 큰 메모리 영역을 할당 받아서, 이것을 적절하게 분할하여 어플리케이션이 요청하는 메모리 할당을 처리하게 된다. 즉, malloc()을 호출하더라도 항상 시스템콜을 통해서 시스템으로부터 메모리를 할당 받는게 아니다. 그리고 반대로 메모리를 해제하는 경우도 즉시 시스템으로 반환되지 않고, 메모리 할당자가 자유 메모리 리스트에 추가해서 나중에 프로그램이 다시 메모리 할당을 요청하는 경우 재사용하게 된다.

따라서 빈번하게 메모리를 할당하고 반납하는 프로그램의 경우 메모리 할당자에서 단편화(fragmentation)가 발생해서 실제로 요청한 메모리보다 더 많은 메모리를 시스템으로부터 할당받는 문제가 발생하게 된다. mallinfo()를 사용하면 시스템으로부터 얼마를 할당받았고, 실제 사용은 얼마나 하고 있고, 사용은 안하지만 반납은 안한 메모리가 얼마인지 등의 메모리 사용량 정보를 자세히 확인할 수 있다. 그러나 struct mallinfo 구조체의 멤버들이 int 타입으로 되어있어서 사용량이 큰 경우 제대로된 결과를 표시할 수 없는 문제점이 있다.

그래서 규모가 큰 어플리케이션들은 glibc의 기본 메모리 할당자인 ptmalloc이 메모리 단편화 문제가 심하다고 하여 다른 메모리 할당자를 사용하는 경우도 있다. FireFox의 jemalloc이 대표적인 예이다.

현재 개발 중인 메모리 기반의 데이터베이스 프로그램도 이런 메모리 단편화 문제로 상당히 많은 양의 메모리를 손해 보는 문제가 있었다. 대략 1MB 당 0.2MB의 메모리가 단편화로 손해 보는 것으로 조사 되었다.

그래서 이런 문제를 해결하기 위해 Google Perf-tool(http://code.google.com/p/google-perftools/)에 포함된 TCMalloc 메모리 할당자를 도입해서 사용했다. TCMalloc은 속도도 빠르고 메모리 단편화 문제도 ptmalloc에 비해 상당히 적었다. 그러나 시스템으로부터 할당받은 메모리를 전혀 반환하지 않는 점이 단점이라고 할 수 있다. 즉, 메모리를 한번이라도 할당 받으면, 해당 메모리는 프로그램이 해제를 하더라도 시스템으로 전혀 반환되지 않고 프로세스가 다음에 사용할 때까지 잡고 있는 것이다.

만약 프로그램의 메모리 할당 패턴을 정확하게 알고 있다면 자신만의 메모리 할당자를 만드는것도 한 방법일 수 있다. 하지만 brk/sbrk 시스템콜은 프로세스의 데이터 세그먼트를 선형적으로 확장해서 메모리를 할당받기 때문에 반환 과정을 구현하기가 쉽지 않다. 반면에 mmap은 속도는 조금 느리지만 할당받은 메모리를 munmap으로 쉽게 반환할 수 있는 장점이 있다. 즉, 프로그램이 사용할 전체 메모리량을 미리 예측할 수 있다면 해당 크기만큼 mmap으로 할당 받아서 적절하게 분배하여 사용한 후 munmap으로 반환하도록 구현하면 된다.


<참고 자료>
* Advanced Memory Allocation
* Building your own memory manager for C/C++ projects



반응형

'Programming > C/C++' 카테고리의 다른 글

[펌] C++, 가상함수,순수가상함수  (2) 2010.03.14
[펌] 프로그램, 프로세스, 스레드  (0) 2010.03.13
[펌] 동기화 객체  (1) 2010.03.13
[펌] 유니코드 코드변환  (0) 2010.03.13
[펌] 문자열 인코딩  (0) 2010.03.13
Posted by blueasa
, |
1. Critical Section (임계구역)   
   - 접근 가능한 스레드는 하나
   - 동기화는 하나의 프로세스 안에서만 이루어진다
   - 예) 단일 프로세스 상에서의 접근

2. Mutex (상호배제)
   - 접근 가능한 스레드는 하나
   - 여러 프로세스에서 하나의 자원에 접근하는 경우에 사용된다
   - 예) 다중 프로세스 상에서의 접근

3. Event (이벤트)
   - Mutex와 거의 동일하다
   - 접근 제어 보다는 특정 상황이 발생될 때 이를 알리는 목적으로 사용된다
   - 예) 특정 상황에서 발생되는 접근

4. Semaphore (세마포어)
  - 하나의 자원에 접근할 수 있는 프로세스의 수를 지정할 수 있다
  - 예) 동접자 제한 등

[출처] 동기화 객체|작성자 아스라이



1. 뮤텍스(Mutex) : 커널

모든 thread에 사용될 수 있는 동기화 객체.

mutex를 신호상태로 생성하고 thread에서 wait함수를 호출하면 비신호 상태가 된다. 즉 다른 thread에서 접근 불가.

ReleaseMutex를 사용하여 다시 신호상태로 돌릴 수 있다.

프로그램 중복 실행을 방지하기 위해 사용되기도 한다.

//////////////////////////////////////////////////////////////////////////
 // 프로그램 중복 실행 방지

 HANDLE hMutex = CreateMutex( NULL, FALSE, "MyProject" );
 if(GetLastError() == ERROR_ALREADY_EXISTS)
 {
  return FALSE;
 }

 
2. 크리티컬섹션(Critical section) : 유저

같은 프로세스내에 사용될 수 있는 동기화 객체.

EnterCriticalSection으로 비신호, LeaveCriticalsection으로 신호 상태가 된다.

LeaveCriticalSection 사용하면 다른 thread의 접근이 가능하다.

 
3. 세마 포어(Semaphore) : 커널

뮤텍스와 비슷하다. 하지만 접근 가능한 thread의 개수를 지정할 수 있다.

세마포어는 내부적으로 카운트를 가지고 있는데, wating thread가 있으면 카운트가 하나씩 줄어든다.

그리고 그 카운트가 0이 되면 비신호 상태가 되어 다른 thread에서 접근이 불가능하다.

ReleaseSemaphore 사용 시 카운트가 1 증가하여 다른 threa에서 접근이 가능해진다.

WaitforSingleObject 처럼 thread를 정지시키는 효과를 부여하기 위해 모든 thread를 비신호 상태로 만들기도 한다.


반응형
Posted by blueasa
, |

GDI+를 사용하게 되면 문자는 기본적으로 유니코드를 사용하게되어 있다.

그렇기 때문에 ANSI코드를 직접 입력하게 되면 문자가 깨져나오는 불상사가 생기게 된다.

그렇기때문에 #define UNICODE 메크로를 사용하여 코드가 유니코드를 사용하게 해야하는데 이는 아직 익숙치 않은 것이라 ANSI를 UNICODE로 변환하여 사용하는 방법을 사용한다. 물론 익숙하지 안은 것이외에도 반드시 코드변환을 사용해야 할 경우는 있다.

텍스트 파일에 있는 문자를 읽어 올 때에는 아직까지 모든 텍스트 파일의 문자는 ANSI로 되어 있기 때문에 이를 UNICODE로 변환 해야하는 경우가 있을 수 있다.

 

ANSI 코드를 UNICODE로 변경시켜주는 API 함수는 MultiByteToWideChar()이 있다

MultiByteToWideChar

(

UINT CodePage,               //변환대상의 코드페이지

DWORD dwFlag,               //변환 Flag

LPCSTR lpMultiByteStr,      //변환대상 MBCS 문자열

int cbMultiByte,                //변환대상 문자열 길이 -1이면 자동으로 길이 계산

LPWSTR lpWideCharStr,      //변환결과를 저장할 버퍼

int cchWideChar                //변환결과 문자열 길이 보통 변환대상의 2배로 설정한다.

)

 

UNICODE 를 ANSI로 변환할때는 WideCharToMultiByte()를 사용한다.

MultiByteToWideChar

(

UINT CodePage,                  //변환대상의 코드페이지

DWORD dwFlag,                  //변환 Flag

LPCWSTR lpWideCharStr,      //변환대상 MBCS 문자열

int cchWideChar,                 //변환대상 문자열 길이 -1이면 자동으로 길이 계산

LPSTR lpMultiByteStr,           //변환결과를 저장할 버퍼

int cbMultiByte,                   //변환결과 문자열 길이

LPCSTR lpDefaultChar,         //변환할 수 없는 문자를 대신할 문자

LPBOOL lpUsedDefaultChar   //기본으로 설정된 lpDefaultChar여부를 확인

)

 

char *amsg = "Good programming!";

wchar_t wstr[100];

 

wchar_t *wmsg = L"Perpect programming!";

char astr[100];

 

MultiByteToWideChar(CP_ACP, 0, amsg, -1, wstr, 100);

 

WideCharToMultiByte(CP_ACP, 0, wmsg, -1, astr, 100, NULL, NULL); 

 

 

위와 같이 MultiByteToWideChar 와 WideCharToMultiByte 를 사용해서 코드를 변환 할 수 있지만 문자열이 간단한 것이라면 다음과 같이 wsprintfW 와 wsprintfA를 사용해 더욱 간단하게 코드 변환을 할 수 있다.

char *amsg = "Good programming!";

wchar_t wstr[100];

 

wchar_t *wmsg = L"Perpect programming!";

char astr[100];

 

wsprintfW(wstr, L"%S", amsg);

 

wsprintfA(astr, "%S", wmsg);

 

%S에서 s가 대문자인 것을 유의해야한다. 

 

 

API는 ANSI 와 UNICODE의 혼합 사용을 지원하기 위해 매크로를 지원하고 있다. 실제로는 무척 많은 양이지만 압축하면 다음과 같다.

일반형 UNICODE ANSI Description
TCHAR wchar_t char 문자
LPTSTR wchar_t* char* 문자열
LPCTSTR const wchar_t* const char* 문자열 상수

 

TEXT("문자열")을 사용하면 ANSI, UNICODE 에 상관없이 해당 코드 환경에 알맞게 문자열을 변환해준다.

 

출처 : http://blog.daum.net/crexy/7265111


반응형
Posted by blueasa
, |
 컴퓨터의 문자 표현체계에 대해서 얼마나 알고 계산가요? 혹 아스키 코드, 아스키 확장코드, 유니코드 이 정도로 알고 있는것은 아니신가요?? 부끄럽지만 저는 유니코드의 표현 종류가 한 가지 종류가 아닌것은 알고 있었지만 만 면밀하게 알고 있지는 않았고 오늘 필요에 의한 자료를 찾아보면서 많이 부족했었다는 것을 깨닫게 되었습니다. 유니코드에도 생각외로 많은 종류의 표시집합이 존재하더군요. 예를 들면 UTF-8, UTF-16, UTF-32, UCS2, UCS4 같은 것 말이죠.
  그래서 libiconv 라이브러리를 사용하기 위한 자료를 찾다보니 이 것에 대해서 꽤나 중요하다는 생각이 문득 들더군요. 그래서 일단은 간소하게나마 정리하는게 좋지 않을까 해서 올려봅니다.

1. 조합형( JOHAB )

 
비운이라고 생각해야 할까요? 한글을 표시하는 여러가지 표현방법 중 하나 입니다. 아주 옛날 도스에서 텍스트 에디터 같은 프로그램을 사용해보셨다면 조합형 한글, 완성형 한글등의 한글 표현 방법이 있다는 것을 아실겁니다. 그 중 하나이죠. 그런데 이 표시방법은 최근엔 거의 쓰이지 않습니다. 왜냐하면 Microsoft가 조합형이 아닌 완성형 한글을 채택했기 때문이라고 하네요. 역시 글로업 기업답게 파워가 막강한가 봅니다.
  어쨌든 표현 방식은 16비트를 사용하게 되며, 최상위 비트는 1, 그 후엔 5/5/5로 비트를 나누어 초성, 중성, 종성을 표시하게 됩니다. 말 그대로 조합형이니 만큼  만약 초성에 ㄱ, 중성에 ㅏ, 종성에 ㅇ이라는 코드를 넣게 되면 해당 글자는 ""이라는 글자를 표시하는 비트가 되게 됩니다. 무척 편리한 방법입니다. 예전 하드웨어 성능이 별로 좋지 않을때 한글 오토마타를 처리하는 것에 있어 완성형 보다 CPU도 덜 쓰고, 복잡하지도 않아 조합형이 좋다고 얘기될 때가 많았었는데 말이죠.
  위 방식은 일반적으로 자주 쓰였던 조합형의 종류이고, 조합형에도 여러가지 종류가 있었다고 합니다.

  1. N바이트 조합형

   
이 형태의 출력 방법은 자음 모음을 각각 분리하여 보관된 문자 형태에 맞는 값을 가지고 저장하는 방법입니다. 예를 들면 다음과 같네요. "블로그" 라는 문자에 대한 표기방법은 "ㅂㅡㄹㄹㅗㄱㅡ" 가 됩니다. 초기 때 자주 사용되었다고 하네요. 최대 5바이트까지 사용될 수 있었기 때문에 비효율성 때문에 사라졌을것 같습니다.

  2. 3바이트 조합형

   
이 방법은 위에 언급한 초,중,종의 방식을 하나의 바이트로 사용한 것입니다. 만약 종성이 존재하지않는 ""와 같은 문자일 경우 채움 문자를 적어넣었다고 합니다. n바이트 조합형 보다 효율적이기는 하지만 그래도 비효율 적이죠?

  3. 2바이트 조합형

    흔히 조합형이라고 하면 일컫는 방식입니다. 최상위 비트가 1, 초,중,종성을 각각 5비트로 나누어 표시한 방식이죠. 문제점은 두 번째 바이트의 최상위 비트가 0일 수 있기에 문자열 검색에서 문제가 생긴다고 합니다. 뭐 데이터가 NULL값과 동일하게 되어 문제점이 생기는 것 같습니다.

2. 완성형( EUC-KR )

 
이 문자 집합은 KS X 1001, KS X 1003 인코딩을 사용하는 문자형 입니다. EUC(Extended Unix Code, 확장 유닉스 코드)라는 이름에 걸맞게 확장 코드 중 KR 즉 한글을 표현하는 방식입니다. 표현 방식은 일반 아스키 코드들은 KS X 1003형식을 배당하고, 그 이상의 한글 코드들은 KS X 1001형식을 배당하게 됩니다. 보통 많이 들으셨던 것이 바로 이 방식일 겁니다. 문자 코드의 영역은 대충 아래와 같이 된다고 하는군요.
  • 0x21 ~ 0x2C: 특수 문자(문장 부호, 그림 문자 등), 한글 낱자, 괘선 조각, 외국 문자(히라가나, 가타카나, 그리스 문자, 키릴 문자 등)
  • 0x30 ~ 0x48: 한글 글자 마디 영역. 자주 쓰이는 2350자만 가나다 순서대로 배열
  • 0x49: 사용자 정의 영역 A
  • 0x4A ~ 0x7D: 한자 영역. 4888자를 한글 독음 순서대로 배열했으며, 독음이 다르고 모습이 같은 한자는 중복
  • 0x7E: 사용자 정의 영역 B

  이 문자 표현 형식의 단점은 모든 한글을 표현할 수 없는 단점이 생깁니다. 위에 언급했다 시피 자주 쓰이는 2350자만 배열하였기 때문에 문제가 되는 것이죠.

B0 0 1 2 3 4 5 6 7 8 9 A B C D E F
A0  
B0
C0
D0
E0
F0

  위 표는 KS X 1003에 해당하는 한글 부분 표 입니다. 위키피디아에 존재하는 것인데, 저장하는 방식이 일괄적이더군요. 일단 FIRST BYTE에 해당하는 문자는 제일 왼쪽 상단에 존재하는 B0 이라는 문자입니다. 그리고 두 번째 BYTE는 해당 글자에 위치하는 행+열에 해당되는 값이죠. 만약 "" 라는 문자를 저장하여 Binary로 표기하게 된다면 B0 C2 라는 데이터로 걔의 표기가 가능하게 됩니다.

http://ko.wikipedia.org/wiki/KS_X_1001_%ED%95%9C%EA%B8%80_%EB%B6%80%EB%B6%84_%ED%91%9C 

3. 확장 완성형( CP949 )

 
완성형에서 자주쓰이는 2350자만 표현 가능하게 된 점이 있기에 모든 한글을 표기하기 위하여 추가적으로 확장된 방식입니다. 윈도우 95부터 채택되어 사용이 되었다고 하네요. 이 형태는 완성형인 EUC-KR과 하위 호완성이 존재합니다. 그리하여 A1~FE까지 사용하는 범위의 EUC-KR 과 더불어 빈 공간에 표현하지 못하는 한글을 추가적으로 맵핑하여 한 형태죠. 일단 이 것의 문제는 한글에 대한 순서가 빈공간에 차례대로 끼워넣은 만큼 순서에 대한 정렬 문제가 발생하게 됩니다.
 
4. UCS2

 
흔히 많이 보는 Windows에서의 Unicode라고 생각하시면 됩니다. 이 형태는 각국의 문자표기에 따른 문제점을 해소하기 위해서 바이트의 문자 표기를 위해 등장했고, 일률적인 형태를 위해 영문 문자 마저 2바이트의 공간을 사용하게 됩니다. 결국 영문 같은 경우 공간을 낭비하게 된다고 생각할 수도 있겠군요. 하지만 전체가 2바이트이기 때문에 기타 UTF 방식의 표현 형태보다는 처리가 간단하다고 할 수 있습니다. Windows 같은 경우엔 내부적으로 UCS2 문자열을 사용하는 것으로 알고 있습니다. 그 이유는 속도 처리가 빨라서라고 하는군요^^.
  그래서 일반적으로 Windows 에서 개발 프로그래밍을 할 때에 L"아아아"와 같은 형태의 문자을 사용하면 UCS2의 방식을 따르게 됩니다. 물론 인코딩은 별개로 생각되야 하구요^^.

5. UCS4

 
2바이트를 사용하는 UCS2에 비해 UCS4는 하나의 글자를 4바이트로 표현합니다. 그 만큼 문자의 공간 낭비가 심하다고 할 수 있죠. 하지만 UCS2에서는 65,536개 이상의 문자 표현이 불가능하기 때문에 나타난 방식이라고 생각이 되네요 일단 기본적으로 UCS2로 표현이 되는 문자들은 UCS로 처리 표시되고 그 영역을 벗어나는 부분만 UCS4로 처리를 하게 됩니다. 그리고 GCC 같은 경우 유니코드 문자열은 Windows개발툴과 다르게 UCS2가 아닌 UCS4로 사용되므로 주의가 필요합니다. 이에 대한 소스코드의 호환성을 잘못 썼다간 아이쿠 하는 사태가 발생할 수도 있다는 아주 무시무시한 사실이 @0@

6. UTF-8

 
최근 많이 들어보셨던 단어 인가요? 개발자가 아닌 일반 사용자 분들도 최근엔 자주 보셨던 단어 일 겁니다. 바로 "URL을 항상 UTF-8로 보내기" 라는 옵션 때문이죠. 이 것의 체크 여부에 따라서 한글로 되어있는 그림이 보였다 보이지 않았다 하는 점 때문에 조금 이러저러한 문제가 있을겁니다. 하지만 결과적으로 말하자면 일단 UTF-8 형식을 사용하는 것이 옳다고 얘기할 수 있습니다. 왜냐구요? 이 것은 UCS2에서 말했다 시피 영문 코드마저도 2바이트 공간을 항상 사용해야 하기 때문에 존재하는 낭비를 줄이기 위해서 나온 형태 중에 하나 이기 때문이죠. 다만 다른 점은 UCS2, UCS4는 형태 그대로의 문자집합인데 비하여 하나의 인코딩 방법이라고 할 수 있습니다.
000000-00007F 0xxxxxxx ASCII와 동일한 범위
000080-0007FF 110xxxxx 10xxxxxx 첫 바이트는 110 또는 1110으로 시작하고, 나머지 바이트들은 10으로 시작함
000800-00FFFF 1110xxxx 10xxxxxx 10xxxxxx
010000-10FFFF 11110zzz 10zzxxxx 10xxxxxx 10xxxxxx UTF-16 서로게이트 쌍 영역 (yyyy = zzzzz - 1). UTF-8로 표시된 비트 패턴은 실제 코드 포인트와 동일

  가장 왼쪽은 유니코드 의 범위이고 이 범위에 따라서 해당 문자열에 관련된 Binary 값이 변환될 수 있습니다. 그리하여 문자열에 대한 영어 공간을 절약할 수 있는 것이죠.
  여기서 일어날수 있는 문제점은 바로 Microsoft 문서 편집기와의 호환성 문제 입니다. 흔히 BOM 이라고 불리는 Byte Order Mark 라는 것이 UTF-8에서는 아스키와의 확장성을 띄기 때문에 실제로 사용되지 않는데 최고의 문서 편집기(?)인 Notepad를 보시면 텍스트파일을 읽을 때 UTF-8에 해당하는 BOM마크를 필수적으로 써주어야 합니다. 그로인해 문제점이 생길 수 있을것이라 생각됩니다.

7. UTF-16

  이 인코딩은 UTF-8과는 약간 의미가 다릅니다. UTF-8 같은 경우 아스키와의 호환성, 공간절약을 목적으로 사용한 것이지만 UTF-16은 65,536개의 문자 이상의 표현할 수 없는 문자를 표현하기 위한 인코딩 방식입니다. 고로 UTF-16은 UCS2와 거의 동일하다고 보시면 됩니다. 그래서 Notepad에서도 저장방식이 UTF-8 방식과 Unicode Big Endian, Unicode Little Endian 이 있는 것 같군요. 표현하지 못하는 기본 방식에 대해서는 위키피디아를 참조 하시면 좋을 것 같습니다.

  후.. 너무 많군요.. 단순히 몇개 밖에 안될 것이라 생각했던 것이 너무나 많습니다. 저도 자료를 찾아 정리해가면서 적은 것이라 부정확한 정보가 존재할 수도 있겠군요. 하지만 한 곳의 정보만 참조한 것이 아니라 여러군데를 찾으면서 한 것이라 꽤 정확하게 정리한 것 같습니다. UTF-32 와 같은 추가적인 인코딩방법도 존재하는데, 이 런 인코딩을 필요할 때 자세히 알아서 필요할 때 사용하면 될것 같습니다.
  FreeType을 사용하게 되면서 결과적으로 아주 많은 것을 공부하게 된것 같습니다. 문자열 셋에 대한 여러가지 인덱스 들과 변환 방법을 통한 여러가지 종류의 텍스트 지원을 생각하다 보니 이런 저런 문제점이 발견되고 그 것을 해결하기 위해 자료를 찾다보니 여기 까지 오게 된것 같군요. 나름대로 쓰기 편하게 FontLoader같은 클래스를 만들고 있으니 잘 된다면 공개를 해보도록 하겠습니다.


 

출처 : http://www.filewiki.net/tc/81

반응형
Posted by blueasa
, |