320x100

아스키 코드(ASCII CODE) 테이블

Posted on 2014. 2. 11. 17:59
Filed Under Programming/Basic

반응형

[LOG] 로그 남기기 메시지 유형

Posted on 2009. 6. 19. 13:01
Filed Under Programming/Basic


“second data reading failed. cause = [IOException message=“socket read failed.”]peer=“10.10.10.13” port=1234  auth=“base23” thread=32”
반응형

[펌] 초간단 TinyXml 사용 방법

Posted on 2009. 6. 16. 15:25
Filed Under Programming/Basic

초간단 TinyXml 사용 방법

TinyXML: http://www.sourceforge.net/projects/tinyxml


작성자
:
최흥배 ( jacking75@gmail.com )
문서 최종 수정일 : 2004.10.09

 

--- 예제로 사용 될 xml 문서

<?xml version="1.0" encoding="euc-kr"?>

 <MAJAK>

 <LOBBY_CONTROL>

 <OptionIndex="1125"X="697"Y="3"W="18"H="15">로비옵션</Option>

 <WindowHideIndex="1160"X="732"Y="3"W="18"H="15">숨기기</WindowHide>

 <LobbyChangeIndex="1027"X="32"Y="40"W="116"H="40">로비나가기</LobbyChange>

 <EnterRoom Index="1033" X="152"Y="40" W="116"H="40">방들어가기</EnterRoom>

 <MakeRoom Index="1011"X="276"Y="40" W="116" H="40">방만들기</MakeRoom>

 <RoomInfo Index="1015" X="398" Y="40" W="116" H="40">관전자정보</RoomInfo>

 

 <RoomList Index="1002" X="28" Y="94" W="507" H="257">방리스트</RoomList>

 <UserList Index="1037" X="568" Y="102" W="216" H="233">유저리스트</UserList>

  

<FindText Index="1090" X="603" Y="360" W="87" H="20">유저찾기입력</FindText>

 <IDFind Index="1091" X="693" Y="360" W="20" H="18">ID 찾기</IDFind>

  

<IsWhisper Index="1088" X="717" Y="357" W="12" H="10">귓속말설정</IsWhisper>

 <IsInvite Index="1089" X="717"Y="373" W="12" H="10">초대설정</IsInvite>

  

 

<ChatInput Index="1035" X="34" Y="500" W="416" H="18">채팅입력</ChatInput>

 <Charging Index="1005" X="461" Y="498" W="60" H="20">신고</Charging>

 <Banner Index="2000" X="10" Y="536" W="545" H="44">배너</Banner>

  

<QuickStart Index="1084" X="573" Y="550" W="100" H="24">바로시작</QuickStart>

 <FullScreen Index="1172" X="677" Y="550" W="100" H="24">전체화면</FullScreen>

 <Windowed Index="1173" X="677" Y="550" W="100" H="24">화면</Windowed>

 <Avata X="577" Y="420" W="56" H="111">아바타</Avata>

 </LOBBY_CONTROL>

 <ROOM_CONTROL>

 <GAME_BTNIndex="7000"X="706"Y="0"File=".\\data\\Gbtn_Capture.spr">화면캡쳐</GAME_BTN>

 <GAME_BTNIndex="7001"X="729"Y="2"File=".\\data\\Gbtn_WndMin.spr">숨기기</GAME_BTN>

 <GAME_BTNIndex="7003"X="773"Y="2"File=".\\data\\Gbtn_WndClose.spr">나가기</GAME_BTN>

 <GAME_BTNIndex="7004"X="640"Y="509"File=".\\data\\Gbtn_Entry.spr">참여</GAME_BTN>

 <GAME_BTNIndex="7005"X="640"Y="509"File=".\\data\\Gbtn_Observe.spr">관전</GAME_BTN>

 <GAME_BTNIndex="7017"X="682"Y="509"File=".\\data\\Gbtn_ObserveInfo.spr">관전자정보</GAME_BTN>

 <GAME_BTNIndex="7006"X="745"Y="509"File=".\\data\\Gbtn_Option.spr">설정</GAME_BTN>

 <GAME_BTNIndex="7007"X="640"Y="539"File=".\\data\\Gbtn_Invite.spr">초대</GAME_BTN>

 <GAME_BTNIndex="7008"X="689"Y="539"File=".\\data\\Gbtn_112.spr">신고</GAME_BTN>

 <GAME_BTNIndex="7009"X="738"Y="539"File=".\\data\\Gbtn_Out.spr">방 나가기</GAME_BTN>

  

<GAME_BTNIndex="7010"X="172"Y="578"File=".\\data\\MajakBtn_Bbong.spr">뽕</GAME_BTN>

 <GAME_BTNIndex="7011"X="213"Y="578"File=".\\data\\MajakBtn_Jji.spr">찌</GAME_BTN>

 <GAME_BTNIndex="7012"X="254"Y="578"File=".\\data\\MajakBtn_Kkang.spr">깡</GAME_BTN>

 <GAME_BTNIndex="7013"X="295"Y="578"File=".\\data\\MajakBtn_Rich.spr">리치</GAME_BTN>

 <GAME_BTNIndex="7014"X="336"Y="578"File=".\\data\\MajakBtn_Cheumo.spr">츠모</GAME_BTN>

 <GAME_BTNIndex="7015"X="377"Y="578"File=".\\data\\MajakBtn_Rone.spr">론</GAME_BTN>

 <GAME_BTNIndex="7016"X="418"Y="578"File=".\\data\\MajakBtn_Pass.spr">패스</GAME_BTN>

 <GAME_BTNIndex="7018"X="266"Y="400"File=".\\data\\Gbtn_Start.spr">게임시작요청</GAME_BTN>

  

<GAME_BTNIndex="7019"X="190"Y="478"File=".\\data\\MaJakResult_Btn.spr">결과창의OK</GAME_BTN>

 </ROOM_CONTROL>

 <ROOM_LAYOUT>

 <USERINFO_POSNUM="0"X="12"Y="32">왼쪽상단</USERINFO_POS>

 <USERINFO_POSNUM="1"X="514"Y="32">오른쪽상단</USERINFO_POS>

 <USERINFO_POSNUM="2"X="563"Y="557">오른쪽하단</USERINFO_POS>

 <USERINFO_POSNUM="3"X="62"Y="562">왼쪽하단</USERINFO_POS>

 </ROOM_LAYOUT>

 </MAJAK>

 ----------

  

1. TinyXml을 사용 할려면

 

- 먼저 TinyXml 라이브러리를 다운 받은 후 빌더를 해야된다. 빌더를 할 때는 이 라이브러리를 사용할 프로젝트가 싱 쓰레드인지 멀티 쓰레드 인지에 따라서 tinyXml쓰레드 옵션을 설정 후 빌드를 해야된다.

 - 빌드가 끝나면 사용할 프로젝트에서 lib파일을 링크 시킨다.

 - tinyxml.h, tinyxml.cpp, tinyxmlerror.cpp, tinyxmlparser.cpp를 프로젝트 폴더에 포함시킨다.

 - 위 파일 중 tinyxml.h파일만 소스에서 include 하면 된다.

 - 사용할 프로젝트가 만약 mfc 프로젝트로 stdafx 파일을 사용한다면 tinyxml.cpp, tinyxmlerror.cpp, tinyxmlparser.cpp 의 상단에 #include stdafx.h를 입력해야 된다.

 - TinyXml을 사용한 후 따로 delete로 지울 필요는 없다( 즉TiXmlNode* node; 이렇게사용된것들..).

  

2. xml 파일 읽기

 - TiXmlDocument doc( "MaJak_Para.xml" );

doc.LoadFile();

  

- TiXmlDocument m_XmlDoc;

  if( m_XmlDoc.LoadFile("MaJak_Para.xml" ) == false )

       return ERR_LOADXML;

  

3. 노드의 이동

 - 루트에서시작하여바로 Option 노드로이동할려면

TiXmlNode* node = doc.FirstChild("MAJAK")->FirstChild("LOBBY_CONTROL")->FirstChild( "Option" );

 - 루트에서 LOBBY_CONTROL노드로이동할려면

node = doc.FirstChild();

node = node->NextSibling();

 int Pos = 1

for( i = 0; i < Pos; ++i )

{

    node = node->FirstChild();

} 

여기에서 Pos 2하면 LOBBY_CONTROL노드의첫번째자식까지이동한다.

 - LOBBY_CONTROL 노드에있는상태에서노드의자식LobbyExit노드로바로갈려면

node = node->FirstChild( "LobbyExit" ); 
-노드의값을볼려면 node->Value()하면문자가나온다.

 - 도큐먼트의 top 레벨의기준으로해서자식노드로이동할려면

 count = 0;

for( node = doc.FirstChild();

      node;

      node = node->NextSibling() )

{

    count++;

}

 

// 아래서위로이동할려면

count = 0;

 for( node = doc.LastChild();

    node;

    node = node->PreviousSibling() )

{

    count++;

}

  

// 이터레이터를사용

count = 0;

for( node = doc.IterateChildren( 0 );

      node;

      node = doc.IterateChildren( node ) )

{

     count++;

}

  

4. 노드의애트리뷰트값을얻기

 - 현재노드에서모든자식노드의애트리뷰트값을얻을려면다음과같이한다.

 

node = node->FirstChild();

CONTROL_RECT ControlRect;

TiXmlElement* elem;

 

while( node != NULL )

{

    elem = node->ToElement();

    elem->Attribute( "Index", &ControlRect.index )

    elem->Attribute( "X", &ControlRect.x );

    elem->Attribute( "Y", &ControlRect.y );

    elem->Attribute( "W", &ControlRect.w );

    elem->Attribute( "H", &ControlRect.h );

 

    node = node->NextSibling();

}

 Attribute 리턴값으로애트리뷰트값의문자형을얻을있다.

  

 

- 애트리뷰트이동방법

// 노드의애트리뷰트에서마지막애트리뷰트까지이동

count = 0;

 for( element = todoElement->FirstChildElement();

      element;

      element = element->NextSiblingElement() )

{

     count++;

}

 

5. 애트리뷰트를 얻을 수 있도록 노드 대신 element를 얻는 방법

node = node->FirstChild();

TiXmlElement* Rootelem = node->NextSiblingElement( _strNodeName );

TiXmlElement* elem = Rootelem->FirstChildElement( _strSubNodeName );

elem->Attribute( "Index", &iIndex );

 

6. 애트리뷰트의첫버째속성과다음속성을얻는방법

 

TiXmlAttribute* Attribut;

Attribut = elem->FirstAttribute()

  

// 정수스트링을얻을있다.

Attribut->IntValue();
Attribut->Value();

  

// 다음애트리뷰트값을얻을렴ㄴ

Attribut = Attribut->Next();

 


<출처: http://jacking.springnote.com/pages/2844150>
<글주인블로그: http://blog.naver.com/jacking75>

반응형

[C++] static storage class 란?

Posted on 2009. 4. 28. 18:39
Filed Under Programming/Basic


어떤 대상이 static으로 선언되어 있다면 이것이 의미하는 바는, 이 대상이 메모리상의 어떤 위치에 바인딩되는지 '컴파일타임'에 '정적으로' 바인딩되어 고정된다는 의미입니다. (혹은 링크타임에 되기도 합니다만, 여기서의 논의에서는 섞어서 사용하기로 하죠) -static이란 메모리를 고정시킨다는 의미의 예약어입니다. 이건 변수나 객체에 해당되는 얘기고 함수에 static이면 static인 변수나 객체만 접근을 한다는 얘기구요-

그래서, static으로 선언된 대상이 무엇이든 그것이 메모리의 어느 부분에 대응되는지는 컴파일타임에 결정되어 고졍될 수 있어야 합니다.

1) local static 변수 : 일반적으로 로컬변수는 스택 영역에 생성되며, 해당 함수가 호출되는 시점에 따라 로컬 변수가 바인딩되는 주소가 달라지게 됩니다. 이는 static의 정의에 위배되는 일이죠. 따라서 로컬변수를 static으로 선언하면 컴파일 과정에서 전역 데이터 영역에 변수를 생성하고 그 위치에 변수의 주소를 바인딩합니다. 전역데이터영역은 프로그램 수행동안 침범되지 않는 영역이므로 안전하게 정적으로 바인딩된 주소를 유지할 수 있습니다.

2) global static 변수 : local변수는 static으로 선언될 때 전역 데이터 영역에 생성된다고 했습니다. 전역 데이터 영역은 다른 곳이 아니라, 바로 전역변수가 자리잡는 영역이죠. 그런데 전역변수를 static으로 선언했을경우, 전역변수는 이미 전역 데이터 영역에 자리잡는 변수이므로 위치는 그전과 같습니다. 하지만 전역static변수가 그냥 전역 변수와 다른 점은 반드시 '컴파일타임'에 바인딩될 수 있어야 한다는 것입니다.
전역변수의 경우, extern키워드로 다른 모듈의 전역변수를 링크해서 사용할 수 있습니다. 여기서 중요한 것은 extern키워드로 선언한 전역변수는 링크 타임에 메모리의 위치가 바인딩되어 결정된다는 것이지요. static 개체는 위에서 말했듯이, '컴파일타임'에 메모리 바인딩이 결정되어야 합니다.
따라서 static으로 선언한 전역변수는 다른 모듈에서 extern으로 링크해서 사용할 수 없습니다.

3) static member 변수 : 클래스의 일반적인 멤버변수는 this포인터를 기반으로 오프셋이 얼마.. 라는 식으로 멤버변수의 주소를 계산하게 됩니다. 즉, 이 말은 어떤 변수를 참조할때, 메모리상의 어떤 주소에서 값을 가져와야 하는지는 실제 객체가 생성된 후인 런타임에서야 알 수 있다는 것이죠. 따라서 클래스의 멤버로 선언된 스태틱 변수는 컴파일타임에 로컬 스태틱 변수와 마찬가지로 전역 데이터 영역에 바인딩되어 사용됩니다. 그 결과로 해당 클래스의 모든 객체에서 동일한 변수를 사용하게 되는 것이지요.
사실, 스태틱 멤버 변수의 메모리 바인딩은 정확히는 링크 타임에 결정됩니다만.. 클래스 멤버의 경우에는 this를 기반으로 한 메모리 위치의 계산이 이루어지지 않는 다는 것에 초점이 맞춰진 언어설계라고 하겠습니다.

4) static member 함수 : 이 역시 멤버변수의 논의와 동일합니다. static 멤버함수는 그 함수 내에서 직접 참조하는 요소가 컴파일타임에 결정될 수 있을 것을 요구합니다. 즉, this포인터에 의해 위치가 변하는 멤버변수의 참조가 금지됩니다. 따라서 스태틱 멤버함수에서는 this포인터의 사용이 금지되지요. 그렇게 함으로써 static멤버만 접근할 수 있는 것이죠. 일반 멤버는 객체가 생성된 후인 런타임에만 그 위치를 결정할 수 있기 때문에 접근을 제한하도록 하는 메커니즘입니다.
또한, virtual 멤버함수는 어떤 함수가 실행될지는 런타임에 동적으로 바인딩된다는 것을 알려주는 키워드입니다. 따라서 당연히 정의로부터 static과 정면으로 대치되는 개념이지요. 결과적으로 한 멤버함수가 static이면서 동시에 virtual일수는 없습니다.
그리고 함수 뒤쪽에 static을 붙이는 선언 구문은 없습니다. 아마도 const지정자를 함수 뒤쪽에 붙이는 것에서 오해를 하신듯 한데, const지정자를 함수 앞에 붙일 경우 리턴 타입이 const인 것을 지정하는 구문과 구분할 수 없게 되므로 궁여지책으로 const지정자는 함수이름 뒤쪽으로 가게 된 것이죠.
하지만 static지정자는 함수 앞에 붙어도 다른 것과 혼동될 여지가 없으므로 앞에 붙게 되는 것입니다.
반응형

병렬처리 방법 #2. 컴파일러가 지원하는 OpenMP !

Posted on 2009. 4. 28. 18:34
Filed Under Programming/Basic

2008/03/24
우연히 ZDNet에서 본 동영상: 프로그램 내에 병렬처리를 가능케 하기 위해 어떻게 해야 하는가?

 
 ▲ 아저씨 왜 그렇게 잘아셩~ *.*?

컴파일러가 지원하는 OpenMP < 이런게 있었단다 헐;
MSDN 검색해보니 'parallel OpenMP directive' 라고 있었다. 역시..

[CODE]
#include <windows.h>
#include <time.h>
#include <stdio.h>
#include <omp.h>
#include <conio.h>

int main()
{
 DWORD t1 = GetTickCount();
 
 struct tm  when;
 __time64_t before, after, result;

 time( &before );


 #pragma omp parallel for (i, j, k)
 {
  for( int i = 0; i < 100; i++ ) //int i = omp_get_thread_num();
  {
   printf_s("[i] Hello from thread %d\n", i);
   for( int j = 100; j < 150; j++ )
   {
    printf_s("\t[j] Hello from thread %d\n", j);
    for( int k = 1000; k < 1100; k++ )
    {
     printf_s("\t\t[k] Hello from thread %d\n", k);
    }
   }
  }

 }

 time( &after );
 result = after - before;
 _localtime64_s( &when, &result );
 
 DWORD t2 = GetTickCount();
 printf_s( "%d sec", when.tm_sec );

 _getch();
}
[/CODE]

돌려봤다;
27초 : 28초
밀리초 단위로 재봐야 확실히 나올듯하다.
아무튼 오늘은 컴파일러가 저런것도 지원한다는 것의 발견.. >_</

동영상 설명중 병렬처리 가장 많이 쓰는 방법: threading building blocks 는 아래 사이트 참조!
http://threadingbuildingblocks.org/


반응형

[STL] 요약 : container, iterator, algorithm, 함수자(functor), adaptor, allocator

Posted on 2009. 4. 28. 18:28
Filed Under Programming/Basic


컨테이너(container)


C 언어의 배열처럼 동일한 요소들을 모아놓은 집합을 뜻합니다. 구조체는 각기 다른 자료형을 내부적으로 유지하지만, 컨테이너는 반드시 동일한 요소로 구성되어 있어야 합니다.


컨테이너에는 순차 컨테이너(sequence container)와 정렬 연관 컨테이너(sorted associative container)가 있습니다. STL에 들어 있는 모든 컨테이너는 고정된 크기를 갖는 것이 아니라 동적으로 크기를 언제든지  변경할 수 있습니다.



*  순차 컨테이너(sequence container)

순차 컨테이너는  동일한 객체가 선형으로 구성된 집합(collection)으로, 실행시에 동적으로 크기를 변경할 수 있습니다. 순차 컨테이너에는 다음과 같은 컨테이너가 포함되어 있습니다.


순차 컨테이너 종류 (컨테이너 / 호칭 / 설명)

vector / 벡터 /

1. 구성 요소에 임의로 접근할 수 있습니다. (모든 요소에 접근할 때의 소요 시간이 동일합니다.)

2. 집합 끝에서 발생하는 삽입과 삭제는 신속히 처리하지만, 처음과 중간에 대해서는 해당 위치 다음에 있는 요소들의 개수에 비례해서 처리됩니다.


deque / 덱 /

1. 구성 요소에  임의로 접근할 수 있습니다. (모든 요소에 접근할 때의 소요  시간이 동일합니다.)

2. 집합의 양 끝(시작과 끝)에서 발생하는 삽입과 삭제를 신속히 처리하지만, 중간에  대해서는 해당 위치 다음에 양쪽에 있는 요소들의 개수에 비례해서 처리됩니다.


list  / 리스트 /

1. 구성 요소에 선형적으로 접근합니다. (요소의 개수에 따라서 접근 소요 시간이 늘어나고, 요소 위치에 따라서 소요 시간이 다릅니다.)

2. 집합 내의 어디에서나 구성 요소의 삽입과 삭제가 동일한 시간으로 신속히 처리됩니다.



* 정렬 연관 컨테이너(sorted associative container)

정렬 연관 컨테이너는 키(key)를 사용해서 데이터를 신속하게 찾아낼 수 있는 집합으로, 순차 컨테이너와 마찬가지로 실행 시에 동적으로 크기를 변경할 수 있습니다. 정렬 연관 컨테이너에는 다음과 같은 컨테이너가 포함되어 있습니다.


정렬 연관 컨테이너 종류 (컨테이너 / 호칭 / 설명)

set / 셋 /

1. 집합에는 키만 저장할 수 있습니다.

2. 중복되지 않는 키를 지원합니다. 따라서, 동일한 키가 두 개 존재할 수 없습니다.

3. 원하는 키를 신속하게 찾아냅니다.


multiset  / 멀티셋 /

1. 집합에는 키만 저장할 수 있습니다.

2. 중복 키를 지원합니다.  따라서, 여러 개의 복사본을 가질 수 있습니다.

3. 원하는 키를 신속하게 찾아냅니다.


map / 맵 /

1. 집합에는 키와 데이터를 함께 저장합니다.

2. 중복되지 않는 키를 지원합니다. 따라서, 동일한 키가 두 개 존재할 수 없습니다.

3. 키를 사용해서 원하는 객체를 신속하게 찾아줍니다.


multimap / 멀티맵 /

1. 집합에는 키와 데이터를 함께 저장합니다.

2. 중복  키를 지원합니다.  따라서, 여러 개의 복사본을 가질 수 있습니다.

3. 키를 사용해서 원하는 객체를 신속하게 찾아줍니다.




반복자(iterator)



STL의 모든 연산은 컨테이너에 대해서 적용될 수 있도록 고안되었습니다. 그래서, 컨테이너의 요소 하나, 일정 범위의 구간, 전체에 대해서 연산을 적용할 수 있도록 세분화되어 있습니다. 반복자는 컨테이너로부터 값을 읽어 올 수 있는 방법을 제공해 줍니다. 벡터와 같은 컨테이너는 []연산자를 통해서도 값을 읽어 올 수 있지만, 모든 컨테이너에 대해서 사용할 수 있는 방법은 아닙니다. 따라서, 우리는 모든 컨테이너에 대해서 동일하게 동작할 수 있도록 값을 읽어 오는 방법을 익혀야 합니다.


"반복자는 포인터이다"라고 생각하면 좋습니다. 포인터를 제대로 다룰 줄 알면 더 바랄 것이 없습니다. 그러나, 실제로는 포인터가 반복자의 일부입니다. 단지, C++라는 언어가 포인터라는 개념을 이미 갖고 있었고, 기존의 프로그래머들이 모두 능숙하게 포인터를 다룰 수 있기 때문에, C++ 표준 위원회는 STL의 개념이 포인터가 갖고 있는 몇 가지 특징들을 포함하도록 했습니다.


STL은 이미 알려진 자료 구조를 템플릿을 사용해서 구현한 코드입니다. 누구나 리스트와 맵이라는 자료 구조를 구현할 줄 알고 있습니다. 그러나, 실제로 구현했을 경우 코드 또한 완전히 같을 수 있을까요? 그렇지는 않을 겁니다. 성능을 향상시킨다고 나름대로 많은 기교를 부릴 게 틀림없습니다. 그래서, 우리는 벡터가 일반 배열처럼 동작한다는 것은 알지만, 어떻게 구현했는지는 알 수 없습니다. "이렇게 구현했겠지"하고 추측하는 것만큼 위험한 것은 없습니다. 컴파일러를 제작하는 회사마다 다릅니다.


이렇게 각기 다르게 구현된 컨테이너에 대해서 제대로 동작하려면, 반복자에 대해서도 몇 가지 제한이 필요합니다. 가령, "벡터 컨테이너의 반복자는 어떠어떠한 기능을 지원해야 한다"입니다. 셋의 경우도 순차적으로 진행되는 컨테이너가 아니기 때문에, 벡터에서 지원하는 [] 연산자를 지원하지 않습니다. 지원할 수도 있지만, 일반적으로 셋을 표현하는 자료 구조의 환경 때문에, [] 연산자를 적용했을 경우 효율이 떨어질 뿐더러 별로 의미가 없습니다. 모든 컨테이너는 어떠한 기능의 반복자가 필요한지 STL 규정에 이미 정해 두었습니다.



반복자의 종류

반복자 / 설명

입력 : Input /

1. 두 개의 반복자, first와 last에 대해서  first != last 라는 식에 대해서 참을 반환해야 합니다.

2. ++first 연산을 수행하면, 반복자 first 다음 요소의 위치를 반환해야 합니다.

3. *first 연산을 수행하면, 반복자 first가 가리키는 값을 반환해야 합니다.

그러나, 다음 코드는 사용할 수 없습니다. 입력 반복자는 읽기 전용입니다.

*first = 5;

4. 컨테이너의 요소를 검색하는 find 알고리즘에서 사용됩니다.


출력 : output /

1. ++first 연산을 수행하면, 반복자 first 다음 요소의 위치를 반환해야 합니다.

2. *first 연산을 수행하면, 반복자 first가 가리키는 곳에 값을 저장합니다.

그러나, 다음 코드는 사용할 수 없습니다. 출력 반복자는 쓰기 전용입니다.

N = *first

3. 컨테이너의 요소를 복사하는 copy 알고리즘에서 사용됩니다.


순방향 : forward /

1. 입력 반복자와 출력 반복자의 모든 기능을 포함합니다.

2. 한 쪽 방향으로만 순회할 수 있습니다.

3. 반복자의 위치를 저장해 두었다가 저장한 위치부터 다시 순회할 수 있습니다.

4. 컨테이너의 요소를 읽고 쓰는 replace 알고리즘에 사용됩니다.


양방향 : bidirectional /

1. 순방향 반복자의 모든 기능을 포함한다.

2. 양쪽 방향으로의 순회가 가능합니다.

따라서, --first 연산을 수행하면, 이전 요소의 위치를 반환해야 합니다.

3. 컨테이너의 요소들을 뒤집을 수 있는 reverse 알고리즘에서 사용됩니다.


임의접근 : random access /

1. 양방향 반복자의 모든 기능을 포함합니다.

2. 컨테이너의 모든 요소에 접근 소요 시간이 동일합니다.

3. +, -, +=, -=, [] 연산자를 지원해야 합니다.

4. <, >, <=, >= 연산자를 지원해야 합니다.

5. 컨테이너의 요소를 이진 검색하는 binary_search 알고리즘에서 사용됩니다.


반복자에는 위의 표에서 나열한 것 외에, 편리를 위해서 제공되는 반복자가 몇 개 더 있습니다.


1. 삽입 반복자(insert iterator)

* 후위 삽입 반복자 : back_insert_iterator

컨테이너의 push_back 함수를 사용해서, 컨테이너의 마지막에 요소를 자동으로 삽입하는 반복자로, 축약해서 back_insertor로 쓸 수 있습니다.


* 전위 삽입 반복자 : front_insert_iterator

컨테이너의 push_front 함수를 사용해서, 컨테이너의 처음에 요소를 자동으로 삽입하는 반복자로, 축약해서 front_insertor로 쓸 수 있습니다.


* 삽입 반복자 : insert_iterator

컨테이너의 insert 함수를 사용해서, 컨테이너의 중간에 요소를 자동으로 추가해 주는 반복자로, 축약해서 insertor로 쓸 수 있습니다.


2. 스트림 반복자(stream iterator)

* 입력 스트림 반복자(istream_iterator)

입력 반복자의 한 종류로, 스트림으로부터 한 쪽 방향으로 읽어나갈 수 있습니다. 따라서, cin과 같은 표준 입력 스트림(키보드)으로부터 입력을 받을 수 있습니다.

istream_iterator<Type>()는 입력 스트림의 끝을 나타내는 반복자, 컨테이너의 멤버 함수인 end 함수와 같은 역할을 합니다.


* 출력 스트림 반복자(ostream_iterator)

출력 반복자의 한 종류로, 스트림으로부터 한 쪽 방향으로 출력해 나갈 수 있습니다. 따라서, cou과 같은 표준 출력 스트림(모니터)으로 출력을 할 수 있습니다.


3. 참조 반복자(reference iterator)

* 수정 반복자(mutable iterator)

반복자를 사용해서 값을 변경할 수 있는 반복자로, 일반 반복자인 vector<int>::iterator등이 여기에 해당됩니다.


* 상수 반복자(const_iterator)

반복자를 사용해서 값을 변경할 수 없는 반복자로, vector<int>::const_iterator로 생성할 수 있습니다. 상수 반복자 it에 대해서 다음 코드는 에러입니다.


set<int> set1;

set1.insert(3);


set<int>::iterator it = set1.begin();

*it = 5; // 에러. it는 상수 반복자.


위의 코드는 아래와 같이 변경되어야 합니다. 셋을 구현할 자료 구조의 특성 때문에. 셋은 항상 상수 반복자만을 제공합니다.


set1.erase(it);

set1.insert(5);



알고리즘(algorithm)

컨테이너에 대해서 적용할 수 있는 기능들을 체계적으로 정리해 놓은 함수들을 말합니다. 알고리즘에 속한 함수는 모두 일반적(generic)이기 때문에, 어떤 하나의 컨테이너가 아니라 여러 컨테이너에 대해서 동작합니다. 여기서 모든 컨테이너가 아니라 여러 컨테이너인 것은 기능의 효율성 때문에, 적합하지 않은 컨테이너(자료구조)에 대해서는 지원하지 않기 때문입니다.


알고리즘에 포함된 함수는 모두 4개지로 분류할 수 있습니다.

* 변경 불가 순차 알고리즘(nonmutating sequence algorithm)

* 변경 가능 순차 알고리즘(mutating sequence algorithm)

* 정렬 관련 알고리즘(sorting-related  algorithm)

* 범용 수치 알고리즘(generalized numeric algorithm)


변경 불가 순차 알고리즘(nonmutating sequence algorithm) 종류

함수 / 설명

find / 원하는 요소를 검색합니다.

find_if / 조건자를 true가 되게 하는 첫 번째 요소를 검색합니다.

adjacent_find / 동일한 두 개의 원소가 이웃하고 있는 쌍을 검색합니다.

count / 특정 값과 일치하는 요소의 개수를 반환합니다.

count_if / 조건자를 true가 되게 하는 요소의 개수를 반환합니다.

for_each / 인자로 넘어간 함수를, 요소 전체에 대해서 호출합니다. for문과 같은 반복문을 대신 해서 사용할 수 있습니다.

equal / 두 개의 구간이 일치하면, true를 반환합니다.

mismatch / 두 개의 구간이 일치하지 않으면, true를 반환합니다.

search / 구간 내에 포함된 하위 구간을 검색합니다.  C 함수의 strstr 함수와 비슷한 역할을 합니다.


변경 가능 순차 알고리즘(mutating seqence algorithm)종류

함수 / 설명

copy / 특정 구간에 속하는 원소를 다른 구간으로 복사합니다. 구간이 중복될 경우, 왼쪽으로의 이동만 허용합니다.

copy_backward / 특정 구간에 속하는 원소를 다른 구간으로 복사합니다. 구간이 중복될 경우, 오른쪽으로의 이동만 허용합니다.

fill / 구간에 속하는 모든 원소를 주어진 값으로 채웁니다.

fill_n / 시작 위치에서부터 n 개의 요소를 주어진 값으로 채웁니다.

generate / 구간에 들어 있는 요소 각각에 대해 인자로 넘어간 함수를 호출하고, 그 결과로 다시 요소를 채웁니다. 구간 내의 요소는 함수의 결과값으로 채워집니다.

partition / 구간 내에서 주어진 값보다 작은 요소는 앞쪽에, 큰 요소는 뒤쪽에 오도록 재배치합니다.

stable_partition / 구간 내에서 주어진 값보다 작은 요소는 앞쪽에, 큰 요소는 뒤쪽에 오도록 재배치하면서, 최초의 상대적인 위치를 그대로 유지합니다.

random_shuffle / 구간 내의 요소를 난수로 채웁니다.

remove / 주어진 값과 일치하는 구간 내의 모든 요소를 삭제합니다. 그러나, 실제로 삭제되는 것이 아니고, 가장 뒤쪽으로 자리르 변경합니다. 실제 삭제는 erase 함수를 사용합니다.

replace / 주어진 값과 일치하는 구간 내의 요소들을 지정한 값으로 치환합니다.

reverse / 구간 내의 모든 요소들을 거꾸로 뒤집어서 재배치합니다.

rotate / 구간 내의 모든 요소를 주어진 위치를 중심으로 한 바퀴 돌립니다.

swap / 두 개의 값을 교환합니다.

swap_ranges / 두 구간에 속하는 값들을 교환합니다.

transfrom / 구간에 속하는 요소 각각에 대하여, 인자로 넘어간 함수를 호출한 결과를 다른 구간에 저장합니다.

unique  / 구간 내에 속한 모든 중복된 요소를 제거함으로써, 해당 요소가 유일하도록 만듭니다. remove 함수처럼, 실제로 삭제가 일어나는 것이 아니고, 구간의 맨 뒤로 이동시켜 놓기 때문에, 컨테이너의 크기에는 변화가 없습니다.


정렬 관련 알고리즘(sorting-related algorithm) 종류

함수 / 설명

sort / 구간을 정렬합니다. (quick-sort)

stable_sort / 구간을 정렬합니다. 동등한 요소의 상대적인 위치를 유지합니다. (merge-sort)

partial_sort / 구간을 정렬합니다. 동등한 요소의 상대적인 위치를 유지하지 않습니다. (heap-sort)

nth_element / 구간이 정렬되었을 경우, N번째 위치에 놓이게 될 요소만 놓고, 나머지 요소들은 N보다 작은 값들을 왼쪽에, 큰 값들을 오른쪽에 배치한다

binary_search / 정렬되어 있는  구간으로부터 요소를 검색합니다. (이진 검색)

lower_bound / 이진 검색을 사용해서 정렬된  구간을 검색한 후, 동일한 값들이 연속되어 있을 경우 맨 앞의 요소를 반환합니다.

upper_bound / 이진 검색을 사용해서 정렬된 구간을 검색한 후, 동일한 값들이 연속되어 있을 경우 맨 마지막의 요소를 반환합니다.

equal_range / 이진 검색으로 검색한 값이 동일하게 연속된 구간을 반환합니다. lower_bound와 upper_bound 함수의 결과를 합한 결과가 반환됩니다.

merge / 두 개의 정렬된 구간을 병합해서, 중복되지 않는 구간에 결과를 저장합니다.

inplace_merge 두 개의 정렬된 구간을 병합해서, 중복되지 않는 구간에 결과를 저장합니다.

includes / 주어진 정렬된 구간의 요소들이 다른 구간에 속하는지를 반환합니다.

set_union / 정렬된 두 개 구간의 합집합을 다른 구간에 저장합니다.

set_difference / 정렬된 첫 번째 구간에 속한 요소들 중에서 정렬된 두 번째 구간에 속하지 않는 요소들을 다른 구간에 저장합니다.

set_intersection / 정렬된 두 개의  구간에 공통으로 속한 요소들을 다른 구간에 저장합니다.

set_symmetric_defference / 정렬된 두 개의 구간에서, 한 쪽 구간에만 속해있는 요소들을 다른 구간에 저장합니다.

make_heep / 구간 내의 요소들을  힙(heep)으로 재구축 합니다.

sort_heep / 힙에 저장된 원소들을 정렬합니다.

push_heap / 주어진 구간을 힙으로 유지하면서, 새로운 요소를 힙에 추가합니다.

pop_heap / 주어진 구간을 힙으로 유지하면서, 가장  큰 요소를 삭제합니다.

min / 두 개의 요소 중, 작은 값을 반환합니다.

max / 두 개의 요소 중, 큰 값을 반환합니다.

lexicographical_compare / 두 개의 구간을 사전 순서로 비교해서 결과를 반환합니다.

next_permutation / 주어진 구간을 사전 순서에 기초해서, 다음 순열로 변경합니다.

prev_permutation / 주어진 구간을 사전 순서에 기초해서, 이전 순열로 변경합니다.


범용 수치 알고리즘(generalized numeric algorithm) 종류

함수 / 설명

accumulate / 주어진 구간에 속하는 요소들의 합계를 구합니다.

partial_sum / 주어진 구간의 요소를 기초로, 부분합을 구합니다.

adjacent_difference / 주어진 구간의 요소를 기초로, 이웃하는 요소의 차이를 구합니다.

inner_product / 주어진 두 구간의 내적(inner product)을 구합니다.



함수자(functor)


함수 객체(function object)라고도 부르며, 0개 이상의 인자를 받아서 알고리즘의 기본 동작을 변형하거나 확장시켜 주는 객체를 얘기합니다. 따라서, 우리가 지금까지 사용해 오던 함수도 함수자에 속합니다. 함수 포인터를 인자로 받아서, 알고리즘 내부에서 해당 함수를 사용할 경우, 알고리즘이 확장될 수 있기 때문입니다.


그러나, STL에서는 함수자로서 함수를 많이 사용하지 않습니다. 함수 포인터를 인자로 사용했을 경우의 단점은, 반환값과 인자가 동일한 함수에 대해서만 동작하기 때문입니다. 그래서, 범용성이 떨어지게 됩니다. 또한, 함수 포인터를 사용하기 때문에, 매번 함수를 호출할 때마다 포인터를 통해서 접근해야 할 뿐더러 해당 함수를 호출할 때마다 메모리 할당과 해제 또한 책임져야 합니다.

그래서, 성능면에서 많은 문제가 발생합니다.


함수 포인터를 사용했을 경우 발생하게 되는 위의 문제점들을 해결하기 위해, STL은 클래스를 인자로 사용합니다. 함수 대신 사용하는 클래스는 반드시 () 연산자를 재정의해야 합니다. 이렇게 함수를 직접 넘기지 않고 클래스를 통해서 구현된 함수 객체를 넘길 때의 장점은, 지정된 클래스로 형변환될 수 있는 모든 클래스를 인자로 사용할 수 있기 때문에 범용성이 확보된다는 것입니다. 여기서 지정된 클래스로의 변환은, 인자로 사용가능한 클래스를 상속받음으로써 해결할 수 있습니다.

두 번째로 성능과 관련된 문제는, 클래스 내부에서 구현된 () 연산자 오버로딩 함수는, 코드가 짧기 때문에 충분히 인라인(inline) 함수로 구현이 가능하기 때문에, 함수 호출과 관련된 부담에서 벗어날 수 있습니다. 더불어 추가적으로 생기는 장점은, 함수에서 누적되는 등의 용도로 사용하는 어떤 정보가 필요하다면, 함수 포인터의 경우 전역 변수를 사용해야 하지만 클래스는 멤버 변수로 쉽게 관리할 수 있습니다.


함수자의 종류로는 말 그대로 함수자와 함수 어댑터(adaptor)가 있습니다. 함수자는 함수 포인터나 클래스를 사용해서 함수의 기능을 변경하는 것을 말하고, 함수 어댑터는 함수자의 기능을 변형해서 새로운 함수자를 생성합니다.


다음은 STL이 제공하는 연산자를 구현한 클래스 함수자들로, 산술 연산, 비교 연산, 논리 연산에 대한 함수자를 제공합니다.


산술 연산 함수자 종류

연산자 / 이름 / 설명

덧셈(+) / plus / x와 y의 합을 반환합니다. (x + y, 이항 함수자)

뺄셈(-) / minus / x와 y의 차를 반환합니다. (x - y, 이항 함수자)

곱셈(*) / multiplies / x와 y의 곱을 반환합니다. (x * y, 이항 함수자)

나눗셈(/) / divides / x와 y의 몫을 반환합니다. (x / y, 이항 함수자)

나머지(%) / modulus / x와 y의 나머지를 반환합니다. (x % y, 이항 함수자)

부정(-) / negate / x의 부호를 변경한 값을 반환합니다. (-x, 단항 함수자)


비교 연산 함수자 종류

연산자 / 이름 / 설명

같다(==) / equal_to / x가 y와 같은지 비교합니다. (x == y, 이항 함수자)

같지 않다(!=) / not_equal_to / x가 y와 같지 않은지 비교합니다. (x != y, 이항 함수자)

크다(>) / greater / x가 y보다 큰지 비교합니다. (x > y, 이항 함수자)

작다(<) / less / x가 y보다 작은지 비교합니다. (x >= y, 이항 함수자)

작거나 같다 (<=) / less_equal / x가 y보다 작거나 같은지 비교합니다. (x <= y, 이항 함수자)


논리 연산 함수자 종류

연산자 / 이름 / 설명

And(&&) / logical_and / x와 y를 AND 연산합니다. (x && y, 이항 함수자)

OR(||) / logical_or / x와 y를 OR 연산합니다. (x || y, 이항 함수자)

NOT(!) / logical_not / x를 NOT 연산합니다. (!x, 단 항 함수자)



함수 어댑터(function adaptor)

* 바인터(binder)

두개의 인자를 필요로 하는 이항 함수 객체의 인자 하나를 고정시켜서, 인자를 하나만 요구하는 단항 함수 객체로 변환시켜 줍니다.

* 부정자(negator)

함수 인자로 사용되는 조건자의 의미를 반대로 변환시켜 줍니다.

* 함수 포인터 어댑터(adaptors for pointers to functions)

단항이나 이항 함수 포인터를 라이브러리가 제공하는 함수 어댑터와 사용할 수 있도록 도와줍니다.

* 멤버 함수 포인터 어댑터(adaptors for pointers to member functions)

단항이나 이항 멤버 함수 포인터를 라이브러리가 제공하는 함수 어댑터와 사용할 수 있도록 도와줍니다.


바인더(binder) 어댑터

* bind1st

함수자에 전달되는 두 개의 인자 중, 첫 번째 인자를 고정값으로 사용할 수 있도록 도와주는 바인더

* bind2nd

함수자에 전달되는 두 개의 인자 중, 두 번째 인자를 고정값으로 사용할 수 있도록 도와주는 바인더


부정자(negator) 어댑터

* not1 / 인자로 전달된 단항 조건자의 의미를 반대로 변환합니다.

* not2 / 인자로 전달된 이항 조건자의 의미를 반대로 변환합니다.


함수 포인터 어댑터(adaptors for pointers to functions)

* ptr_fun / 단항 함수를 함수 어댑터처럼 사용할 수 있도록 지원합니다.

* ptr_fun / 이항 함수를 함수 어댑터처럼 사용할 수 있도록 지원합니다.


멤버 함수 포인터 어댑터(adaptors for pointers to member functions)

* mem_fun / 인자가 없는 멤버 함수를 함수 어댑터처럼 사용할 수 있도록 지원합니다.

* mem_fun / 단항 멤버 함수를 함수 어댑터처럼 사용할 수 있도록 지원합니다.

* mem_fun_ref / 인자가 없는 멤버 함수를 함수 어댑터처럼 사용할 수 있도록 지원합니다. 인자가 없는 멤버 함수 어댑터인 mem_fun의 레퍼런스(&) 버전입니다.

* mem_fun_ref / 단항 멤버 함수를 함수 어댑터처럼 사용할 수 있도록 지원합니다. 단항 멤버 함수 어댑터인 mem_fun의 레퍼런스(&) 버전입니다.

* mem_fun / 인자가 없는 const 멤버 함수를 함수 어댑터처럼 사용할 수 있도록 지원합니다. 인자가 없는 멤버 함수 어댑터인 mem_fun의 상수(const) 버전입니다.

* mem_fun / 인자가 없는 const 멤버 함수를 함수 어댑터처럼 사용할 수 있도록 지원합니다. 단항 멤버 함수 어댑터인 mem_fun의 상수(const) 버전입니다.

* mem_fun_ref / 인자가 없는 const 멤버 함수를 함수 어댑터처럼 사용할 수 있도록 지원합니다. 인자가 없는 멤버 함수 어댑터인 mem_fun_ref의 상수(const) 버전입니다.

* mem_fun_ref / 단항 const 멤버 함수를 함수 어댑터처럼 사용할 수 있도록 지원합니다. 단항 멤버 함수 어댑터인 mem_fun의 상수(const) 버전입니다.





어댑터(adaptor)


어댑터는 컨테이너의 인터페이스 또는 함수자의 기능을 변경하는 컴포넌트를 말합니다. 가령, 기존의 컨테이너를 사용해서 스택을 꾸미고 싶을 때는 스택 어댑터(stack adaptor)를, 함수자의 의미를 변경할 때는 바인더(binder)나 부정자(negator) 등을 사용합니다.


어댑터의 종류

1. 컨테이너 어댑터(container adaptor)

* 스택 어댑터(stack adaptor)

- 스택(stakc) 자료구조를 표현하는 어댑터를, 스택 인터페이스로 변환합니다.

- 스택 : 한 쪽 끝에서만 삽입과 삭제가 일어나고, 먼저 집어넣은 것을 마지막에 꺼냅니다. (FILO : file-in, last-out)

*큐 어댑터(queue adaptor)

- 큐(queue) 자료구조를 표현하는 어댑터로, 큐 인터페이스로 변환합니다.

= 큐 : 한 쪽 끝에서 삽입이, 반대쪽 끝에서 삭제가 일어나고, 먼저 집어 넣은 것을 먼저 꺼냅니다(first-in, first-out)

* 우선 순위 큐 어댑터(priority queue adaptor)

- 우선 순위 큐(priority queue) 자료구조를 표현하는 어댑터로, 우선 순위 큐 인터페이스로 변환합니다.

- 우선 순위 큐 : 한 쪽 끝에서 삽입이, 반대쪽 끝에서 삭제가 일어나고, 항상 가장 큰 값을 먼저 꺼냅니다.


2. 함수 어댑터(function adaptor)

* 바인더(binder)

- 두 개의 인자를 필요로 하는 이항 함수 객체의 인자 하나를 고정시켜서, 인자를 하나만 요구하는 단항 함수 객체로 변환시켜 줍니다.

* 부정자(negator)

- 함수 인자로 사용되는 조건자의 의미를 반대로 변환시켜 줍니다.

* 함수 포인터 어댑터(adaptor for pointers to functions)

- 단항이나 이항 함수 포인터를 라이브러리가 제공하는 함수 어댑터와 사용할 수 있도록 도와줍니다.

* 멤버 함수 포인터 어댑터(adaptors for pointers to member functions)

- 단항이나 이항 멤버 함수 포인터를 라이브러리가 제공하는 함수 어댑터와 사용할 수 있도록 도와줍니다.


3. 반복자 어댑터(iterator adaptor)

* 역 반복자 어댑터(reverse iterator adaptor)

- 반복자의 인터페이스를 변경합니다.

- 역 반복자 어댑터 하나 밖에 없습니다.

- reverse_iterator 클래스의 base 함수로 구할 수 있습니다.

- 각 컨테이너마다 역 반복자를 제공하기 때문에, 굳이 반복자 어댑터를 사용할 필요는 없습니다.




할당자(allocator)


메모리를 어떻게 할당해야 할지를 결정합니다. 가령, 자신이 만든 컨테이너가 일반적으로 사용하는 malloc이나 VirtualAlloc 함수 또는 new 연산자를 사용해서 동적 메모리를 할당할 수 없을 경우에 사용합니다.


기본 할당자(allocator)

1. 헤더 파일

#include <memory>

2. 선언

template <typename Type> class allocator;

3. 기본 할당자로 지정된 allocator 클래스의 경우, 할당자를 지정하지 않은 모든 컨테이너에 대해 공통적으로 사용됩니다. allocator 클래스는 메모리 할당에 new와 delete 연산자를 사용합니다.

4. 메모리 할당에 많은 시간이 소요되서는 안 되기 때문에, 최소한의 상수 시간내에 할당이 이루어 집니다.


사용자 정의 할당자(custom allocator)

1. 기본 할당자가 갖고 있는 기능에 새로운 기능을 추가하기 위해 사용합니다.

2. 가령, 디버거의 용도로 사용할 수 있는 할당자가 있다면, 코드를 디버깅하기 위한 용도로 STL과 결합할 수 있습니다. 디버거 할당자는 동적으로 할당된 모든 메모리를 보여주고, 누수를 탐지하는 기능을 가질 것입니다.

3. 그러나, 기본 할당자만으로 메모리 할당은 충분하기 때문에, 별도의 할당자를 만들거나 하지 않습니다.

4. 새로운 할당자가 debug_allocator 클래스에 구현되어 있을 경우, 아래 코드처럼 벡터 컨테이너를 비롯한 모든 컨테이너에 대해 적용할 수 있습니다.

vector< int, debug_allocator<int> >v;

출처: 누군가의 네이버 블로그~

반응형

[매크로] 간단하면서 자주 사용되는 비트연산 매크로 정의

Posted on 2009. 1. 14. 14:02
Filed Under Programming/Basic

 // clear bits
 #define CLR_BITS( P, B ) ( P  &= ~(B) )

 // set bits  
 #define SET_BITS( P, B ) ( P  |= (B) )

 // check bits
 #define CHECK_BITS( P, B ) ( P & B )

반응형

[매크로] 전처리기 명령어 모음(#include, #define, #if, #error, #line, #pragma, ...)

Posted on 2009. 1. 14. 10:58
Filed Under Programming/Basic

컴파일러는 사용자가 작성한 코드를 컴파일하기에 앞서 전처리문에서 정의해 놓은 문장들을 먼저 처리한다.

종류로는 #include, #define, #if, #error, #line, #pragma 등이 있다.
이것은 방대한 소스 코드를 지우지 않고 활성화와 비활성화하는 데에 가장 많이 이용된다.
즉, 기존에 있는 소스 코드를 건드리지 않고 부분적인 컴파일을 하는 것이다.

어떤 C 컴파일러는 전처리문의 첫 문자 #이 항상 그 라인의 첫 문자이어야 한다.

ANSI 표준에 따른 C의 전처리문의 종류
- 파일 처리를 위한 전처리문 : #include
- 형태 정의를 위한 전처리문 : #define, #undef
- 조건 처리를 위한 전처리문 : #if, #elif, #ifdef, #elif defined(), #ifndef, #else, #endif
- 에러 처리를 위한 전처리문 : #error
- 디버깅을 위한 전처리문 : #line
- 컴파일 옵션 처리를 위한 전처리문 : #pragma

조건 처리를 위한 전처리문은 어떤 조건에 대해 검사를 하고 그 결과를 참(0이 아닌 값) 또는 거짓(0)으로 돌려준다.
#if : ...이 참이라면
#ifdef : ...이 정의되어 있다면
#else : #if나 #ifdef에 대응된다.
#elif : else + if의 의미
#elif defined() : else + ifdef의 의미
#endif : #if, #ifdef, #ifndef이 끝났음을 알린다.

#include
헤더 파일과 같은 외부 파일을 읽어서 포함시키고자 할 때 사용된다. 이때의 파일은 이진 파일(Binary file)이 아닌 C의 소스 파일과 같은 형태의 일반 문서 파일을 말한다:
#include <stdio.h>        /* 이 위치에 stdio.h라는 파일을 포함시킨다. */
#include "text.h"           /* 이 위치에 text.h라는 파일을 포함시킨다. */

<...>을 사용할 때와 ...을 사용할 때의 차이점은 <...>은 컴파일러의 표준 포함 파일 디렉토리(또는 사용자가 별도로 지정해 준)에서 파일을 찾는 것을 기본으로 한다. 그리고 ...을 사용했을 때는 현재의 디렉토리를 기본으로 파일을 찾게 된다. 아예 디렉토리를 같이 지정할 수도 있다:
#include <C:\MYDIR\MYHEAD.H>
#include "C:\MYDIR\MYHEAD.H"

#define
상수 값을 지정하기 위한 예약어로 구문의 상수로 치환한다. 또한 #define은 함수 역활과 비슷하게 아래와 같이 쓰일 수 있다:
#define SUM(x) ((x) = (x) + (x))

#define으로 정의할 수 있는 것은 숫자만이 아니다:
#define MYNAME "Young Hee"

이렇게 #define으로 정의된 것은 일반적인 변수와는 다르다. 그 차이는 명백하다:
#define MYNAME "Turbo"
char my_name[] = "Turbo"

MYNAME은 전처리문으로 my_name은 문자형 배열 변수로 정의되었다:
printf(MYNAME);
printf(MYNAME);
printf(my_name);
printf(my_name);

이것을 전처리한 상태는 다음과 같이 될 것이다:
printf("Turbo");
printf("Turbo");
printf(my_name);
printf(my_name);

이런 결과에서 우리가 유추해 볼 수 있는 것은 전처리 명령을 사용했을 경우 "Turbo"라는 동일한 동작에 대해서 두개의 똑같은 문자열이 사용됐고, 변수를 사용했을 경우에는 하나의 문자열을 가지고 두번을 사용하고 있다는 것이다. 결과적으로 이런 경우에는 전처리문을 사용했을 경우 메모리 낭비를 가져 온다는 것을 알 수 있다.

#undef
#define으로 이미 정의된 매크로를 무효화한다:
#define ADD(a, b) (a + b)
#undef ADD(a, b)

앞으로 사용되는 ADD(...)는 undefined symbol이 되어 에러 처리된다.

#if ~ #endif
#if 구문은 if랑 아주 비슷하다. 이것은 어떠한 구문을 컴파일 할지 안할지를 지정할 수 있다:
#define A 1

#if A
source code ...
#endif

위 source code 부분은 컴파일이 된다. if문에서와 같이 참, 거짓을 구분하여 컴파일이 된다. 위에서 A값은 1 즉 0보다 큰 수이기 때문에 참인 것이다. 직접 아래와 같이 하면 거짓이기 때문에 source code 부분은 컴파일이 되지 않는다:
#if 0
source code ...
#endif

#if A == 2
source code 2 ...
#elif A == 3
source code 3 ...
#else
source code 1 ...
#endif

#ifdef ~ #endif
컴파일 할 때
#define MYDEF                /* MYDEF는 값은 가지지 않았지만 어쨋든 정의는 되었다 */

#ifdef YOURDEF              /* 만약 YOURDEF가 정의되어 있다면... */
#define BASE 10             /* BASE == 10 */
#elif defined MYDEF                    /* 그외에 MYDEF가 정의되었다면... */
#define BASE 2               /* BASE == 2 */
#endif

BASE는 상수 2로 치환되어 전처리기가 컴파일러에게 넘겨준다.

#ifndef __헤더명_H__ ~ #endif
헤더 파일이 겹치는 것을 막기 위한 일종의 매크로이다. 예를 들어, 헤더 파일에 어떤 클래스의 인터페이스 선언을 넣었다고 하자. 이 클래스 인터페이스에서 다른 파일의 프로토타입이 필요해서 다른 A 파일을 include 하고 있는데 이 헤더 파일을 include 하는 파일에서 A라는 헤더 파일을 이미 include 하고 있다면 두번 define한 것이 된다. 그러면 SYNTEX 에러가 난다. 그래서 그런 것을 막는 방법의 하나로 #ifndef을 사용한다. 이전에 include되어 있으면 #endif 쪽으로 점프해 버려 결국 한번 선언되는 것이다:
#include  <stdio.h>    ------ (a)
#include  <stdio.h>    ------ (b)

이렇게 두번 썼다고 하자. 그런데 앞에서 이미 include 했는데 밑에 또 한다면 문제가 된다. 컴파일러가 검사해야할 코드량도 많아진다. 그래서 stdio.h에는
#ifndef __STDIO_H__
#define __STDIO_H__
...
#endif
가 선언되어 있다. 만약 __STDIO_H__가 선언되어 있지 않다면 선언한다는 뜻이다. 그 뒤 (b)에서는 이미 (a)쪽에서 __STDIO_H__ 을 선언한 상태이기 때문에 전처리기 쪽에서 무시해버린다. 그러므로 컴파일러는 (a)만 검사한다.

#defined
define이 여러 개 되어 있는지를 검사할 때 쓴다. 이것은 여러 개를 동시에 검사 할 수 있다:
#if defined A || defined B
#if (defined A) || (defined B)
#if defined(A) || defined(B)


#ifdef와 #if defined의 차이
#ifdef은 정의가 되어 있는지를 테스트 하기 때문에 한번에 여러 개를 사용할 수 없다:
#ifdef name

여러 개가 정의되어 있는지를 테스트하기 위해서 #if defined를 사용할 수 있다:
#if defined(MACRO1) || defined(MACRO2)

#if는 ||로 중첩해서 사용할 수 있다(형식이 #if expression이므로 C 표현이 올 수 있다):
#if (MACRO1) || (MACRO2)

#error
소스 라인에 직접 에러 메세지를 출력한다. 전처리기가 #error 문을 만나면 그 즉시 컴파일을 중단하고 다음과 같은 에러 메시지를 출력한다:
ERROR : XXXXX.c ########: Error directive: 내용
- XXXXX.c --> 현재 컴파일 중인 파일 명
- ####### --> 전처리기가 #error 문을 만난 시점에서의 행 번호(헤더 포함)

#ifdef __LARGE__
#error This program must be compiled in LARGE memory model!
#endif

이 내용은 만일 프로그램이 LARGE 모델이 아니라면 "#error" 뒤에 표시된 메세지를 출력하고 컴파일을 중지하게 된다.

#line
이 명령은 소스 코드의 행 번호를 지정하기 위한 것으로 주로 컴파일러에 의해 미리 정의된 __LINE__과 함께 사용된다.
__LINE__과 __FILE__을 각각 행 번호와 파일 명으로 변경한다:
#include <stdio.h>
#define DEBUG

void main(void)
{
        int count = 100;

        #line 100               /* 다음 줄번호를 100으로 설정한다 */
                                   /* <-- 이 줄의 번호가 100이다 */
        #ifdef DEBUG        /* <-- 이 줄의 번호가 101이다 */
        printf("line:%d, count = %d\n", __LINE__, count);
        #endif

        count = count * count - 56;
        #ifdef DEBUG
        printf("line:%d, count = %d\n", __LINE__, count);
        #endif
        count = count / 2 + 48;
        #ifdef DEBUG
        printf("line:%d, count = %d\n", __LINE__, count);
        #endif
}

#pragma
컴파일 옵션의 지정. 컴파일러 작성자에 의해서 정의된 다양한 명령을 컴파일러에게 제공하기 위해 사용되는 지시어이다. 컴파일러의 여러 가지 옵션을 명령행에서가 아닌 코드에서 직접 설정한다. #pragma는 함수의 바로 앞에 오며 그 함수에만 영향을 준다.
Turbo C는 9개의 #pragma 문(warn, inline, saveregs, exit, argsused, hdrfile, hdrstop, option, startup)을 지원하고 있다:

#pragma inline
컴파일할 때 어셈블러를 통해서 하도록 지시한다. 즉, 인라인 어셈블리 코드가 프로그램에 있음을 알려준다(명령행 상에서 '-B' 옵션).

#pragma saveregs
이 홉션은 휴즈 메모리 모델에 대해 컴파일된 함수에게 모든 레지스터를 저장하도록 한다.

#pragma warn
이 지시어는 Turbo C에게 경고 메시지 옵션을 무시하도록 한다.

#pragma warn -par
이는 매개 변수(parAMETER)가 사용되지 않았다고 경고(warnING)를 내지 못하도록 한다. 이와 반대되는 표현은
#pragma warn +par

경고의 내용 앞에 (+)를 사용하면 경고를 낼 수 있도록 설정하고 (-)를 사용하면 경고를 내지 못하도록 하는 것은 모든 경고에 대해 동일하다. 명령 행에서는 "-wxxx"로 경고를 설정하고 "-w-xxx"로 경고를 해제한다. 경고의 종류는 무척 많은데 자주 사용되는 것을 아래에 나타냈다. 모든 것을 알고 싶다면 컴파일러 User's Guide의 명령행 컴파일러 부분을 참고하기 바란다:
par : 전해진 파라미터가 사용되지 않음
rvl : void 형이 아닌 함수에서 리턴 값이 없음
aus : 변수에 값을 할당했으나 사용하지 않았음
voi : void 형 함수에서 리턴 값이 사용되었음
sig : 부호 비트(MSB)가 고려되지 않은 형 변환(type-castion)에서 부호 비트를 소실할 수 있음

Standard C pre-defined symbols

__FILE__ a string that holds the path/name of the compiled file
__LINE__ an integer that holds the number of the current line number
__DATE__ a string(Mmm dd yyyy) that holds the current system date
__TIME__ a string(hh:mm:ss) that holds the current system time
__STDC__ defined as the value '1' if the compiler conforms with the ANSI C standard
__cplusplus determines if your compiler is in C or C++ mode. Usually used in headers


#include <stdio.h>

void main(void)
{  
        printf("The path/name of this file is %s\n", __FILE__);  
        printf("The current line is %d\n", __LINE__);  
        printf("The current system date is %s\n", __DATE__);  
        printf("The current system time is %s\n", __TIME__);  

        #ifdef __STDC__  
        printf("The compiler conforms with the ANSI C standard\n");  
        #else  
        printf("The compiler doesn't conform with the ANSI C standard\n");  
        #endif  

        #ifdef __cplusplus  
        printf("The compiler is working with C++\n");  
        #else  
        printf("The compiler is working with C\n");  
        #endif 


프로그래머들 마다 코딩 스타일(암시적 약속)이 있다. 보통 매크로, const 변수는 대문자로 적는 것이 원칙이다. 매크로 함수와 일반 함수, 매크로 대상체(object-like macro)와 일반 변수를 구분하기 쉽게 해주는 것이기 때문이다.

#define STDIO_H_
왜 뒤에 _를 붙였을까? 이것도 하나의 암시적 약속이다. 컴파일러 제작 회사는 매크로를 정의할 때 사용자들과 이름이 충돌이 나지 않게 하기 위해서 대부분 _를 뒤어 덧붙인다. 또한 _를 하나 혹은 두 개 연속으로 시작하는 것은 컴파일러 내부에서 사용하는 매크로라는 성격이 강하다. 물론 강제적인 뜻은 없으며 단지 관습상 그렇다. 왜 이것이 관습이 되었나 하면 보통 매크로 변수 이름이나 함수 이름을 지을 때 뒤에 _를 붙이지 않기 때문이다. 그래서 함수 제작자들이 _를 단골로 붙였다.


출처: http://bischoff.tistory.com/91

반응형

중복 Call by Reference

Posted on 2008. 5. 30. 14:35
Filed Under Programming/Basic

참조형을 넘기고 넘기고 넘기면~?

void AAA(CInfo & info)
{
    BBB(info);
}

void BBB(CInfo & info)
{
    CCC(info);
}

void CCC(CInfo & info)
{
   

}

반응형

About

by 쑤기c

반응형