이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받을 수 있습니다.

반응형
원문 : http://poseidon.cse.pusan.ac.kr/%7Edhlee/course/f06/java/

CA24148:  JAVA Programming

Fall Semester 2006
Lecture: Monday/Wednesday, 13:00-14:30, A06-6202 

Instructors: DoHoon Lee
AI : Yongho Ju(yhju at pearl dot cs dot pusan dot ac dot kr)

 

Description: syllabus

Textbook:프로그래머를 위한 J AVA2, 최종명외2, 홍릉출판사
Grading:
Attendence and etc.(5%), Mid(20%), Final Exam (20%), Assignment(20%), Term Project(35%)

NOTICE : 과제물과 학기말과제에 관한 사항이나 보다 자세한 일정은 추후 수업이 진행되면서 공지할 예정임.

Preliminary syllabus

Week

Contents

Textbook Reading

Lecture Note/Slide

Lab

Assignments/Term Project

1

교과목 소개 JAVA 언어 개요

1-83

Chap00, Chap01-slide, Chap01-slide4

 

Lab01, 범하기쉬운실수리스트

ESPA 등록및사용법, jbuilder설치및사용법, jedit 설치및사용법, eclipse 설치및사용법 

2

연산자와 제어구조

84-86

Chap02-slide, Chap02-slide4,

 Lab02 dec2bin.java dec2bin1.java dec2bin2.java TimeChk.java

 

3

기본적인 파일입출력

11장

Chap11-slide, Chap11-slide4

ConsoleReader.java ConsoleWriter.java

Lab03 ,lab3_1.java TestBufferedReader.java TestPrintWriter.java TestScanner.java

lab3_2.in, lab3_3.in

 

4

객체지향프로그래밍I

3장

chap03-slide, chap03-slide4

lab04

HomeWork#01 Due:10/13

5

객체지향프로그래밍II

4장

chap04-slide, chap04-slide4

 

 

6

객체지향프로그래밍III

5장

 

lab05

 

7

객체지향프로그래밍IV

6장

chap0501-slide, chap0501-slide4

 

Mid Exam(문제지)

8

객체지향프로그래밍

7-10장

 

lab06

Term Project 공지사항

proposal for term project

발표평가서

9

객체지향프로그래밍

 

 

lab07(update), SetTest.java

 

10

자바GUI프로그래밍

 

chap0502, chap0502-slide4

 

PresentationI /

11

GUI

 

chap07, chap07-slide4

Lab08

Presentation2

12

GUI

 

 Chap08, chap08-slide4

Lab09

 

13

 

 

 Chap10-13

 Lab10, Lab10.zip

중간보고서제출

14

네트워크 프로그래밍

 

Chap10-13-slide4

 Lab11

 

15

       

Final Exam

12/16 10:00-13:00

장소:제도관4층 실습실(1) - 중간고사와 동일

 

      최종발표공지사항

Presentation for final report

12/23 10:00-

장소:자연대연구실험동(첨단관) 404호 세미나실

 

 

반응형

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받을 수 있습니다.

반응형
간단한 메뉴바를 만들어보겠습니다! :)
구구절절 설명없고 소스에 간단한 주석과 결과물만 있으니 직관적으로 배워보시길. ㅎㅎ


/*
 * fileName : Test.java
 */
import java.awt.*;


public class Test
{

	public static void main(String [] args)
	{
		//메뉴바-메뉴-아이템
		MenuItem menuItem = new MenuItem("참치김밥");
		
		//메뉴바-메뉴
		Menu menu = new Menu("김밥");
		menu.add(menuItem);
		
		//메뉴바
		MenuBar menuBar = new MenuBar();
		menuBar.add(menu);
		
		//윈도우 프레임
		Frame frame = new Frame("김밥천국");
		frame.setSize(800,600);
		frame.setMenuBar(menuBar);
		frame.setVisible(true);
		
	}
}

반응형

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받을 수 있습니다.

반응형






Levenshtein Distance Algorithm: C++ Implementation

by Anders Sewerin Johansen




// Include STL string type

#include 

// Include STL vector type (dynamic array)

#include 

int distance(const std::string source, const std::string target) {

  // Step 1

  const int n = source.length();
  const int m = target.length();
  if (n == 0) {
    return m;
  }
  if (m == 0) {
    return n;
  }

  // Good form to declare a TYPEDEF

  typedef std::vector< std::vector > Tmatrix; 

  Tmatrix matrix(n+1);

  // Size the vectors in the 2.nd dimension. Unfortunately C++ doesn't
  // allow for allocation on declaration of 2.nd dimension of vec of vec

  for (int i = 0; i <= n; i++) {
    matrix[i].resize(m+1);
  }

  // Step 2

  for (int i = 0; i <= n; i++) {
    matrix[i][0]=i;
  }

  for (int j = 0; j <= m; j++) {
    matrix[0][j]=j;
  }

  // Step 3

  for (int i = 1; i <= n; i++) {

    const char s_i = source[i-1];

    // Step 4

    for (int j = 1; j <= m; j++) {

      const char t_j = target[j-1];

      // Step 5

      int cost;
      if (s_i == t_j) {
        cost = 0;
      }
      else {
        cost = 1;
      }

      // Step 6

      const int above = matrix[i-1][j];
      const int left = matrix[i][j-1];
      const int diag = matrix[i-1][j-1];
      const int cell = min( above + 1, min(left + 1, diag + cost));

      // Step 6A: Cover transposition, in addition to deletion,
      // insertion and substitution. This step is taken from:
      // Berghel, Hal ; Roach, David : "An Extension of Ukkonen's 
      // Enhanced Dynamic Programming ASM Algorithm"
      // (http://www.acm.org/~hlb/publications/asm/asm.html)

      if (i>2 && j>2) {
        int trans=matrix[i-2][j-2]+1;
        if (source[i-2]!=t_j) trans++;
        if (s_i!=target[j-2]) trans++;
        if (cell>trans) cell=trans;
      }

      matrix[i][j]=cell;
    }
  }

  // Step 7

  return matrix[n][m];
}

반응형

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받을 수 있습니다.

반응형

출처 : http://www.jspgeek.com/tt/index.php?pl=17426&PHPSESSID=18639be6718deed229e55fb4b90c6897


Java의 역사는 Java의 실행 환경인 Java VM의 실행 속도에 대한 도전의 역사였다고 해도 과언은 아니다. 어떻게 하면 더 빨리 실행할 수 있을까 고민하는 Java VM이 걸어온 그 길에서 아키텍처의 변천사를 살펴보면, Java VM에 대해서 더욱 깊이 이해할 수 있는 기회가 될 수 있을 것이다.

Java의 역사


Java는 1995년 5월 23일에 개최된 「Sun World Expo」의 디벨로퍼 컨퍼런스에서 공개되었다. 「Java 탄생의 페이지」에 의하면 이날이 Java의 생일이라고 되고 있다. 썬마이크로시스템이 말하니까 틀림이야 없겠지만, 원래 Java의 전신은 Oak라고 하는 언어였다. Oak는1990년 12월에 시작된 Green Project의 일환으로서 개발된 언어이다. 하지만, Java는 1995년에 태어났으니까 Oak가 탄생한 1990년에 벌써 태어날 준비를 하고 있던 것이나 다름없다.


우리에게 친숙한 마스코트 "Duke"도 이 Green Project의 "중요한 부산물"이라고 Green Project의 홈페이지에 게재되어 있다. 가전업계의 열악한 자금 환경으로 받아들여지지 않았다고 한다. 실제로 Green Project의 주 성과물인 "*7"(StarSeven:대형 PDA라고도 한다.)도 햇빛을 보지 못한 것이 사실이다. 물론, 여러 가지 비즈니스적인 사정 때문에, 또는 단지 운이 없었다고 하는 견해도 있을 수 있다. 그러나, 그 크기, 무게를 생각하면, 그 것을 한 손에 쥐고 조작할 수 있는 체격은 미국인밖에 없을 것이다.


여담은 접어두고, 언어로서 Oak의 계보를 굳이 표현하자면, 아버지는 C++, 어머니는Smalltalk(interpreter라고 하는 관점에서)를 들 수 있겠고, 물론 객체 지향언어의 정통이라고 하는 사람도 있을 수 있다. (아버지가 Smalltalk로 어머니가 C++ 일까? 아니, 아버지가 C++, 어머니가 Green Project이다!). Gosling씨에 의하면, Oak는 lisp나 다른 언어에도 영향을 받았다고 한다. 초기의 Oak 매뉴얼에 따르면, Green Project의 페이지에 가면 PostScript(그립다!)의 Ver0.2을 다운로드 할 수 있다고 한다.


1995년 5월에 탄생한 Java는 1996년에 개최된 「JavaOne」에서 소개되었으며, 80 이상의 협찬 기업과 5000명 이상의 참가자로 대성황을 거두었다. 이 행사는 OpenSurce 플랫폼을 사용하면서, 포인터를 중시하고 있는 개발자들에게 오브젝트 언어가 Web의 개발 환경에 얼마나 딱 들어 맞는지 보여주는 상징적인 일이었다.


역사적으로 보면, Java의 당초 생각들은 Gosling씨의 홈페이지에 있는 Java 탄생 당시의 「original Java White Paper」에 기재되어 있다. 이 정책에 따르면 James Gosling씨가 Green Project에서 가전제품에 내장시키기 위한 용도로 Oak를 사용했으며, 현재에 이르기까지 그 정신은 아직 살아 있다. 아래와 같은 것이다. (편집: 현재, Gosling씨의 홈페이지는 폐쇄되고 있다. original Java White Paper는 현재 다음 URL로 참조할 수가 있다. 「Focus on Java:on Overview, original Java White Paper by James Gosling」).


(1)Simple

   프로그래머에게 단순해야 알기 쉽다.

(2)Object-Oriented

   객체 지향적이다.

(3)Distributed

   분산 오브젝트이다.

(4)Robust

   안정하다.

(5)Secure

   안전하다.

(6)Architecture Neutral

   시스템 환경에 독립적이다.

(7)Portable

   데이터 타입이 통일되어 있다.

(8)Interpreted

   Java의 코드를 번역하면서 실행한다.

(9)High performance

   메모리 효율, 동작 속도가 고성능이다.

(10)Multithreaded

   multi-thread이다.

(11)Dynamic

   실행 시에 동적으로 클래스를 로드한다.


위의 11개 항목은 White Paper에 번호 없이 쓰여져 있다. 앞으로, 이 "위대한 정신"의 항목에 따라, Java VM의 아키텍처 변천을 설명하겠다.


Simple, Object-Oriented, Distribute


Java는 포인터를 사용하지 않는 언어로써, 프로그래머가 메모리 관리를 생각하지 않아도 되어 편리해졌다. 구문도 매우 명쾌하다. 그리고 C++의 객체 지향을 계승하고 있다. URL을 지정해 원격의 오브젝트에 접근할 수 있다. 이런 기능들이 분산오브젝트 환경에 딱 들어 맞는 것이다.


White Paper에는 없지만, 네트워크상의 다른 시스템에 있는 오브젝트를 호출하는 분산 오브젝트 환경에 포인터를 사용하게 되면 머리가 아플 수도 있다. 여기서 Java 밖에 모르는(포인터를 모르는) 운(?)이 좋은 독자를 위해서, 포인터에 대해서 설명을 해 보자. 포인터란, 변수에 값이 대입된다는 개념에서, 컴퓨터의 메모리와 주소의 관계를 실현한 것이다. 우선 변수에 메모리의 주소 값을 대입해 둔다. 그리고, 그 주소에 저장되어 있는 메모리 내용을 참조할 수 있도록 한다. 이 도구가 바로 포인터이다. 포인터 자체의 형태는 포인터형이라고 알고 있지만, 포인터에 저장되어 있는 주소에 어떤 형태의 값이 저장되어 있는지 모른다. 즉, 데이터를 저장했을 때의 형태를 기억하지 못하기 때문에, 포인터는 취급할 수 없는 것이다. 게다가 주소가 가리키는 메모리에 대한 형태는 다이나믹하게 지정할 수 있다. 이런 사항들은 분산오브젝트에서는 곤란한 문제를 가져오게 된다. 예를 들면, 어느 프로그램이 다른 시스템에 있는 오브젝트를 호출하려고 한다. 그 오브젝트에 포인터가 있을 경우, 호출하는 프로그램은 상대방의 시스템의 오브젝트를 만질 수 있는 모든 메모리를 하나하나 카피해 오지 않으면 잘 작동하지 않게 된다.


옛날, CORBA라고 하는 분산 오브젝트 브로커는 OMG라고 하는 컴퓨터 산업의 실력가로 이루어진 컨소시엄에 의해서 탄생하였다. 그 중 C++은 네트워크에 분산된 오브젝트를 참조할 때 메모리 카피의 책임이 프로그래머에게 있었기 때문에, 개발자에게는 부담이 많았다. 프로그래머가 아무렇지도 않게 포인터를 사용할 경우, 네트워크상에 대량의 메모리 전송을 일으켜 버릴 수도 있다. 게다가 그 오브젝트의 라이프 사이클, 메모리의 확보, 릴리스 등이 원격이라고 하는 것을 의식해 프로그래밍하지 않으면, 메모리 어플리케이션이 종료하기도 한다. 물론 서버측도 그 위험에 처하기 마련이다.


포인터의 개념이 없는 Java에서는, 이러한 일이 모두 일어나지 않는다. 컴파일러가 포인터를 허락하지 않기 때문이다. CORBA 스팩에서도 Java의 임플리멘테이션이 있다. Java에는 RMI라고 하는 네트워크 원격 오브젝트를 호출하는 스팩이 있어, 쉽게 이용할 수 있다. 지금, 전성기인 J2EE 스팩, SOAP 등에 있어도 말이다. 이것은 곧, Simple, Object-Oriented, Distributed라고 하는 사상이 포함되어 있었기 때문이다.


Robust, Secure, Architecture Neutral, Portable


안정, 안전, 아키텍처 독립, 휴대용이라고 하는 개념은, 다양한 기종, OS, 언어환경이 혼재하는 인터넷에서는 필수 불가결한 요소이다. 메모리 관리를 프로그래머에게 강요하지 않기 때문에 '안정성'을 확보할 수 있다. 인스턴스가 없는 오브젝트를 참조하면 null exception 오류가 발생하는데, try-catch구문으로 둘러싸면 프로그램으로 처리도 가능하다. 메모리를 직접 액세스할 수 없기 때문에, 이를 악용해 PC의 부트 레이어에 불필요한 코드를 심으려고 해도 불가능 하다.


White Paper에서는 Secure 구현 정책으로 virus-free, tamper-free를 제창하고 있다. 현실 세계의 브라우저에서는 여러 구멍이 발견되고 있는 것이 현실이지만, 이상적인 목표로는 virus-free, tamper-free이다.


인터넷으로 연결되어 있는 시스템의 데이터, 실행 형식을 의식하지 않는 것이 분산 오브젝트의 필수 조건이다. 이런 생각이 Architecture Neutral, Portable이다. 예를 들어, 프로그래머가 이 오브젝트는 이 메이커의 시스템에서 실행되므로 정수는 16bit가 되어야 해. 그러니까 이쪽의 시스템에서 보내는 변수는 16bit 이하로 떨어뜨려 두지 않으면…… 이라고 생각하는 것은 모든 종류의 시스템이 접속된 인터넷상에서는 불가능에 가깝다.


Interpreted


드디어 구현의 이야기 차례이다. Interpreted란, 번역해 실행한다는 의미다. 포인터의 개념을 배제하였기 때문에, 메모리 관리의 책임을 언어측면에서 흡수하기 위해서는, 메모리 관리를 실행 환경에서 제공해야 한다. 이와 더불어, 플랫폼 의존성을 배제하고, 어느 시스템에서나 동일하게 동작하고 데이터도 플랫폼에 의존하지 않는 통일된 표현을 유지하기 위해서는, Smalltalk와 같은 interpreter 방식을 사용하는 것이 좋다.


interpreter의 상대어로 컴파일러가 있다. 컴파일러는 미리 자연 언어에 가까운 원시코드를 CPU가 즉시 실행할 수 있는 기계어로 번역해, 나중에 한번에 실행하는 것이다. 그에 반해 interpreter는, 원시 코드를 읽으면서, 순서대로 실행한다. interpreter의 시초라고 하면 Bill Gates가 만들었던 BASIC이라고 생각한다. Java는 BASIC과 달리, 원시 코드를 한 번 아르바이트 코드라고 하는 중간 코드로 번역하고 나서, 그것을 interpreter가 실행하게 되어 있다.


그런데, Java와 같은 중간 코드 방식의 interpreter는, Java가 최초이다. Smalltalk라고 말할 수도 있겠지만 그렇지 않다. 세상의 연구자의 논문을 닥치는 대로 읽어 보지 않으면 정말로 무엇이 최초 인가, 올바른 결론은 나오지 않는다. 그러나, 상용 여부와 대중적인 의미에서 그 시작을 찾아보면 UCSD Pascal이라고 생각한다. 1970년대 초의 마이크로 컴퓨터 시대는, 메모리가 매우 고가여서 CPU는 쓸데없이 늦은 기기가 될 수 밖에 없는 환경이었다. 그런 가운데, 구조화 프로그래밍과 풍부한 포맷 선언을 가졌던 UCSD Pascal의 중간 코드 컴파일 방식은, 개발자 NiklausWirth 박사의 "언어스팩과 최적화는 독립된 문제이다"라고 하는 논의를 증명하기 위해서 만들었다고도 한다. 그러나, 실제로 임플리멘테이션 측면에서는 꽤 유리했다.


중간 코드 컴파일러란, 자연 언어에 가까운 프로그램 언어를 언어스팩에 의존하지 않는 가상 시스템용의 코드로 한 번 변환하는 것이다. 중간 코드 인터프리터는, 이 코드를 실제 시스템상에서 실행되는 프로그램으로 해석하면서 실행한다. 원시 코드를 직접 코드에 포함하지 않음으로써 메모리 효율을 극대화시키셔, 작은 메모리에서도 실행할 수 있게 된다. 게다가, 이 중간 코드를 번역해 실행하는 프로그램마저 만들어 버리면, 어느 시스템에서도 실행할 수 있게 되는 것이다. 이 중간 코드는 기계어에 가까운 형태로 되어 있어서, 번역해서 실행하는 프로그램도 간단하고, 성능도 괜찮게 나온다.


실제로, 자연 언어에 가까운 프로그램 기술인 Java 언어를 *.java 파일로 작성하고 javac 커맨드로 컴파일해서 아르바이트 코드인 *.class 파일을 만들어 낸다. java.exe는 이 아르바이트 코드를 번역하면서 실행한다. 이런 흐름은 UCSD Pascal과 완전히 똑같다. 게다가 Java의 경우, 아르바이트 코드의 효율화를 생각해서, ZIP으로 압축되어 있기도 한다. CPU와 메모리의 관계와 비교하면, 인터넷의 통신 속도나, 디스크의 I/O 속도가 더 늦다. 그러므로 CPU로 ZIP으로 압축해 전송 량을 줄이던지, 압축을 하지 않고 CPU의 부하를 저하시키던지 의 선택에서 전자를 선택하는 것은 당연하다고 할 수 있다.


아르바이트 코드를 번역해서 실행하는 형식(/opt/java/bin/java라든지 java.exe)을 Java Virtual Machine(Java 가상 시스템, 이하JVM)이라고 한다. 여러 가지 기종의 여러 가지 OS로 JVM을 만들어 버리면, 어느 시스템에서도, 완전히 똑같은 아르바이트 코드를 실행할 수 있게 된다. 아르바이트 코드가 취급하는 데이터는 어느 시스템에서도 동일한 것으로서 취급할 수 있다. 아르바이트 코드가 어느 정도 기계어에 가까우면 실행도 가벼운 법이다. 번역해서 실행하기 때문에, 메모리의 부정 사용이 있으면, 번역한 시점에서 에러 처리를 할 수 있다. 이로써, 프로세스가 갑자기 죽는 일은 없어진 것이다. 이렇게 해서, 인터넷에서 요구하는 모든 요건들이 JVM이 등장함으로써 단번에 해결된다.


High Performance


기종에 의존하지 않는 가상 시스템이 전부는 아니다. 역시, 타겟 시스템 CPU에 맞게 컴파일을 해서 직접 실행할 수 있는 기계어에 비하면 퍼포먼스가 떨어지게 된다. 왜냐하면, 파싱하는 시간만큼 오버헤드가 되어, 실행속도가 떨어지기 때문이다. 이외에도, Java의 경우, 실행 시에 정의된 변수의 메모리 영역도 정리해야 한다. 즉, 로컬변수에 값을 대입하려면, 그 변수를 위한 메모리를 확보한 후 거기에 대입하고 싶은 값을 저장하게 된다. 그리고, 변수는 언젠가는 사용하지 않게 되므로 뒤처리를 해야 하는데, 하나 하나 그 자리에서 처리하기 때문에 성능 향상을 바랄 수 없다. Java에서는 가비지 콜렉션을 도입하고 있다.


HotJava 브라우저에서는, 애플릿을 움직이고 있었고 이 정도이면 위의 방식으로도 충분했다. 실제로, HotJava 브라우저의 Duke 애니메이션은 잘 되어 있어서, 처음 보았을 때는 대단하다고 생각했다. 가비지 콜렉션이 동작하고 있을 때는 조금 멈추지만, 그런 것은 애교 정도로 봐줘도 무관하다. 그러나, 오늘날에는 이러한 오버헤드는 위험하다. 왜냐하면, 초당 몇 천 개의 request를 실수 없이 처리해야 하기 때문이다.


JIT의 등장


앞에서 소개한 퍼포먼스의 문제를 해결하기 위해서 등장했던 것이 JIT(Just In Time Compiler:실행 시 컴파일)다. 이것은, 클래스가 필요할 때마다, 중간코드로부터 기계어로 컴파일해서 실행한다는 발상이다. 당연한 말이겠지만, 실행 시에 컴파일러가 기계어로 번역하기 때문에 최초로 실행할 때는 시간이 약간 걸리게 된다. 객체 지향 언어에서는, 오브젝트를 초기화하는 메소드를 정의할 수 있다. 이 정의는 constructor라고 하며, 오브젝트를 준비하는 부분이다. 컴파일은 첫 회 즉 해당 오브젝트가 만들어질 때 동작하므로, constructor가 실행될 때 속도가 떨어진다. 여러 가지 오브젝트를 자주 바꿔가며 사용하는 경우에는 컴파일이 무거워져 버린다는 결점이 있지만, 보통 인터넷 어플리케이션은 반복이 많기 때문에, interpreter에 비하면 충분히 빠르게 된다. 아마도, 반복이 전혀 없는 직선 도로와 같은 프로그램은(만드는 것도 어렵겠지만) interpreter가 더 빠를 것이다.


기계어로 컴파일되면 이식성이 없어질 거라 생각할 수 있지만 그렇지 않다. 컴파일된 기계어는 메모리로 올라가고 사용될 때를 제외하고는 로드되지 않는다. 즉, java.exe의 실행이 끝나면 없어져 버린다. 다른 UNIX 시스템으로 기동할 때는, 그 시스템의 새로운 기계어가 사용되므로, 이식에 문제는 발생하지 않는다. 물론 취급하는 데이터의 형식도 유지되므로 걱정할 필요가 없다.


JIT는 컴파일된 기계어의 성능보다는 컴파일에 필요한 시간이 중요한 사항이다.


가비지 컬렉션의 개선


Java는 확보된 메모리의 릴리스를 JVM이 자동적으로 실시한다. 이 기능을 가비지 컬렉션이라고 부른다. 그리고, JVM이 사용하는 메모리는 실행할 때 지정한다(이 영역을 heap 이라고 한다). 초기의 Java에서는 heap을 대충대충 사용하였다. 빈 곳이 없으면 정리해서 릴리스하였다. 즉, 사용하고 있지 않는 곳을 사용할 수 있게 한다는 것이다. 작은 메모리에서 동작시키면, 정리해서 릴리스한다고 해도 메모리 영역이 금방 차 버린다. Heap 사이즈를 크게 잡아주면, heap이 없어질 때까지 순조롭게 움직이다가 조금 멈추고 그리고 다시 작동하게 된다. 즉, 이 가비지 컬렉션 때문에 병목현상이 발생하게 되는 것이다.


꼭 Web 어플리케이션 프로그래머가 아니라도, 로컬 변수를 임시로 사용하는 경우도 있고, 꽤 오랫동안 오브젝트로 유지해야 하는 경우도 있을 것이다. 이 두 가지를 메모리 매니지먼트 영역에서 함께 관리하는 것은 좋지 않다.


그래서, New와 Old로 heap의 내용을 2개로 나눈 구조가 등장하게 된 것이다. 단기적으로 참조되자 마자 필요 없게 되는 변수는, New의 heap에 놓고, 빈번하게 릴리스시킨다. 한편, 어느 정도 오랫동안 메모리에 있어야 하는 변수는, Old의 heap에 놓고, 가득 찼을 때에 릴리스시킨다. 이 Old가 가득 찼을 때에 일어나는 가비지 콜렉션을 Full GC라고 부른다.


HotSpot의 등장


heap의 개선은 Web 어플리케이션 서버 실행에 긍정적인 영향을 미쳤다. 이와 동시에, Web 어플리케이션 서버는 더욱 많은 클래스를 사용할 수 있게 되었고, 프로그램도 거대하게 되었다. 이와 같이 거대한 어플리케이션에서는, JIT와 같이 새로운 클래스를 실행할 때마다 컴파일하게 되면 시간이 걸리기 때문에, 이것도 좋지 않은 방식이다. 아무리 JIT가 고속으로 컴파일한다고 해도 오버헤드가 크게 발생하기 마련이다. 그런데, 해석하는 도중에 프로파일을 취해, 특히 많이 사용되는 부분만 컴파일하고 나머지는 가만히 두면, 좀 더 빨라지지 않을까? 맞는 말이다. 이런 방식이 바로 HotSpot이다. 이 기술은 휴렛팩커드(이하 HP)가 JavaSoft에 제안해, 현재 실행되고 있다. 아~ JavaSoft는 언제 오픈 되었단 말인가!


많은 어플리케이션에서 전체 처리의 20%가 실행 시간의 80%를 차지하는 경우가 많다. 옛날, CISC 시스템과 RISC 시스템 중 어느 쪽이 더 우수하냐는 논쟁이 있었는데, HP가 재빨리 RISC를 상용화했다. 이것은, CISC 시스템에서는, 실행 시간의 대부분을 일부의 명령 세트를 실행하는데 소비되고 있어, 다른 복잡한 명령 세트를 처리하는 데는 나머지의 몇 안 되는 시간만을 사용하게 되었다. 그렇다면, 실행 시간의 대부분을 소비하고 있는 명령세트를 고속화해서, 복잡한 명령 세트를 여유 있게 실행하면 어떨까? HotSpot이 이와 같은 형식이다.


예전에 C와 Java 중 어느 쪽이 더 빠른가? 라는 질문에, 최적화 파라미터를 지정해 잘 컴파일 된 C보다 Java가 더 빠르다는 대답을 한 적이 있다. 포인터를 잘 사용한 프로그램에는 이길 수 없겠지만, 아마추어가 단지 최적화 옵션을 지정해 컴파일 한 C의 계산 프로그램과 Java를 비교하였을 때, Java가 빠르다는 예가 실제로 있었다. 몇 번이나 interpreter로 실행한 후, 프로파일을 취득해서, 이것을 기본으로 컴파일 하므로, C로 말하자면, prof 데이터를 기본으로 컴파일하는 "최적화 컴파일"과 같은 효과를 가져온다.


최근 Intel의 Itanium에서 사용하고 있는 기술인 EPIC(Explicitly Parallel Instruction Computing:명시적 병렬 명령 컴퓨팅)에서는, 컴파일 시에 최적화해, 내부 레지스터를 낭비 없게 사용해, 고성능을 발휘한다. HotSpot 기술과 매우 비슷하다. HP는 Itanium을 탑재한 HP-UX시스템에 HotSpot을 구현한 JVM을 벌써 릴리스 했으며, 높은 성능을 실현하고 있다.


그럼, 최초의 컴파일 시에 프로파일 데이터를 뽑도록 지정해서, 테스트 Run을 한번 실행해서 이러한 정보를 얻을 수 있다. 그 결과를 최적화 컴파일에 반영한다. 이는 고도의 컴파일 테크닉이다. 그렇지만, Java에서는 적당하게 해도 자동적으로 해 준다. 계산에 사용한 시나리오가 바뀔 경우, C++에서는 prof를 고쳐 컴파일을 다시 하지만, Java에서는 단지 실행만하면, 그 때에 응한 코드를 다시 얻을 수 있다.


HotSpot과 heap의 개선은 JDK(Java Development Kit) 1.3.x부터 실현되었다. 그렇게 해서, EC 사이트에서 Java를 성능의 저해없이 이용되기에 이른 것이다. HP의 Itanium용 JDK의 최신 버전은 1.4.2이다.


Multithreaded (multi-thread)


HotJava와 같이 동영상을 취급하는 브라우저에서 multi-thread는 필수 요건이다. 1개의 동영상이 움직이고 있는 동안에 다른 버튼의 조작을 할 수 없다고 하면, 완전히 쓸모가 없기 때문이다. 물론, 요즘에는 Web 어플리케이션 서버에서 복수의 request를 multi-thread로 병행하기 때문에 필요 불가결한 요소이다. 당초의 Java에서 multi-thread는 모두 JVM으로 실현되고 있었다. interpreter가 타임 쉐어링을 통해서 가상적으로 복수의 thread를 동작시키게 되어 있었다. 이러한 interpreter로 실행되는 thread를 green thread라고 부른다. 아마 Green Project의 흔적인 것 같다. JIT, HotSpot의 등장으로 실행 시에 기계어로 컴파일되므로, 가상 시스템상의 thread도, OS가 직접 관리하는 thread가 되었다. 이것을 native thread라고 부른다. Green thread에 비하면 오버헤드가 현격히 적다.


Dynamic


C++에는 네임 스페이스라고 하는 개념이 있지만, Java에는 없다. 비슷한 것을 찾자면 Class Path가 있다. Class Path는 환경 변수 CLASS_PATH에 설정해 Java를 호출할 때 파라미터로 지정한다. OS의 파일 구조를 지정하는 이유는 실행 시에 클래스를 불러 들이는 방식이기 때문이다. Java는 프로그램을 *.java로 기술해, javac 커맨드로 컴파일 해서, *.class라는 이름의 클래스 파일을 작성해서 실행한다고 설명했지만, 이 클래스 파일이 실행 시에 읽혀지므로, 읽는 디렉토리를 지정해두지 않으면 찾지를 못하게 된다.


UNIX에는, gnuzip이라는 표준 도구가 있다. 대체로 모든 UNIX OS에서 사용 가능하다. 그러나, ZIP에는 없는 것이 있다. Windows의 WinZip로 작성한 ZIP 파일은 UNIX에서 압축을 풀고 싶어도 풀 수 없다! 이럴 경우에는, JDK에 포함되어 있는 jar 커맨드라고 하는 Java 프로그램을 사용할 수 있다. Windows의 엔지니어라면 익숙할 것이다. 그러나 반대로, UNIX의 tar 커맨드로 작성한 파일을 Windows에서 풀고 싶을 때는, jar 커맨드를 사용할 수 있다. 무엇보다, Windows에는 tar 커맨드의 무료 버전은 많기 때문이다.


jar커맨드를 갑자기 등장시켜 미안하지만, Class Path에 다수의 class 파일이 있는 것도 관리 측면에서는 귀찮은 일이다. 그런 측면에서 Java에서는 WinZip과 같은 기능을 가진 jar 커맨드로 클래스 파일들을 압축할 수가 있게 되어 있다. 이 파일은 *.jar 라고 하는 확장자를 붙이기 때문에 jar파일이라고 부른다. Java판의 tar니까 jar라고 하는 것일까?


클래스를 동적으로 로드하기 때문에 메모리를 크게 절약할 수 있다. 만들어 놓은 프로그램이 전부 실행되는 것이 아니기 때문이다. Jar로 다양한 클래스를 정리해서 거대한 라이브러리를 만든다고 해도, 사용하지 않는 것은 메모리에 로드되지 않기 때문에 실행 시에 메모리를 신경 쓰지 않고 클래스 라이브러리를 작성할 수 있다. Swing도 그렇다. 전부 읽어 들인다고 생각하면 큰일일 것이다.


그런데, 실행 시에 클래스를 읽어 들이는 방식은 오늘날 Web 어플리케이션 서버 테크놀로지 발생을 재촉하는 큰 역할을 하였다. 실행 시에 동적으로 클래스를 불러 들이는 기능이 없었다면, JSP(Java Server Pages)와 같은 스팩도 생각하지 못했을 것이다. 그렇게 되면, *.jsp 파일 안에 Java언어를 묻어 둘 수 있다. 통상적으로 JSP를 참조할 때는 *.java가 신규로 생성되어 javac로 컴파일 되고 그 결과 *.class가 작성된다. 이 *.class는 Servlet으로 동작한다.


그러나, JSP도 고도로 비대화되고 있는 오늘날, 사전에 컴파일 해 두는 기능을 어플리케이션 서버에서는 표준으로 하고 있다. JSP를 *.java로 변환한 시점에서 2Gbytes를 넘어 버려 에러가 발생해 버리는 경우도 있다. 그리고, 다른 경우에는, 사전에 컴파일해 두는 것을 전제로 하고 설계를 하기도 하는데 웃기지도 않다.


한 번 운영을 시작하면 멈출 수 없는 Web 어플리케이션 서버의 경우, 버그에 의한 원시코드의 수정을 반영시키는 디플로이먼트(deployment)가 순조롭게 행해져야 한다. 클래스를 동적으로 로드하는 Java의 생각이 Web 어플리케이션 서버에도 계속되고 있는 것이다.


◆ 컴퓨터 그 자체의 역사가, 이제 겨우 반세기를 지나고 있는 가운데, 벌써 8년의 역사를 가지고 있는 Java는 어느덧 결코 새로운 언어가 아니다. 마이크로 컴퓨터 소년의 시대부터, Jobs씨나, Gosling씨와 같은 세대를 보낸 엔지니어로서 Java의 스팩은 대단하다고 느낀다. 이런 나의 생각이 여러분에게 전해지면 다행일 것이다. Java를 넓은 독자층에게 아주 잘 설명할 수는 없었지만, 전술한 Original Java Whitepaper나, Java 탄생의 페이지를 아직 보지 않은 않았다면, 꼭 보기를 바란다.

반응형

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받을 수 있습니다.

반응형

학교 과제로 제출했던 스캔라인 폴리곤 필 및 바운더리 4-웨이 폴리곤 필 ^.^

다각형채우기!!


주요 소스 코드


//━━━━━━━━━━━━━━━━━━━━━━━━┓
                         
//  ─━ 제작자 : 혼돈속의 지남차
//  ─━ 목  적 : 학교 과제
//  ─━ 과  목 : 컴퓨터 그래픽스
//  ─━ 주  제 : 다각형 색 채우기.
//  ─━ 방  법 : Scanline fill & Flood Fill
//  ─━ 날  짜 : 2008. 10. 3 03:46 AM

//━━━━━━━━━━━━━━━━━━━━━━━━┛
#include <windows.h>
#include "defines.h"  //안에 각종 헤더파일 및 define들이 쓰여져있음.
#include "GLPoints.h" //(x,y)튜플

void scanLine(vector<CGLPoints>& vecGLPoints); //vertex뭉치를 인자로 받아 ScanlineFill

// 재귀함수 이용한 version 1 (도형크기가 커지면 오버플로우 발생)

/*

void boundary_fill(int x, int y, BYTE* byBoundaryColor)
{

    cout << x << " " << y << endl;
    //현재픽셀이 테두리색이랑 같으면 중단.
    BYTE byCurrerntColor[3];
    glReadPixels(x,WINSIZEY-y,1,1,GL_RGB,GL_UNSIGNED_BYTE,byCurrerntColor);
    if((byBoundaryColor[0] == byCurrerntColor[0]
        && byBoundaryColor[1] == byCurrerntColor[1]
        && byBoundaryColor[2] == byCurrerntColor[2] )|| x<=0||WINSIZEY<=y||WINSIZEX<=x||WINSIZEY<=WINSIZEY-y || matrix[x][WINSIZEY-y] == true )
    {
        return ;
    }
    else
    {
        matrix[x][WINSIZEY-y]=true;
        glColor3f(1.0, 1.0, 0.0);
        setPixel(x,WINSIZEY-y);

        boundary_fill(x+1, y, byBoundaryColor);
        boundary_fill(x, y-1, byBoundaryColor);

        boundary_fill(x-1, y, byBoundaryColor);
        boundary_fill(x, y+1, byBoundaryColor);

    }

}

*/



// 자체 스택을 이용한 version2 - 도형의 크기가 커도 잘 동작 !! :)
void
boundary_fill(int x, int y, BYTE* byBoundaryColor)
{

  stack< CGLPoints > stackStoreCoords;

  stackStoreCoords.push(*(new CGLPoints(x,y)));
  cout << x << " " << y << endl;
  //현재픽셀이 테두리색이랑 같으면 중단.
  BYTE byCurrerntColor[3];
  do
  {
    int nX = stackStoreCoords.top().GetX();
    int nY = stackStoreCoords.top().GetY();
    glReadPixels(nX
      ,WINSIZEY-(nY),1,1,GL_RGB,GL_UNSIGNED_BYTE,byCurrerntColor);
    if((byBoundaryColor[0] == byCurrerntColor[0]
      && byBoundaryColor[1] == byCurrerntColor[1]
      && byBoundaryColor[2] == byCurrerntColor[2] )
    )//  || nX<=0  \
      ||WINSIZEY<=nY  \
      ||WINSIZEX<=nX  \
      ||WINSIZEY<=WINSIZEY-nY  \
      || matrix[nX][WINSIZEY-nY] == true )
    {
      stackStoreCoords.pop();

    }
    else
    {
    //  matrix[nX][WINSIZEY-nY]=true;
      glColor3f(1.0, 1.0, 0.0);
      setPixel(nX,WINSIZEY-nY); //점을 찍고 스택에다 4방향을 쌓는다.
      stackStoreCoords.push(*(new CGLPoints(nX+1,nY)));
      stackStoreCoords.push(*(new CGLPoints(nX,nY-1)));
      stackStoreCoords.push(*(new CGLPoints(nX-1,nY)));
      stackStoreCoords.push(*(new CGLPoints(nX,nY+1)));
     
    }

  } while(!stackStoreCoords.empty()); //스택이 비면 루프를 빠져나온다.


}
//CGLPoints클래스를 y좌표 기준으로 정렬할때 비교함수.
bool LessByY(CGLPoints pt1, CGLPoints pt2)
{
  return (pt1.GetY()<pt2.GetY());
}
//━━━━━━━━━━━━━━━━━━━━━━━━━
//      ─━   메인 시작 ━─
//━━━━━━━━━━━━━━━━━━━━━━━━━
void main(int argc, char** argv)
{
 //생략
}
//━━━━━━━━━━━━━━━━━━━━━━━━━
//      ─━   함수들 정의 ━─
//━━━━━━━━━━━━━━━━━━━━━━━━━

//초기화
void init(void)
{
 //생략
 
}



//점 찍기
void setPixel(GLint x, GLint y)
{
   //생략
}

//선 긋기
void setLine(GLint x, GLint y, GLint x2, GLint y2)
{
  //생략
}


//다각형 선긋기 (점들을 인자로 받는다)
void setPolyLine(vector<CGLPoints>& vecGLPoints)
{
  int nNumOfvertice = vecGLPoints.size();
  int i;
  for(i = 1 ; i < nNumOfvertice ; i++)
  {
    setLine(vecGLPoints[i-1].GetX(), vecGLPoints[i-1].GetY(), vecGLPoints[i].GetX(), vecGLPoints[i].GetY());
  }
  setLine(vecGLPoints[nNumOfvertice-1].GetX(), vecGLPoints[nNumOfvertice-1].GetY(), vecGLPoints[0].GetX(), vecGLPoints[0].GetY());
}

//스캔라인 방식으로 다각형 내부 칠하기(점들을 인자로 받는다)
void scanLine(vector<CGLPoints>& vecGLPoints)
{
  int i,j;
  int color;
  int minY, maxY;
 
  //X좌표용
  //y좌표를 키이며 x좌표를 value로 하는 맵
  vector<int>*  vecScanLineYCoord = new vector<int>[WINSIZEY];

  CGLPoints* temp = new CGLPoints(0,0);
  //다각형의 y의 최대값과 최소값을 찾는다.
  temp = min_element(vecGLPoints.begin(),vecGLPoints.end(),LessByY); //최소값
  minY = temp->GetY();
  temp = max_element(vecGLPoints.begin(),vecGLPoints.end(),LessByY); //최대값
  maxY = temp->GetY();

  //최소최대값 콘솔출력(테스트용)
  cout << minY << " "<< maxY << endl;

  int nLenth = vecGLPoints.size(); //점들의 갯수
  for(i = 1 ; i <= nLenth ; i++)
  {
    //두점의 기울기를 구함.
    float fSlope =  float(vecGLPoints[i-1].GetY() - vecGLPoints[i%nLenth].GetY()) 
            / float(vecGLPoints[i-1].GetX() - vecGLPoints[i%nLenth].GetX());
    //두점의 직선의 방정식의 y절편
    // y-y2 == (y2-y1)/(x2-x1) * (x-x2)  where x == 0
    float yInterCept = fSlope*(-vecGLPoints[i-1].GetX())+vecGLPoints[i-1].GetY();
    //for문을 올바로 돌리기 위해 두 vertex의 y좌표 대소비교한다.
    int minimum, maximum;
    if(vecGLPoints[i-1].GetY()<vecGLPoints[i%nLenth].GetY())
    {
      minimum = vecGLPoints[i-1].GetY();
      maximum = vecGLPoints[i%nLenth].GetY();
    }
    else
    {
      minimum = vecGLPoints[i%nLenth].GetY();
      maximum = vecGLPoints[i-1].GetY();
    }
    for(j = minimum ; j < maximum ; j++ )
    {
      // j는 y좌표이며 x좌표는 y좌표에 절편을 뺀값을 (1/기울기)를 곱해주면 나온다. (y = mx + b 에서도출)
      vecScanLineYCoord[j].push_back((j-yInterCept)*(1.0f/fSlope));
    }

  }



  //각 scanLine y좌표에에 저장된 x좌표들을 오름차순으로 정렬한다.
  for(i = minY ; i <= maxY ; i++)
    sort(vecScanLineYCoord[i].begin(),vecScanLineYCoord[i].end());
 

  //스캔라인의 최소값에서 최대값까지
  for(i = minY ; i < maxY ; i++)
  {

    //정렬된 X좌표들의 PAIR들을 선을 그어 폴리곤에 색을 채운다.
    int nSize = vecScanLineYCoord[i].size();
    for(j = 0 ; j < nSize ; j+=2)
    {

      //콘솔 테스트 출력
//      cout << "scanLine Y :  " << i << "X좌표 Pair : " \
        <<vecScanLineYCoord[i][j%nSize]  << " "<< vecScanLineYCoord[i][(j+1)%nSize] << endl;
      setLine(vecScanLineYCoord[i][j%nSize],i,vecScanLineYCoord[i][(j+1)%nSize],i);//선긋기
    }
  }
}
void displayFunc(void)
{
  int i;
  glClear(GL_COLOR_BUFFER_BIT);
  glColor3f(1.0, 1.0, 0.0);
  //  (x,y)튜플들을 가질수 있는 벡터선언
  vector<CGLPoints> vecPtVertice;

  //랜덤도형
  srand(GetTickCount());
//  for(i = 0 ; i < 100 ; i++)
//    vecPtVertice.push_back(*(new CGLPoints(rand()%WINSIZEX, rand()%WINSIZEY)     ));


  //큰도형
  vecPtVertice.push_back(*(new CGLPoints(100,400)));
  vecPtVertice.push_back(*(new CGLPoints(300,200)));
  vecPtVertice.push_back(*(new CGLPoints(500,100)));
  vecPtVertice.push_back(*(new CGLPoints(700,300)));
  vecPtVertice.push_back(*(new CGLPoints(500,500)));
  vecPtVertice.push_back(*(new CGLPoints(400,350)));
  vecPtVertice.push_back(*(new CGLPoints(300,600)));

  //작은도형
/* 
  vecPtVertice.push_back(*(new CGLPoints(226, 143)));
  vecPtVertice.push_back(*(new CGLPoints(216, 174)));
  vecPtVertice.push_back(*(new CGLPoints(270, 184)));
  vecPtVertice.push_back(*(new CGLPoints(287, 139)));
  vecPtVertice.push_back(*(new CGLPoints(247, 132)));
*/

  //테두리
  setPolyLine(vecPtVertice);

  if(g_MouseState.rightButton == true)
    scanLine(vecPtVertice); //스캔라인으로 다각형을 채운다. (인자로 vertice넘김)

  //마우스를 클릭하면 해당영역부터 Boundary_fill 시작.
  if(g_MouseState.leftButton==true)
  {
    glColor3f(1.0, 1.0, 0.0);
    BYTE byBoundaryColor[3]={255,255,0};
    DWORD dwBegin  = GetTickCount();
    boundary_fill(g_MouseState.x,g_MouseState.y,byBoundaryColor);
    DWORD dwEnd    = GetTickCount();
    cout << "Boundary_FILL 소요시간 : " << float((dwEnd - dwBegin)/1000.0f) << endl;
  }
  glFlush();
 
}

void HandleMouseState(int button, int state, int x, int y)
{
//생략
}

 

실 행 화 면

 

Boundary Fill을 이용

재귀함수를 이용한 Boundary_fill

image
그림1) 이 정도 크기는 문제없다

 
그림2) 프로그램이 멎어버린다.

 

그러나 이 재귀함수의 문제점은 폴리곤의 크기가 시스템 스택이 저장할 수 있는 임계값을 넘으면 Stack Overflow가 나서
더 이상 진행을 못하고 프로그램 자체가 죽어버린다.

 

그래서 자체적인 스택을 구현해야하는데 (힙 메모리를 써서 한계값이 훨씬 큼) STL의 stack을 썼다.

 


그림3> 자체적인 스택을 이용한 비교적 큰 사이즈의 도형 색채우기.

Scanline 이용하여 채우기.

같은 도형으로 실험했을때 똑같이 채워졌다.
차이점이라면 수행속도는 0초에 가깝게 훨씬 빠르게 채워졌다.

점 백개를 랜덤으로 찍어 올바르게 수행하는지 테스트 했다.

도형의 내부 외부를 잘 구분하여 올바르게 색칠이 되었다.

 

 

 

Discussion


똑같은 도형을 다른 두 방식으로 채웠을때 수행시간

 
두 방식은 소요시간에서 엄청난 차이가 난다.

 

Scanline_fill 같은 경우는 직선의 방정식을 이용한 coherence 특징을 이용해서 거의 0초에 가깝게 나왔지만
Boundary_fill 같은 경우는 픽셀하나씩 찍을때 마다 glReadPixels를 호출하기 때문에 그에 따른 오버헤드 및 한점 한점 처리를 하기 때문에 21~24초에 가까운 시간이 걸렸다.(실행화면에 있는 큰 도형의 경우)

구현시 어려웠던 점은 glReadPixels의 y인자를 창의 Y크기에서 뺀 값을 넣어줘야 올바르게 작동하는 것이었다.
이 것 때문에 수 구현상 수 많은 시행착오를 겪었다.
OPENGL에서는 맨 아래가 y좌표 0인 반면 윈도우상에서 실제로 디스플레이 될 때는 맨 윗쪽이 0으로 찍혔다.

Scanline_fill 구현 시 ,두 점의 직선의 방정식을 이용 y-y1 = (y2-y1)/(x2-x1)(x-x1)
두 점 사이에서 스캔라인값을 1증가 혹은 감소 시킬때 x값을 추정후 그 x값의 y인덱스에 x의 밸류를 넣어주고
다른점들에 대해서도 이와 같이 반복후 스캔라인의 특정 vector를 sort한후 pair를 이루게 하여 선을 그어주었다.

원래 좀 더 직관적인 방법인 아래에서 부터 픽셀하나하나 검사하며 점을 만나면 그리기 시작하고 다시 만나면 그리기를 중단하는 식으로 할려고 했으나 시간도 많이 걸리고 예외사항이 너무 많이 나와서 "직관적"이긴 하지만 구현시에는
coherence 를 이용한 방법보다 더 많은 수고로움이 필요했다. (실제로 OPENGL을 이용하여 선을 그으면 x좌표에 각도에 따라 2개이상의 점이 찍히는 경우가 있기때문에 책에서 나온 예외사항 외에도 생각할 거리가 많다.)

Boundary fill 이 도형크기에 따라 시간이 달리 소요되긴 하지만 위의 실험에서 23초쯔음 걸리는것을 보고 약간 의문점이 드는것이 , 포통 Graphic 툴에서도 Boundary_fill 이나 숙제엔 나오지 않은 Flood_fill 같은 방법 또한 사용할텐데
어떻게 그렇게 짧은 순간에 다각형을 칠할 수 있는지 궁금해졌다.

이번 과제를 통해 보너스로 STL에서 제공하는 vector 및 stack에 대해서  좀 더 친숙해질 수 있었고 새삼 상용그래픽툴의 위대함을 느끼게 되었다.

반응형

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받을 수 있습니다.

반응형


원문 :

개발자의 10년일기

http://devdev.tistory.com/74

문자세트 및 유니코드의 기본 그리고 윈도우32에서는 어떤 식으로 유니코드를 처리하고 있는지 살펴보도록 하자. 이 글을 통해 프로그램의 국제화에 대한 관심도를 높이고 국제화의 실현에 대한 전체적인 안목을 가질 수 있기를 바란다.

문자 세트

문자세트란 하나의 문자와 그 문자에 부여된 값의 관계를 나타내는 일종의 테이블과도 같은 것이다. 컴퓨터 분야에서사용되고있는 문자세트를 크게 본다면 SBCS(Single Byte Character Set), MBCS(Multi ByteCharacter Set), DBCS(double Byte Character Set), WBCS(Wide ByteCharacter Set 또는 Unicode) 등 네 가지로 분류할 수 있다.

SBCS는 하나의 문자세트에 부여된 글자 값이 Single Byte, 즉 0-255의 범위 내에 있음을 말한다. 우리가 잘알고있는 ASCII 또는 ISO-8859-1 문자 세트가 여기에 속한다. 주로 0x00부터 0x7F까지는 우리가 잘 알고있는ASCII를 나타내며 이 중에서도 0x20부터 0x7F까지는 화면 출력이 가능한 문자세트를 정의하고 있다. SBCS를 사용하는각 언어들은 0x80부터 0xFE까지의 문자세트를 제각기 따로 정의하고 있으며 우리는 이들 각 문자세트들을 각각 다른코드페이지(Codepage)로 정의하고 있다.

DBCS는 하나의 문자세트에 부여된 글자값이 두 바이트, 즉 0-65535의 범위 내에 있음을 말한다. 이는 원래의8비트에서 또 다른 8비트를 단순히 확장한 것이다. DBCS는 8비트로 처리할 수 없을 정도로 문자의 종류가 많은 중국, 한국,일본 그리고 대만 등에서 주로 사용하고 있다. DBCS는 SBCS의 한계를 넓히는 목적은 이루었다고 볼 수 있으나 프로그램상의많은 불편한 점을 함께 가져오므로 프로그래머에게는 일종의 골칫거리를 안고 온 셈이기도 하다(여기서 필자가 말하는 골칫거리란DBCS 자체를 가리키는것은 결코 아니며, DBCS를 적용하면서도 원래의 SBCS를 계속 지원해야만 하는 환경에서 오는프로그램상의 복잡성에서 오는 불편함을 의미한다).

MBCS란 SBCS와 DBCS를 한데 묶어놓은 문자세트를 의미하며, DBCS의 문자세트에는 거의 SBCS의 문자가포함되므로, 우리가 말하는 DBCS는 사실은 주로 MBCS를 의미한다. 한글의 KSC5601을 예를 들어보면, 문자세트의 처음부분인 0x00부터 0x7E 까지는 ASCII와 동일하게 되어있다. 이러한 배열은 중국, 일본, 대만에도 마찬가지며 나중에설명할 Unicode에서도 마찬가지다. DBCS에서 첫 번째 바이트를 리딩 바이트(Leading Byte)라 하고 두 번째바이트를 트래일링 바이트(Trailing Byte)라 하며, 첫 번째 128 문자는 ASCII로 되어있으며 나중 128 문자는각 언어마다 다르게 지정되어있다. 일본 코드페이지 932, 중국 936, 한국 949 그리고 대만 쪽에서 사용하는 코드페이지950 등이 DBCS의 대표적인 예라 할 수 있다. ASCII를 제외하고는 각 DBCS들이 모두 다르기 때문에 우리가 흔히프로그램에서 strlen()는 MBCS에서는 더 이상 직접 사용할 수가 없게된다. 즉, strlen()은 문자의 수를 반환하게되는데, SBCS에서는 문자의 수가 바이트 수와 일치하므로 strlen()의 반환값은 사실 해당 문자가 차지하는 바이트의 숫자일수도 있다. 하지만 MBCS에서는 문자의 수와 그 문자가 차지하는 바이트의 수는 그 값이 다르므로 strlen()으로 문자의수를 계산할 수 없게된다.

이와 같이 MBCS 사용에 있어서 가장 큰 문제점이라면 I/O에 연관된 문자의 처리에 해당되는 프로그램의 모든 로직을일일이 DBCS와 SBCS을 염두에 두고 개발해야 한다는 점이다. 하나의 프로그램에서 SBCS와 DBCS를 동시에 염두에 두고입출력을 개발해야 하는 것이 프로그래머에게 있어서는 귀찮은 일이며 자칫 실수하면 프로그램상의 BUG(버그 또는 오류)를 가져오는요인이 되기도 한다. SBCS로는 프로그램을 국제화(Internationalization 혹은 간단하게 i18n)할 수 없고MBCS를 사용하자니 프로그램상의 난이도가 늘어나게 된다. 또한 각 언어마다 제각기 다른 문자세트를 사용하게되니 프로그램국제화의 길은 점점 멀어지게되는 느낌이다. 여기서 개발된 것이 바로 유니코드이다.


유니코드

유니코드는 흔히 WBCS라 말하기도 한다. 왜냐하면 SBCS와 DBCS가 공존하는 MBCS와는 달리 모든 코드값이일괄적으로 16비트로 할당되기 때문이다. 하지만 유니코드의 구조를 잘 살펴보면 기존의 국제 표준화와의 호환성을 최대한 지키려애썼음을 한 눈에 알 수 있다. 유니코드도 DBCS에서처럼 문자값의 표현에 있어서 16비트를 사용하므로 0-65535 범위의값을 표현할 수 있다. 현재로서는 이 값으로 지구상의 모든 언어를 표현하기엔 충분한 셈이다.

그림 1에서 보는 바와 같이, 처음 127 문자인 0x0000부터 0x00 7F는 ASCII의 0x00부터 0x7F의문자와 동일하며, 0x0080부터 0x00FE는 국제표준화 ISO8859-1의 순서와 동일하게 배열하였다. 이로써 유니코드와SBCS의 국제 표준화 문자세트와의 관계를 쉽게 하였다. 유니코드는 또한 한글의 조합형과 코드 값만 다를 뿐 그 배열은동일하며, 따라서 Unicode와 조합형 코드와의 변환이 매우 용이하다. 유니코드에서 정의한 한글 조합형은 그림 1에서 보듯이AC00부터 D7A3, 즉 11172개이다. 그림 1에서 처음 256개의 유니코드 값은 ISO/ANSI 8859-1와 그 문자배열이 동일하며 (ISO/ANSI 8859-1의 처음 127개의 글자 값은 ASCII 7비트와 동일하다.), 중복되는 여러기호들 그리고 한국, 일본 및 중국, 대만에서 사용하는 한자 표기는 그 모양이 동일하면 모두 하나의 코드값으로 묶어놓았다.


·General Scripts : 주로 SBCS인 Latin, Cyrillic, Greek, Hebrew, Arabic, Devanagari, Thai 등의 문자세트들을 표현하고 있다.

·Symbols : 구두점, 학술기호, 그리고 각종 심벌들을 정의한다.

·CJK Misc.: CJK(Chinese, Japanese, Korean)에 사용되는 각종   음성기호

·CJK Ideographs : 한자 표기(20902)

·Hangul : 한글 표기(11172)

·Surrogate: Future Expansion(2048)

·Private Usage : 특정 벤더 또는 사용자 사용(6400)

·Compatibility : 유니코드와 다른 인코딩 시스템과의 있을 수 있는 호환성 문제에 대비하여 준비한 범위


한글 초성이 19개, 중성이 21개, 그리고 종성이 27개이다. 하지만 종성은 사용하지 않을 경우도 있으므로 사실상 종성은28개가 되는 셈이다. 따라서 조합형 한글이 지원하는 총 글자 수는 19×21×28 = 11,172로서 유니코드에서 지원하는글자 수와 일치한다. 유니코드에서는 완성된 한글 글자 외에도 한글 초성, 중성, 종성을 따로 정의하고 있으며 이들 자모값의체계적인 배치는 유니코드 내의 완성된 한글로의 변환을 용이하게 하고있다. 표 1의 자모판을 보면서 유니코드의 글자값을 함께계산해 보도록 하자.

유니코드에 정의된 자모값을 사용하여 완성된 유니코드의 글자값과 조합형 글자값을 계산할 수 있다. 첫 번째 번호는 각 자모에할당된 인덱스값이며 두 번째 번호는 해당 자모의 유니코드값이다. 조합형 문자세트로의 코드값 변환은 초성, 중성, 종성에 각각채움이 있으므로 위의 인덱스를 조금씩 변경하여야 한다.

자모값을 포함한 하나의 스트링을 받아 한글로 변환하는 방법을 유니코드 버전 2에서 제시하고있다. 초성, 중성, 종성의 각인덱스값을 각각 Cho_i, Jung_i, Jong_i로 정의한다면 유니코드 글자값(V)은 다음과 같이 구할 수 있다.


V = { [(Cho_i × 21) + Jung_i] × 28 } + Jong_i + 0xAC00


예를 들어, 유니코드 입력 스트링의 값이 0x1111(초성), 0x1171(중성), 0x11B6(종성)를 포함하고 있다면위의 자모값에 의하면 0x1111=ㅍ, 0x1171=ㅟ, 0x11B6=ㅀ이 되므로 완성된 글자는 ‘’ 이 되어야 할 것이다.그렇다면 위에 주어진 공식을 사용하여 확인해 보도록 하자.

0x1111은 초성으로 위 테이블 1에 의한 인덱스 값은 17 (0부터 계산함), 0x1171은 16, 그리고0x11B6은 14가 되므로 Cho_i=17, Jung_i=16, Jong_i=14가 된다. 따라서 이 값에 해당되는 유니코드의한글값 V = ( [ (17 × 21)+16 ] × 28 ) + (14 + 1) + 0xAC00 = D4DB ()이 된다.한글의 유니코드 배열이 0xAC00에서 시작되므로 한글의 첫 글자 ‘가’의 유니코드 값이 바로 0xAC 00이 된다. 역시 위의공식에 의하면 {((0 × 21)+0) × 28 + 0 + 0xAC00} = 0xAC00이 된다.

이 공식을 프로그램에 적용한다면 결국 다음과 비슷한 로직이 될 것이다.


void IndexToUnicode (unsigned short Cho_i, Jung_i, Jong_i)

{

   unicodeValue = ((Cho_i * 21 + Jung_i)) * 28;

   if(Jong_i)

   {

    unicodeValue += Jong_i + 1;

   }

   unicodeValue += 0xAC00;

}


한가지 유의할 점이 있다면 역시 종성의 인덱스 값(Jong_i) 계산인데, 종성이 사용되지 않으면 테이블 1에 표기된인덱스를 그대로 사용하며, 종성이 사용된 경우는 Jong_i + 1을 종성의 인덱스 값으로 계산해 주어야 한다는것이다(유니코드에 사용된 자모 인덱스를 사용하면 조립된 한글의 유니코드 값을 알 수 있다). 테이블 1의 인덱스를 사용하여다음과 같은 방법으로 조합형 문자 세트에서의 값을 찾아낼 수 있게 된다.


void toJohab(unsigned short Cho_i, Jung_i, Jong_i)

{

   Cho_i += 2;

   Jung_i += 3;

   if(Jung_i >= 25) Jung+=2;

     /* 중성 인덱스 24, 25는 유니코드에 정의되지 않았음 */

   if(Jung_i >= 18) Jung+=2;

     /* 중성 인덱스 16, 17은 유니코드에 정의되지 않았음 */

   if(Jung_i >= 9)  Jung+=2;

     /* 중성 인덱스 8, 9는 유니코드에 정의되지 않았음 */

   Jong_i += 2;

       

   johabValue = (1 << 15)|(Cho_i << 10)|(Jung_i << 5)|Jong_i;

}


앞서 유니코드의 예를 들면서 사용한 ‘’ 의 조합형 문자값은 다음과 같이 계산할 수 있다.


V = (1 << 15) | (19 << 10) | (23 << 5) | 16


따라서 ‘’ 에 할당된 조합형 문자값을 비트 형태로 표현하면 ‘1100 1110 1111 0000’이 되므로 0xCDF0이 된다.

지금까지 우리는 여러 나라의 문자세트들, 유니코드, 유니코드와 ASCII의 관계 그리고 유니코드와 한글 조합형 문자세트와의관계들을 알아보았다. 혹 어떤 독자들은 지금까지의 설명들이 아직 잘 이해가 가지 않을 수도 있으리라 생각한다. 하지만 그렇다고너무 걱정할 필요는 없을 것 같다. 유니코드의 이해가 아직은 윈도우 I/O 아키텍처에의 이해에 있어서 필수라고는 말할 수는 없기때문이다. 하지만 이미 많은 프로그래머가 유니코드의 필요를 실감하고 있으며 프로그램의 국제화의 측면에 있어서는 유니코드가 가까운미래에 반드시 필요하게 될 것이다. 특히 윈도우 NT를 사용하는 독자라면 유니코드의 이해는 거의 필수라 말하고 싶다.


Unicode와 윈도우

위에서 우리는 소프트웨어 국제화를 위해서는 유니코드가 왜 바람직한 해결책인가를 알아보았다. 유니코드 중심의 소프트웨어국제화를 염두에 두고 개발된 OS가 바로 윈도우 NT라 할 수 있다. 이는 윈도우 95 또는 윈도우 98과는 달리 원래부터유니코드 처리를 위해 최적화되어 개발되었으며 따라서 지역화를 더욱 단순화하고 다양한 문자들을 보다 손쉽게 처리할 수 있다. 이미많은 업체에서 소프트웨어의 국제화를 이루기 위한 수단으로 전통적인 문자세트와 유니코드를 함께 지원하고 있으며, 아직은 유니코드가완전한 뿌리를 내리지 못한 상황이므로 개발환경에서는 전통적인 문자세트와 유니코드를 동시에 지원하도록 하는 것이 현재로서는소프트웨어 국제화의 바람직한 접근방법이라 생각한다. 이렇게 두 문자체계를 동시에 지원하므로 프로그래머가 자유롭게 길을 선택할 수있다는 장점이 있으나, 내부적으로는 입출력되는 문자들을 변환하는 장치를 거쳐야 하므로 성능 면에서 다소 그 기능이 떨어지는단점이 있을 수도 있다. 앞으로 우리는 남은 지면을 통해 유니코드와 전통적인 문자세트를 소프트웨어의 국제화 측면에서 어떻게WIN32 프로그램에 적용해야 할 것인지를 알아보도록 하겠다.

많은 윈도우32 API가 이미 유니코드와 SBCS을 지원하도록 따로 개발되어있다. 물론 개발자는 같은 일을 하는 두 개의다른 코드를 만들어 관리하고 싶지는 않을 것이다. 따라서 기존 ANSI용 API를 사용하는 프로그램을 유니코드 API와 호환되게하는 일 또한 중요한 과제중의 하나가 될 것이다. 참고로 여기서 말하는 ANSI용 API란 우리가 흔히 사용하는 일반 API를의미하며, 이는 API 차원에서의 데이터 처리능력으로 보통 SBCS 또는 DBCS에서 주로 사용되어 왔다. 반면에 유니코드용API라는 개념은 API 차원에서 16비트를 입력받아 일을 처리하도록 개발된 API를 의미한다. 윈도우에서 쉽게 사용할 수 있는다이얼로그박스 MessageBox()를 예를 들어보자. 프로그램 상에서는 ‘MessageBox()’를 사용하지만 실제 컴파일러가사용하는 MessageBox() 버전은 MessageBoxA() 또는 MessageBoxW() 중의 하나이다. 어느 버전을사용하느냐는 프로그래머가 16비트 코드인 유니코드 실행파일을 만들 것인지 아니면 8비트 코드 ANSI 실행파일을 만들 것인지에따라 다르다.

프로그램 실행파일의 관점에서 볼때, 유니코드를 사용할 때와 사용하지 않을 때는 어떻게 다른가? 물론 지금까지의 설명을 종합해 볼 때 유니코드는 16비트를사용하므로 8비트 버전에 비해 두 배의 자원을 사용하게됨을 알고있다. 또한 OS의 문자처리 아키텍처가 실행되는 프로그램과 다를때는 문자 변환기를 사용하므로 성능 면에서 이론적으로 약간의 차이가 있음을 설명한 바 있다. 예를 들어 어떤 프로그램에서 다음과같은 두 개의 변수를 정의한다고 가정해 보자.


char charV;

char * pch;

윈도우32 아키텍처에서 유니코드를 사용하지 않을 때의 charV는 1 바이트 변수로서 그 값은 ‘\0’로 초기화된다.여기서 charV에 ‘A’를 할당하면 (charV=‘A’), charV는 ‘A’의 코드값인 0x41를 가지게 된다. 두 번째줄에서는 문자열을 가리키는 포인트를 pch로 정의하고 있으며, pch가 차지하는 메모리의 크기는 윈도우32 아키텍처 상4바이트(32비트)가 된다. pch=“ABCD”처럼 했을 때 ‘ABCD’는 5바이트 문자열(‘0x41’, ‘0x42’,‘0x43’, ‘0x44’, ‘0x00’)로 메모리에 저장된다. 한편, 유니코드를 사용할 때의 charV는 2 바이트 변수로변하며 따라서 그 값은 0x0041이 되며, 포인터로 정의된 pch는 그 자신의 크기는 변함없이 4바이트이나 ‘ABCD’의 값은‘0x0041’, ‘0x0042’, ‘0x0043’, ‘0x0044’, ‘0x0000’로 메모리에 저장되므로 12바이트 문자열이되는 셈이다. ANSI 문자와 UNICODE 처리에 있어서의 이러한 차이는 약간의 프로그램 성능의 변화를 초래할 수도 있다.유니코드를 입, 출력 소스로 예상하고 있는 윈도우 NT에서 ANSI 스트링을 사용하는 프로그램을 어떻게 처리하는지 그 순서를보면 대략 다음과 같다.


1. ANSI 윈도우 프로그램에서 ANSI 문자열을 윈도우 NT의 윈도우 시스템으로 보낸다

2. 윈도우 NT의 윈도우 시스템은 입력된 ANSI 문자열을 16비트 문자열 유니코드로 변환하여 윈도우 NT의 OS로 보낸다.

3. 윈도우 OS는 프로그램 실행 후 그 반환값을 윈도우 NT 윈도우 시스템으로 보낸다

4. 윈도우 NT 윈도우 시스템은 다시 이 값을 ANSI 윈도우 프로그램으로 보낸다. 이 때 ANSI 윈도우 프로그램이ANSI값을 예상하고 있다면 윈도우 NT 시스템은 윈도우 NT OS에서 반환받은 유니코드 값을 ANSI 형태로 변환하여 ANSI윈도우 프로그램으로 보낸다.


윈도우 NT는 native unicode, 즉 유니코드를 지원하도록 처음부터 디자인된 OS이다. 윈도우 NT는 문자열처리에 있어서 16비트를 사용하고 있으나 아직 대부분이 8비트를 사용하므로 NT는 위에서 나열한대로 변환기가 필요한 셈이다.

윈도우 9x는 유니코드를 거의 지원하지 않으므로 현 시점에서의 가장 바람직한 개발 환경은 역시 ANSI와 유니코드를 동시에지원하는 소스를 만드는 것이다. 이제 남은 지면을 통해 SBCS와 유니코드와의 관계를 API 차원에서 살펴보면서 유니코드,DBCS 그리고 SBCS과 관련된 API들을 잘 이해할 수 있기를 바란다.


Uncode 프로그램

일반적으로, 한 프로그램을 코딩한다고 말할 때 문자세트 사용의 관점에서 본다면 다음과 같은 세 가지 경우를 생각해 볼 수 있다.

·ANSI 버전 : ANSI 문자만 수용하는 프로그램

·UNICODE 버전 : Unicode 문자만 수용하는 버전

·통합버전 : 유니코드와 ANSI를 동시에 수용하는 프로그램    


어떤 버전을 사용하든지 프로그램의 로직은 말할 필요없이 동일하다. 다만 다른 점이라면 유니코드에서는 16비트로 문자를처리하며(ANSI 버전), 컴파일러가 유니코드 버전 API를 사용할 수 있도록 필요한 head 파일을 덧붙이며(UNICODE버전) 프로그램의 소스 파일에 _UNIC ODE를 정의해 주는 것(통합버전) 등이다. 여기서는 윈도우32 API를 사용하여하나의 소스프로그램이 어떻게 유니코드와 ANSI I/O를 동시에 인식할 수 있게 하는지를 주로 Run-Time에서 사용되는문자열의 처리와 관련된 API들을 알아보고 끝으로 i18n과 관련된 몇 가지 WIN32 API들을 알아보도록 하겠다.

우선 가장 먼저 우리가 이해해야 할 것이 있다면 바로 유니코드 또는 Wide Character(WC)를 정의하고 있는‘WCHAR.H’ 파일이다. C나 C++에서 정의하는 WC는 다른 어느 데이터 타입을 정의할 때와 마찬가지로 단순하다.


typedef unsigned short wchar_t;

wchar_t c = L‘A’에서, c의 값은 0x0041이 된다. CPU에서 0x0041이 저장될 때는 LSB(LeastSignifica nt Byte)을 먼저 나타내므로 0x0041은 메모리에서는 사실 ‘41 00’로 저장된다. 또한wchar_t * p = L“ABCD”;라고 정의했다면 CPU가 p의 값을 메모리에 저장할 때는 ‘4100 4200 43004400’처럼 된다. 여기서 유의해야 할 점은 하나의 문자열을 WC로 정의했다면 반드시 WC로 처리해야 한다는 점이다. 그렇지않으면 첫 번째 값 41을 읽은 후 00가 다음 문자로 처리되므로 문자열의 끝이 되어 버리는 셈이다. 너무 당연한 이치이지만자칫 잘못하면 오류가 발생할 수 있는 부분이기도 하다. 재미 삼아 다음 코딩을 실행했을 때의 결과값을 한번 생각해 보자.


wchar_t * p = L“ABCD”;

    printf(“%d”, strlen(p));

strlen() 런-타임 라이브러리는 WC를 처리하도록 고안되지 않았다. 따라서 위 코드를 실행했을 때의 출력값은 1이 될 것이다.


int strlen (const char * str)

       {

           int length = 0;

           while( *str++ )

                   ++length;

           return( length );

}

stelen()을 wcslen()으로 대치시키면 제 값을 출력할 수 있게된다. wcslen()은 다음과 같이 2 바이트를 처리하는 로직으로 되어있으므로 실제 입력되는 문자의 성질에 무관하게 2 바이트로 처리된다.  


size_t __cdecl wcslen(const wchar_t *wcs)

{

    const wchar_t * eos = wcs;

    while(*eos++);

    return ((size_t) (eos - wcs -1));

}


따라서 strlen()에서 발생하는 반대의 오류가 wcslen()에서 발생할 수 있다. 즉, strlen()를 정작사용해야 할 곳에 wcslen()을 사용하게 되면 그 값이 반으로 나타나게 될 것이다. 그렇다면 하나의 프로그램으로 상황에 따라strlen()이나 wcslen()을 선택적으로 사용하게 하고 싶다면 (WC와 SBCS의 두 가지 환경에서 하나의 소스코드로실행하고 싶다면), 다시 말해서 유니코드를 사용하여 프로그램의 국제화를 이루고 싶다면 어떻게 할 수 있겠는가?

이에 대한 답은 바로 통합형 버전의 API를 사용하는 것으로 헤드파일은 TCHAR.H이다. ‘#_UNICODE’와‘#define TCHAR.H’이 소스 프로그램에 정의되었을 때 소스 프로그램 위에 다음과 같은 형태로 수정해 주므로_UNICODE가 정의되었으면 _tstrlen()는 wcslen()로 실행하고, _UNCODE를 정의하지 않았다면_tstrlen()는 strlen()를 실행시키게 된다.


#define _UNICODE

    #include <TCHAR.H>


    wchar_t * p = L“ABCD”;

    printf(“%d”, _tstrlen(p));


문자열을 WC 형태로 저장하고 싶을 때 반드시 해당 문자열의 바로 앞에 L을 덧붙여 주어야 한다고 설명한 바 있다. 하지만반드시 WC만 사용하는 것이 아니므로 문자열 저장 방법 역시 위에서 설명한 Run-Time Library와 같은 형태의메커니즘이 필요한데, TEXT()가 바로 그것이다.


ifdef UNICODE #define __T(x) L##x

else #define __T(x) x

. . .

#define _T(x)  __T(x)

. . .

#define _TEXT(x) __T(x)

. . .

#define TEXT(x) _TEXT(x)

위의 로직은 단순하다. 예를 들어, UNCODE가 정의되었으면 __T(“ABCE”)를 L“ABCD”로 변환하고UNICODE가 정의되지 않았으면 __T(“ABCD”)는 기본형태를 사용한다는 지시이다. 여기서 기본형태란 ANSI 문자세트를의미하다. 그리고 __T는 _T 또는 _TEXT로 변환되므로 우리가 주로 사용하게 되는 매크로는 _TEXT()의 형태가 된다.맨 마지막 줄의 ‘define TEXT(x) _TEXT(x)’는 winnt.h에 정의되어 있는 것으로 알고있는데 간단하게‘windows.h’를 포함시키면 이런 메커니즘을 걱정할 필요없이 문자열을 항상 TEXT로 감싸주므로 유니코드를 사용할 수 있게되어있다. 따라서 위에서 사용된 로직을 염두에 두고 다시 수정해 보면 다음과 같다.


#define _UNICODE

#include <TCHAR.H>


TCHAR * p = TEXT(“ABCD”);


printf(“%d”, _tstrlen(p));


독자의 이해를 돕기 위해 다음과 같은 간단한 테스트 프로그램을 만들어 보았다.

본 소스코드를 여러 형태로 수정해 가면서 본 글에서 명시된 프로그램 국제화의 메커니즘들을 하나씩 실습해 보기바란다.  


#define _UNICODE


#include <TCHAR.H>


#include <windows.h>


int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,

        LPSTR lpCmdLine, int nCmdShow)

{

    TCHAR *wc = TEXT(“ABCD”);

    TCHAR msg[80];


    wsprintf(msg,“_tcslen=%u, sizeof(msg)=%u”,

          _tcslen(wc),sizeof(msg));

    MessageBox(NULL, msg, “UNICODE”, MB_OK);


    return 0;

}


유니코드를 사용한 프로그램의 국제화에 사용되는 데이터 타입의 정의에 대한 지금까지의 설명을 정리해보면 다음과 같다.


/* Generic types */

#ifdef UNICODE

  typedef wchar_t TCHAR;

 #else

  typedef unsigned char TCHAR;

#endif

typedef TCHAR * LPTSTR, *LPTCH;


/* 8-bit character specific */

typedef unsigned char CHAR;

typedef CHAR *LPSTR, *LPCH;


/* Unicode specific (wide characters) */

typedef unsigned wchar_t WCHAR;

typedef WCHAR *LPWSTR, *LPWCH;

문자열을 처리하는 Win32 API들은 대개가 두개의 프로그램으로 이루어져 있는데 하나는 API 이름이 A로 끝나는것이고, 다른 하나는 그 이름이 W로 끝나는 것이다. 여기서 A는 ANSI 표준을 의미하고 W는 Wide 문자, 즉 유니코드를의미한다고 생각하면 된다. 어떤 프로그램이 위에서 설명한 것처럼 통합형 API를 사용할 수도 있고 xxxA() 또는xxxW()와 같이 특정 API를 구체적으로 부를 수도 있다. 필자는 다음과 같은 세 가지 이유 때문에 언제나 통합형 API를사용하도록 권하고 싶다.


·코드의 포팅이 쉽다 (WIN NT, Win9x)

·코딩이 용이하다

·프로그램을 국제화 할 수 있다


여기서 말하는 포팅이란 단순히 윈도우 NT와 윈도우 95/98 간의 포팅을 의미한다. 예를 들어 어떤 프로그램을 만들어컴파일 할 때, 윈도우 NT용이면 UNICODE를 정의해 주고, 95/8용이면 UINCODE를 정의하지 않는다. 이로인해 통합형API들을 사용할 경우 적절한 API를 컴파일러가 알아서 선택하여 사용하므로 일관성 있는 코딩이 가능하고 코딩 또한 용이하게되며, 유니코드를 사용하면 전 세계의 모든 인코딩 시스템을 지원할 수 있게되므로 프로그램의 국제화를 쉽게 실현할 수 있게된다.문자열의 처리와 관련된 API들의 매크로는 대략 lstrcatA(W), lstrc mp(wcscmp),lstrcmpi(wcscmpi), lstrcpy, lstrlen, lstrcmp and lstrcmpi 등이 있으며 이 중lstrcatA(W), lstrcmp(wcscmp), lstrcmpi(wcscm pi), lstrcpy, lstrlen은 같은일을 하는 두개의 API가 있으며, lstrcmp and lstrcmpi은 CompareString()를 불러 UNICODE와ANSI를 구분하게 되어있다.

위의 예제를 잘 활용하여 여기에 나열된 API 매크로들을 충분히 이해할 수 있기 바란다. 앞서 약속한 대로, 유니코드 및 ANSI와 관련된 WIN32 API들 중 우리가 이해하여야 할 몇 가지 API들을 나열해 보면 다음과 같다.


GetCPInfo

MultiByteToWideChar

WideCharToMultiByte

IsTextUnicode

IsDBCSLeadByte

IsWindowUnicode

CharNext,

CharPrev

GetInfo()는 CPINFO 스트럭처에 로케일에 관련된 정보를 채워주며 CPINFO에 포함되는 정보는 다음과 같다.


struct _cpinfo {  

    UINT MaxCharSize;

    BYTE DefaultChar[MAX_DEFAULTCHAR];

    BYTE LeadByte[MAX_LEADBYTES];

} CPINFO;


MaxCharSize는 프로그램이 사용중인 코드페이지 내에서의 문자의 크기를 바이트 수로 나타낸다. 즉, 코드페이지 949한글 완성형의 경우는 SBCS과 DBCS이 혼합되어 있으므로 MaxCharSize는 두 바이트, 즉 2가 될 것이다.DefaultChar란 다른 문자 세트가 현 시스템의 코드페이지로 변환될 때 사용할 문자를 의미하며 이것은 WideCharToMultiByte의 기본값이 되기도 한다. LeadByte란 사용중인 코드 페이지의 LeadByte 범위를 나타나며LeadByte가 없는 경우는 LeadByte[]는 NULL로 채워진다. MultiByteToWideChar와WideCharToMulti Byte는 SBCS과 DBCS에서 공통적으로 사용되는 문자들을 매핑하는 목적으로 사용되며,IsTextUnicode는 대상 문자열 중 UNICODE가 포함되었는지를 알아내려할 때 사용한다. WIN9x의 경우는UNICODE를 거의 지원하지 않으므로 본 API는 시스템 차원에서 무시된다. 그리고 IsDBC SLeadByte는 대상 문자가DBCS의 첫 번째 바이트인지를 알려고 할 때 사용할 수 있으며, IsWindow Unicode를 사용하여 윈도우에서 처리하는문자열의 타입이 UNICODE인지 아닌지를 판단한 수 있다.

끝으로 CharNext와 CharPrev를 사용하여 다음에 오는 문자를 살펴볼 수 있다. 이 두 API는 WIN16ANSI C를 사용하는 사람이라면 AnsiNext와 AnsiPrev를 연상하게 될 것이다. WIN32에서는 이전 코드와의호환성을 유지하기 위해 다음과 같은 매크로를 사용하고 있다.


#define AnsiNext CharNext

#define AnsiPrev CharPrev

반응형

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받을 수 있습니다.

반응형
한글코드표
EUC-KR(KSC 5601)



B0

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      가  각  간  갇  갈  갉  갊  감  갑  값  갓  갔  강  갖  갗

  같  갚  갛  개  객  갠  갤  갬  갭  갯  갰  갱  갸  갹  갼  걀

  걋  걍  걔  걘  걜  거  걱  건  걷  걸  걺  검  겁  것  겄  겅

  겆  겉  겊  겋  게  겐  겔  겜  겝  겟  겠  겡  겨  격  겪  견

  겯  결  겸  겹  겻  겼  경  곁  계  곈  곌  곕  곗  고  곡  곤

  곧  골  곪  곬  곯  곰  곱  곳  공  곶  과  곽  관  괄  괆

B1

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      괌  괍  괏  광  괘  괜  괠  괩  괬  괭  괴  괵  괸  괼  굄

  굅  굇  굉  교  굔  굘  굡  굣  구  국  군  굳  굴  굵  굶  굻

  굼  굽  굿  궁  궂  궈  궉  권  궐  궜  궝  궤  궷  귀  귁  귄

  귈  귐  귑  귓  규  균  귤  그  극  근  귿  글  긁  금  급  긋

  긍  긔  기  긱  긴  긷  길  긺  김  깁  깃  깅  깆  깊  까  깍

  깎  깐  깔  깖  깜  깝  깟  깠  깡  깥  깨  깩  깬  깰  깸

B2

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      깹  깻  깼  깽  꺄  꺅  꺌  꺼  꺽  꺾  껀  껄  껌  껍  껏

  껐  껑  께  껙  껜  껨  껫  껭  껴  껸  껼  꼇  꼈  꼍  꼐  꼬

  꼭  꼰  꼲  꼴  꼼  꼽  꼿  꽁  꽂  꽃  꽈  꽉  꽐  꽜  꽝  꽤

  꽥  꽹  꾀  꾄  꾈  꾐  꾑  꾕  꾜  꾸  꾹  꾼  꿀  꿇  꿈  꿉

  꿋  꿍  꿎  꿔  꿜  꿨  꿩  꿰  꿱  꿴  꿸  뀀  뀁  뀄  뀌  뀐

  뀔  뀜  뀝  뀨  끄  끅  끈  끊  끌  끎  끓  끔  끕  끗  끙  

B3

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      끝  끼  끽  낀  낄  낌  낍  낏  낑  나  낙  낚  난  낟  날

  낡  낢  남  납  낫  났  낭  낮  낯  낱  낳  내  낵  낸  낼  냄

  냅  냇  냈  냉  냐  냑  냔  냘  냠  냥  너  넉  넋  넌  널  넒

  넓  넘  넙  넛  넜  넝  넣  네  넥  넨  넬  넴  넵  넷  넸  넹

  녀  녁  년  녈  념  녑  녔  녕  녘  녜  녠  노  녹  논  놀  놂

  놈  놉  놋  농  높  놓  놔  놘  놜  놨  뇌  뇐  뇔  뇜  뇝

B4

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      뇟  뇨  뇩  뇬  뇰  뇹  뇻  뇽  누  눅  눈  눋  눌  눔  눕

  눗  눙  눠  눴  눼  뉘  뉜  뉠  뉨  뉩  뉴  뉵  뉼  늄  늅  늉

  느  늑  는  늘  늙  늚  늠  늡  늣  능  늦  늪  늬  늰  늴  니

  닉  닌  닐  닒  님  닙  닛  닝  닢  다  닥  닦  단  닫  달  닭

  닮  닯  닳  담  답  닷  닸  당  닺  닻  닿  대  댁  댄  댈  댐

  댑  댓  댔  댕  댜  더  덕  덖  던  덛  덜  덞  덟  덤  덥

B5

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      덧  덩  덫  덮  데  덱  덴  델  뎀  뎁  뎃  뎄  뎅  뎌  뎐

  뎔  뎠  뎡  뎨  뎬  도  독  돈  돋  돌  돎  돐  돔  돕  돗  동

  돛  돝  돠  돤  돨  돼  됐  되  된  될  됨  됩  됫  됴  두  둑

  둔  둘  둠  둡  둣  둥  둬  뒀  뒈  뒝  뒤  뒨  뒬  뒵  뒷  뒹

  듀  듄  듈  듐  듕  드  득  든  듣  들  듦  듬  듭  듯  등  듸

  디  딕  딘  딛  딜  딤  딥  딧  딨  딩  딪  따  딱  딴  딸

B6

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

  텭  땀  땁  땃  땄  땅  땋  때  땍  땐  땔  땜  땝  땟  땠  땡

  떠  떡  떤  떨  떪  떫  떰  떱  떳  떴  떵  떻  떼  떽  뗀  뗄

  뗌  뗍  뗏  뗐  뗑  뗘  뗬  또  똑  똔  똘  똥  똬  똴  뙈  뙤

  뙨  뚜  뚝  뚠  뚤  뚫  뚬  뚱  뛔  뛰  뛴  뛸  뜀  뜁  뜅  뜨

  뜩  뜬  뜯  뜰  뜸  뜹  뜻  띄  띈  띌  띔  띕  띠  띤  띨  띰

  띱  띳  띵  라  락  란  랄  람  랍  랏  랐  랑  랒  랖  랗

B7

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

  퇏  래  랙  랜  랠  램  랩  랫  랬  랭  랴  략  랸  럇  량  러

  럭  런  럴  럼  럽  럿  렀  렁  렇  레  렉  렌  렐  렘  렙  렛

  렝  려  력  련  렬  렴  렵  렷  렸  령  례  롄  롑  롓  로  록

  론  롤  롬  롭  롯  롱  롸  롼  뢍  뢨  뢰  뢴  뢸  룀  룁  룃

  룅  료  룐  룔  룝  룟  룡  루  룩  룬  룰  룸  룹  룻  룽  뤄

  뤘  뤠  뤼  뤽  륀  륄  륌  륏  륑  류  륙  륜  률  륨  륩

B8

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

  툩  륫  륭  르  륵  른  를  름  릅  릇  릉  릊  릍  릎  리  릭

  린  릴  림  립  릿  링  마  막  만  많  맏  말  맑  맒  맘  맙

  맛  망  맞  맡  맣  매  맥  맨  맬  맴  맵  맷  맸  맹  맺  먀

  먁  먈  먕  머  먹  먼  멀  멂  멈  멉  멋  멍  멎  멓  메  멕

  멘  멜  멤  멥  멧  멨  멩  며  멱  면  멸  몃  몄  명  몇  몌

  모  목  몫  몬  몰  몲  몸  몹  못  몽  뫄  뫈  뫘  뫙  뫼

B9

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      묀  묄  묍  묏  묑  묘  묜  묠  묩  묫  무  묵  묶  문  묻

  물  묽  묾  뭄  뭅  뭇  뭉  뭍  뭏  뭐  뭔  뭘  뭡  뭣  뭬  뮈

  뮌  뮐  뮤  뮨  뮬  뮴  뮷  므  믄  믈  믐  믓  미  믹  민  믿

  밀  밂  밈  밉  밋  밌  밍  및  밑  바  박  밖  밗  반  받  발

  밝  밞  밟  밤  밥  밧  방  밭  배  백  밴  밸  뱀  뱁  뱃  뱄

  뱅  뱉  뱌  뱍  뱐  뱝  버  벅  번  벋  벌  벎  범  법  벗

BA

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      벙  벚  베  벡  벤  벧  벨  벰  벱  벳  벴  벵  벼  벽  변

  별  볍  볏  볐  병  볕  볘  볜  보  복  볶  본  볼  봄  봅  봇

  봉  봐  봔  봤  봬  뵀  뵈  뵉  뵌  뵐  뵘  뵙  뵤  뵨  부  북

  분  붇  불  붉  붊  붐  붑  붓  붕  붙  붚  붜  붤  붰  붸  뷔

  뷕  뷘  뷜  뷩  뷰  뷴  뷸  븀  븃  븅  브  븍  븐  블  븜  븝

  븟  비  빅  빈  빌  빎  빔  빕  빗  빙  빚  빛  빠  빡  빤  

BB

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      빨  빪  빰  빱  빳  빴  빵  빻  빼  빽  뺀  뺄  뺌  뺍  뺏

  뺐  뺑  뺘  뺙  뺨  뻐  뻑  뻔  뻗  뻘  뻠  뻣  뻤  뻥  뻬  뼁

  뼈  뼉  뼘  뼙  뼛  뼜  뼝  뽀  뽁  뽄  뽈  뽐  뽑  뽕  뾔  뾰

  뿅  뿌  뿍  뿐  뿔  뿜  뿟  뿡  쀼  쁑  쁘  쁜  쁠  쁨  쁩  삐

  삑  삔  삘  삠  삡  삣  삥  사  삭  삯  산  삳  살  삵  삶  삼

  삽  삿  샀  상  샅  새  색  샌  샐  샘  샙  샛  샜  생  샤

BC

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      샥  샨  샬  샴  샵  샷  샹  섀  섄  섈  섐  섕  서  석  섞

  섟  선  섣  설  섦  섧  섬  섭  섯  섰  성  섶  세  섹  센  셀

  셈  셉  셋  셌  셍  셔  셕  션  셜  셤  셥  셧  셨  셩  셰  셴

  셸  솅  소  속  솎  손  솔  솖  솜  솝  솟  송  솥  솨  솩  솬

  솰  솽  쇄  쇈  쇌  쇔  쇗  쇘  쇠  쇤  쇨  쇰  쇱  쇳  쇼  쇽

  숀  숄  숌  숍  숏  숑  수  숙  순  숟  술  숨  숩  숫  숭

BD

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      숯  숱  숲  숴  쉈  쉐  쉑  쉔  쉘  쉠  쉥  쉬  쉭  쉰  쉴

  쉼  쉽  쉿  슁  슈  슉  슐  슘  슛  슝  스  슥  슨  슬  슭  슴

  습  슷  승  시  식  신  싣  실  싫  심  십  싯  싱  싶  싸  싹

  싻  싼  쌀  쌈  쌉  쌌  쌍  쌓  쌔  쌕  쌘  쌜  쌤  쌥  쌨  쌩

  썅  써  썩  썬  썰  썲  썸  썹  썼  썽  쎄  쎈  쎌  쏀  쏘  쏙

  쏜  쏟  쏠  쏢  쏨  쏩  쏭  쏴  쏵  쏸  쐈  쐐  쐤  쐬  쐰

BE

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      쐴  쐼  쐽  쑈  쑤  쑥  쑨  쑬  쑴  쑵  쑹  쒀  쒔  쒜  쒸

  쒼  쓩  쓰  쓱  쓴  쓸  쓺  쓿  씀  씁  씌  씐  씔  씜  씨  씩

  씬  씰  씸  씹  씻  씽  아  악  안  앉  않  알  앍  앎  앓  암

  압  앗  았  앙  앝  앞  애  액  앤  앨  앰  앱  앳  앴  앵  야

  약  얀  얄  얇  얌  얍  얏  양  얕  얗  얘  얜  얠  얩  어  억

  언  얹  얻  얼  얽  얾  엄  업  없  엇  었  엉  엊  엌  엎

BF

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      에  엑  엔  엘  엠  엡  엣  엥  여  역  엮  연  열  엶  엷

  염  엽  엾  엿  였  영  옅  옆  옇  예  옌  옐  옘  옙  옛  옜

  오  옥  온  올  옭  옮  옰  옳  옴  옵  옷  옹  옻  와  왁  완

  왈  왐  왑  왓  왔  왕  왜  왝  왠  왬  왯  왱  외  왹  왼  욀

  욈  욉  욋  욍  요  욕  욘  욜  욤  욥  욧  용  우  욱  운  울

  욹  욺  움  웁  웃  웅  워  웍  원  월  웜  웝  웠  웡  웨

C0

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      웩  웬  웰  웸  웹  웽  위  윅  윈  윌  윔  윕  윗  윙  유

  육  윤  율  윰  윱  윳  융  윷  으  윽  은  을  읊  음  읍  읏

  응  읒  읓  읔  읕  읖  읗  의  읜  읠  읨  읫  이  익  인  일

  읽  읾  잃  임  입  잇  있  잉  잊  잎  자  작  잔  잖  잗  잘

  잚  잠  잡  잣  잤  장  잦  재  잭  잰  잴  잼  잽  잿  쟀  쟁

  쟈  쟉  쟌  쟎  쟐  쟘  쟝  쟤  쟨  쟬  저  적  전  절  젊

C1

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      점  접  젓  정  젖  제  젝  젠  젤  젬  젭  젯  젱  져  젼

  졀  졈  졉  졌  졍  졔  조  족  존  졸  졺  좀  좁  좃  종  좆

  좇  좋  좌  좍  좔  좝  좟  좡  좨  좼  좽  죄  죈  죌  죔  죕

  죗  죙  죠  죡  죤  죵  주  죽  준  줄  줅  줆  줌  줍  줏  중

  줘  줬  줴  쥐  쥑  쥔  쥘  쥠  쥡  쥣  쥬  쥰  쥴  쥼  즈  즉

  즌  즐  즘  즙  즛  증  지  직  진  짇  질  짊  짐  집  짓

C2

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      징  짖  짙  짚  짜  짝  짠  짢  짤  짧  짬  짭  짯  짰  짱

  째  짹  짼  쨀  쨈  쨉  쨋  쨌  쨍  쨔  쨘  쨩  쩌  쩍  쩐  쩔

  쩜  쩝  쩟  쩠  쩡  쩨  쩽  쪄  쪘  쪼  쪽  쫀  쫄  쫌  쫍  쫏

  쫑  쫓  쫘  쫙  쫠  쫬  쫴  쬈  쬐  쬔  쬘  쬠  쬡  쭁  쭈  쭉

  쭌  쭐  쭘  쭙  쭝  쭤  쭸  쭹  쮜  쮸  쯔  쯤  쯧  쯩  찌  찍

  찐  찔  찜  찝  찡  찢  찧  차  착  찬  찮  찰  참  찹  찻

C3

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      찼  창  찾  채  책  챈  챌  챔  챕  챗  챘  챙  챠  챤  챦

  챨  챰  챵  처  척  천  철  첨  첩  첫  첬  청  체  첵  첸  첼

  쳄  쳅  쳇  쳉  쳐  쳔  쳤  쳬  쳰  촁  초  촉  촌  촐  촘  촙

  촛  총  촤  촨  촬  촹  최  쵠  쵤  쵬  쵭  쵯  쵱  쵸  춈  추

  축  춘  출  춤  춥  춧  충  춰  췄  췌  췐  취  췬  췰  췸  췹

  췻  췽  츄  츈  츌  츔  츙  츠  측  츤  츨  츰  츱  츳  층

C4

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      치  칙  친  칟  칠  칡  침  칩  칫  칭  카  칵  칸  칼  캄

  캅  캇  캉  캐  캑  캔  캘  캠  캡  캣  캤  캥  캬  캭  컁  커

  컥  컨  컫  컬  컴  컵  컷  컸  컹  케  켁  켄  켈  켐  켑  켓

  켕  켜  켠  켤  켬  켭  켯  켰  켱  켸  코  콕  콘  콜  콤  콥

  콧  콩  콰  콱  콴  콸  쾀  쾅  쾌  쾡  쾨  쾰  쿄  쿠  쿡  쿤

  쿨  쿰  쿱  쿳  쿵  쿼  퀀  퀄  퀑  퀘  퀭  퀴  퀵  퀸  퀼

C5

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      큄  큅  큇  큉  큐  큔  큘  큠  크  큭  큰  클  큼  큽  킁

  키  킥  킨  킬  킴  킵  킷  킹  타  탁  탄  탈  탉  탐  탑  탓

  탔  탕  태  택  탠  탤  탬  탭  탯  탰  탱  탸  턍  터  턱  턴

  털  턺  텀  텁  텃  텄  텅  테  텍  텐  텔  템  텝  텟  텡  텨

  텬  텼  톄  톈  토  톡  톤  톨  톰  톱  톳  통  톺  톼  퇀  퇘

  퇴  퇸  툇  툉  툐  투  툭  툰  툴  툼  툽  툿  퉁  퉈  퉜

C6

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      퉤  튀  튁  튄  튈  튐  튑  튕  튜  튠  튤  튬  튱  트  특

  튼  튿  틀  틂  틈  틉  틋  틔  틘  틜  틤  틥  티  틱  틴  틸

  팀  팁  팃  팅  파  팍  팎  판  팔  팖  팜  팝  팟  팠  팡  팥

  패  팩  팬  팰  팸  팹  팻  팼  팽  퍄  퍅  퍼  퍽  펀  펄  펌

  펍  펏  펐  펑  페  펙  펜  펠  펨  펩  펫  펭  펴  편  펼  폄

  폅  폈  평  폐  폘  폡  폣  포  폭  폰  폴  폼  폽  폿  퐁

C7

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      퐈  퐝  푀  푄  표  푠  푤  푭  푯  푸  푹  푼  푿  풀  풂

  품  풉  풋  풍  풔  풩  퓌  퓐  퓔  퓜  퓟  퓨  퓬  퓰  퓸  퓻

  퓽  프  픈  플  픔  픕  픗  피  픽  핀  필  핌  핍  핏  핑  하

  학  한  할  핥  함  합  핫  항  해  핵  핸  핼  햄  햅  햇  했

  행  햐  향  허  헉  헌  헐  헒  험  헙  헛  헝  헤  헥  헨  헬

  헴  헵  헷  헹  혀  혁  현  혈  혐  협  혓  혔  형  혜  혠

C8

  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F

A0

B0

C0

D0

E0

F0

      혤  혭  호  혹  혼  홀  홅  홈  홉  홋  홍  홑  화  확  환

  활  홧  황  홰  홱  홴  횃  횅  회  획  횐  횔  횝  횟  횡  효

  횬  횰  횹  횻  후  훅  훈  훌  훑  훔  훗  훙  훠  훤  훨  훰

  훵  훼  훽  휀  휄  휑  휘  휙  휜  휠  휨  휩  휫  휭  휴  휵

  휸  휼  흄  ?   흉  흐  흑  흔  흖  흗  흘  흙  흠  흡  흣  흥

  흩  희  흰  흴  흼  흽  힁  히  힉  힌  힐  힘  힙  힛  힝

반응형

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받을 수 있습니다.

반응형

http://changho-myrepository.blogspot.com/2007/10/pragma.html

2007년 10월 17일 수요일

#pragma

아직 #pragma까진 써본적 없지만, 앞으로 필요할것 같아서 퍼왔음
#pragma는 define 이나 include와 같이 #으로 시작하는 전처리구문(precompiler)의 하나이다.
컴파일러에 종속적인 구문이라 컴파일러가 변경되었을 경우 제대로된 동작을 보장하지 못하므로 프로젝트 진행중에 서로 다른 컴파일러를 사용한다면 사용하지 않음이 바람직 하겠다.
- 대신 대체하는 문법을 사용해야 되겠다.
#pragma once
이것은 "컴파일러에게 한번만 컴파일해!" 라고 명령한다.
헤더의 중복을 막아준다.
무슨말인가 하면

a.h를 구현한 a.cpp, a.h는 독립적이다.(include가 없다.)
b.h를 구현한 b.cpp, c.h, a.h순서로 include
c.h를 구현한 c.cpp, a.h를 include

컴파일하면 b.h에서 c.h를 포함시키라고 되어있네? 하고 c.h에 들어가고 어? a.h를 포함하라고 그러네? 이러고 a.h를 포함한 c.h가 b.h로 돌아온다 그리고 a.h를 포함하라는 명령을 받고 a.h를 추가하다보면 같은 변수와 함수선언이 되어있다. 에러에러~
같은 선언이 두 번 반복되니 당연히 충돌이 난다. 컴파일러가 똑똑하여 단순히 경고 처리만 해주고 알아서 하나로 종합해줄 수도 있지만 대부분의 기본적인 컴파일러는 이건 아니잖아~ 한다.
이럴 때 써주는 것이다. pragma once
이는 c기본문법을 사용하여 구현할 수 있다.

#ifdef _MYCOMPILECK
#define _MYCOMPILECK
// 헤더 파일의 내용 선언
#endif

#pragma comment()
기본적인 pragma comment()의 형식은 다음과 같다.

#pragma comment( comment-type, ["comment string"] )

[] 안의 구문은 comment-type에 따라 필요할 경우 사용하는 것이다.
comment type에는 compiler, exestr, lib, linker, user 등이 올 수 있다.

#pragma comment( linker, "/subsystem:windows" )
#pragma comment( linker, "/subsystem:console" )

linker 를 사용하면 프로젝트를 console application인지 win32 application인지 명시해줄 수 있다.
또한 섹션의 설정을 할 수 있다.

#pragme comment( linker, "SECTION:.SHAREDATA,RWS" )

#pragma data_seg("SHAREDATA") 와 함께 사용하여 공유 메모리를 생성한다.
위의 명령어 대신 def 파일 안에 아래와 같이 해주어도 된다.

SECTIONS
SHAREDATA READ WRITE SHARED

이 중 가장 대표적인 사용법은 명시적인 라이브러리의 링크이다.

#pragma comment(lib, "xxxx.lib")

와 같이 사용하여 해당 라이브러리를 링크시켜 준다.
여러사람이 같이 수행하는 프로젝트의 경우 이와 같은 방법을 사용하여 lib를 링크하는 것이 라이브러리가 링크되어있다는 사실을 알기에도 좋고 굳이 주석다라 설명할 필요도 없어 좋지 않나 싶다. (있다는 사실은 알지만 아직 프로젝트 수행중 실제로 사용해 본적은 없음)
#pragma data_seg()
pragma data_seg()의 형식은 다음과 같다.

#pragma data_seg( ["section-name"[, "section-class"] ] )

[]는 사용하지 않아도 된다는 의미이다.

#pragma data_seg( "SHAREDATA" )
int x;
char y;
#pragma data_seg()

DLL 파일을 만들어보면서 제일 많이 사용해 보았고 가장 헷갈려 했던 부분이기도 하다.
DLL의 데이터 공유를 하기 위해 사용한다.
공유할 섹션을 만드는 것이다. 위의 명령어는 필수적으로 위에서 사용된 두 가지중 한가지 방법과 함께 사용 되어야 한다.

#pragme comment( linker, "SECTION:.SHAREDATA,RWS" )
SECTIONS
SHAREDATA READ WRITE SHARED

둘 다 해당 SECTION(SHAREDATA)의 허용 범위(?속성?)를 설정하는 것이다. READ, WRITE, SHARED 세 가지를 쓴다는 의미~
해당 사항에 대해 msdn에서 자세한 정보를 발견하지 못해 적지 못하였다(검색능력의 부족!!)
이제 변수 x와 y는 해당 dll을 사용하는 외부 파일과 같이 공유할 수 있는 변수가 되었다.(외부에서 접근 가능하게 되었다.)
이렇게 공유하는 변수는 물론 new로 메모리를 할당한 변수도 공유 가능하다.
특히 new 나 memalloc(이건 아직 미확인이지만 같은 메모리 할당이므로 가능할 것으로 본다)으로 메모리할당한 변수들은 dll외부에서도 해제(delete) 가능하다.
#pragma warning
특정 경고를 끄고 싶을 때 사용한다.
비 쥬얼 스튜디오의 버전이 다르기 때문에 뜨는 경고는 더더욱이 귀찮은 존재이다.(하지만 수정해서 손해볼 것은 없다. 그것이 곧 버그로 이어질 수 있기 때문이다. 특히 형변환의 경우 강제 캐스팅하여 확실히 명시해주는 것이 좋다. 일부러 그 값을 떼어낸다는 프로그래머의 의지를 컴파일러에게 보여주자. 부지런할수록 후에 손이 가는 일이 적어진다. 노력하자~)
형식은 이와 같다.

#pragma warning( warning-specifier : warning-number-list [; warning-specifier : warning-number-list...] )
#pragma warning( push[ ,n ] )
#pragma warning( pop )

실제 사용은 아래와 같이 한다.

#pragma warning( disable:4996 )

#pragma message()
컴파일 중에 메세지를 뿌려준다.
말이 필요없다-.-/

#pragma message("merong")

작성자: changho 위치 오전 11:18

반응형

+ Recent posts