#include <iostream>

using namespace std;

 

//간단한 텍스트 뒤집기
void recive(char s[])
{
    for(int i=0, j=strlen(s)-1; i<j; ++i, --j){
        char t = s[i];
        s[i] = s[j];
        s[j] = t;
    }
}

 

//안전한 메모리 복사

//차라리 memcpy_s 사용할것 이건 안에 어셈이라 더 빠름
bool MyMemCpy(void *dest, void *src, size_t len)
{
     char *des8 = (char*)dest;
     char *src8 = (char*)src;

     /////////////////////////////////
     //dest ■■■■■               //1.dest 메모리 영역안에 src가 있거나
     //      src □□□□□
     //            dest ■■■■■   //2.src 메모리 영역안에 dest가 있으면 안됨
     /////////////////////////////////
 
     if((des8 <= src8    //1검사
                && src8 <= dest8+len)
     ||(src8 <= des8    //2검사
                && des8 <= src8+len))
     {
            return false;
      }

   

      while(len--){
            *des8++ = *src8++;
      }

      return true;
}

 

//////////////////////////////////////////////

int main()
{
      char s[5] = {'a','b','c','d', '\0'};
      char d[5];

      for(int i=0; i<strlen(s); ++i){
          cout << s[i] << ',';
      }

     cout << endl;
 

     recive(s);

     for(int i=0; i<strlen(s); ++i){
         cout << s[i] << ',';
     }
     cout << endl;
 
     if(MyMemCpy(&s[3], &s[0], _countof(d))){
         for(int i=0; i<strlen(d); ++i){
            cout << d[i] << ',';
         }
     }
     return 0;
}

 

 

 

 

 

'C/C++언어' 카테고리의 다른 글

printf의 흔하진 않지만 때론 굉장히 필요한 포맷들  (0) 2013.10.23
memcpy에 대한 고찰...  (0) 2012.11.27
[ VC11-C++11 ] range base for - 1  (0) 2012.09.24
코드 최적화 팁  (0) 2012.09.10
CAtlMap 사용법 정리  (0) 2012.08.25

출처 : http://vsts2010.net/712

 

VC10에서 선보였던C++11의 기능 중 강력하면서 사용하기 쉽고, 자주 사용한 기능이 아마 'auto'이지 않을까 생각합니다. 예전에 강연을 할 때 auto와 관련된 예제를 보여드리면 많은 분들이 아주 좋아하시더군요(좀 놀라기도 하시더군요^^). 어떤 분들은 딴 건 제쳐두고 이것 때문이라도 VC10을 사용해야겠다는 분들이 있었습니다.

이번 VC11에서도'auto'와 같은 강력한 기능이 있습니다. 바로 'range base for' 입니다. 이것을 사용하면 반복문을 아주 쉽고, 강력하게 사용할 수 있습니다.

VC 특화 기능인 for each와 비슷하기 때문에 기존에 for each를 사용하고 있다면 이제는 range base for로 쉽게 바꾸어서 사용하면 됩니다.

예제를 통해 일반적인 for , VC for each,range base for문의 차이를 예제를 통해서 보겠습니다.

< 예제. 1 >

#include <iostream>

int main()

{

int NumberList[5] = { 1, 2, 3, 4, 5 };

std::cout<< "일반적인 for "<< std::endl;

for( int i = 0; i < 5; ++i )

{

std::cout<< i << std::endl;

}

std::cout<< "VC++ 특화의 for each" << std::endl;

for each( int i in NumberList )

{

std::cout<< i << std::endl;

}

std::cout<< "range base for " <<std::endl;

for( auto i : NumberList )

{

std::cout<< i << std::endl;

}

return 0;

}

< 실행 결과 >


<예제.1>을 보면 일반적인 for 문은

for( int i = 0; i < 5; ++i )

와 같이 시작과 종료 조건, 증가 값 이렇게 3개의 조건에 의해서 반복 됩니다.

그러나 range base for문은 VC만의 반복문인 for each와 비슷하게 데이터셋 변수와 이 데이터셋 요소의 타입을 선언하면 됩니다.

for( auto i : NumberList )

기존의 for 문에 비해서 또는 for each 보다도 간편해졌고, for each는 표준이 아닌 VC만의 기능인 것에 비해서 range base for C++ 표준 기능입니다.

range base for 문의 문법은 아래와 같습니다.

for ( for-range-declaration : expression ) statement

range base for 덕분에 반복문의 사용이 쉬워졌고, for 문을 사용할 때 종료 조건이 잘못되어 메모리 침범을 하는 위험도 피할 수 있게 되었습니다.

'C/C++언어' 카테고리의 다른 글

memcpy에 대한 고찰...  (0) 2012.11.27
간단한 텍스트 뒤집기, 메모리 복사 함수  (0) 2012.10.19
코드 최적화 팁  (0) 2012.09.10
CAtlMap 사용법 정리  (0) 2012.08.25
MSXML 파싱  (0) 2012.07.03

출처 : http://blog.naver.com/chaewh83/140016180847

 

이걸 내가 쓰는 프로그래밍으로 풀면

 

1. 컴파일러 최적화 옵션에서 '속도 최적화' 대신 '코드 크기 최적화' 설정이 캐쉬 성능을 향상시켜 좀 더 빠른 코드를 얻게 함.

 

-> 네트워크 프로그래밍의 경우, 경우에 따라 덜 최적화된 코드가 필요하므로 volatile 를 써야 함.

 

2. 각 CPU(인텔의 SSE, SSE2 / AMD 3D Now)의 SIMD를 활용하는 함수를 만들것.

-> 서버는 현재로선 인텔 CPU니까.....

 

3. 가능한 나눗셈 연산을 최소화 할것.

예)

-> b = a / m;

    c = d / m;

이 있으면

 

-> m = 1/m;

    b = a * m;

    c = d * m;

... 쉬프트가 여러우면 곱셈으로 해버림...

 

4. switch문을 쓸때 연속된 수치를 사용할것, 그러면 VC가 테이블 형태의 분기를 만들어줌.

-> 즉 가능하면 enum {} 으로 쓰는게 좋다는 이야기임.

 

예) case 0:
     case 1:

     case 2: ... (역순으로도 상관없음)

 

잘못) case 2:

        case 4:

        case 3:

 

5. 펜티엄 pro 이상에서는 CMOVxx/FCMOVxx 같은 조건적 이동 명령이 가능.

간단한 if문보다는 ? 를 사용할것.

 

->A == 0 ? choice1 : choice 2;

 

다만 디버깅할때 어느쪽 분기로 떨어지는지는 추적하기 힘드므로

#if _DEBUG

 if()...

#else

 ?...

#endif

를 쓰는것도 나쁘지 않을듯.

 

6. 자주 사용하는 자료 구조는 32bit 배수로 정렬시켜서 사용할것,

컴퓨터의 캐쉬라인을 최적으로 쓸 수 있기때문에 성능이 크게 향상됨. 팬티엄4는 L1캐쉬가 64바이트고 L2가 128바이트

이런걸 감안해서 쓰는 기법을 padding 이라 함.

 

7. sin, cos, tan, exp, arcsin등 수학 함수는 가급적 쓰지말고,

각 수치 데이터를 테이블로 만들어서 간략한 함수로 만들어 쓸것

 

-> psp나 3ds때도 이렇게 했는데...

 

8. 부동소수점은 가급적 double보다 float를 사용할것.

double 의 나눗셈은 39사이클이 걸리지만, float는 19사이클임. (단 2의 제곱수로 나누면 8사이클정도만 걸림)

그리고 float형일때 반드시 뒤에 f를 붙일것.

float a = 1.0; 보다는

float a = 1.0f; 이쪽이 더 빠름

 

9. 순서에 상관이 없다면 후연산자 보다는 선연산자를 사용할것.

a++; 보다는 ++a; 를 쓸것.

 

->출처쪽에선 이유가 안나와 있지만, 이걸 어셈 코드상에서 보면

a++; => a = a + 1; 개념이고, ++a; => a+1; 개념임.

 

10. 가능한 const를 사용할것. 컴파일러가 좀더 최적화를 잘 해줄 가능성이 높아짐.

 

11. 메모리 관리 함수는 따로 사용하는것이 좋음. malloc 과 free는 느리기 때문에 가능한 메모리풀을 만들어사용할것.

 

'C/C++언어' 카테고리의 다른 글

간단한 텍스트 뒤집기, 메모리 복사 함수  (0) 2012.10.19
[ VC11-C++11 ] range base for - 1  (0) 2012.09.24
CAtlMap 사용법 정리  (0) 2012.08.25
MSXML 파싱  (0) 2012.07.03
프로그램 시간 측정  (0) 2012.03.21

[ 선 언 ]

#include <atlcoll.h>

// Key 값이 문자형 일 경우

CAtlMap<CString, CImageData*, CStringElementTraits<CString>> m_tImage;

// 위의 조건을 제외한 선언

CAtlMap<int, CImageData*> m_tImage;

[ 등록 ]

m_tImage.SetAt( Key , Value );

[ 삭제 ]

m_tImage.RemoveKey( Key );

m_tImage.RemoveAll();

[ 검색 ]

m_tImage.LookUp( Key, Value ); // Key 값을 넣으면, Value 를 준다. 리턴값은 bool

CAtlMap<Key, Value>::CPair* pPair = m_tImage.LookUp( Key );

[ 시작 위치 ]

POSITION pos = m_tImage.GetStartPosition(); // iterator 라고 생각하면 된다.

[ 다음 위치 ]

m_tImage.GetNext(POSITON); // 리턴값 : CPair*

m_tImage.GetNextValue(POSITON); // 리턴값 : Value

[ 검색 응용 ]

POSITION pos = m_tImage.GetStartPosition();

CAtlMap<Key, Value>::CPair* pPair = NULL;
while (pos)
{
pPair = m_tImage.GetNext(pos);
}

POSITION pos = m_tImage.GetStartPosition();
while (pos)
{
CImageData* data = m_tImage.GetNextValue(pos);
}

--------------------------------------------------------------------------------------------------------------------------

나머지 다른 기능들도 있지만, 위의 내용만 알아도 충분히 알수 있는 내용들이라서 정리하지 않겠다.

[출처] CAtlMap 사용법 정리|작성자 스쿠프

 

 

지금, 서버 엔진쪽을 보고 있는데,  map 구조를 hash_map 으로 바꿔보고 다음 위의 CAtlMap으로도 적용시켜봐서

성능 테스트를 해봐야 할꺼 같다.

'C/C++언어' 카테고리의 다른 글

[ VC11-C++11 ] range base for - 1  (0) 2012.09.24
코드 최적화 팁  (0) 2012.09.10
MSXML 파싱  (0) 2012.07.03
프로그램 시간 측정  (0) 2012.03.21
버츄얼과 오버라이드 차이 Virtual? Override?  (0) 2012.03.06

출처 : http://kukuta.tistory.com/99

 

 

MSXML에 관한 VB나 C# 같은 언어의 예제는 많은데 C++의 예제는 별로 없는 것 같아 간단하게 나마 예제 코드를 만들어 봅니다.

코드를 보시기 전에 xml에 대한 기본적인 개념을 익히시고 싶으신 분은 XML 기초를 참조해 주세요.

MSXML API를 사용하기 위해서는 COM을 어느정도 알면 상당히 편하겠지만 몰라도 상관은 없습니다. 저도 COM에 관해서는 잘 모르기 때문에 이번 포스트에서 그와 관련된 설명은 건너 뛰기로 하겠습니다. 또한 MSXML 파서 설치 같은 것은 기본적으로 다 되어 있다고 가정하고 시작하도록 하겠습니다. 혹시나 설치나 설정 등에 어려움을 겪으신다면 댓글로 남겨 주세요. 그에 대한 포스팅을 따로 마련 해보도록 하겠습니다.

이 포스트는 MSXML4.0 버젼을 기준으로 작성 되었으며 포스트에서 예제로 사용하고 있는 xml파일은 첨부 파일로 저장되어 있으니 포스트를 읽어 보시기 전에 첨부 파일을 다운로드 받으시는 것이 전체적으로 이해 하시기 편할 것이라 생각 됩니다.



1. Dll import
MSXML 파서를 사용하기 위해서는 DLL을 import해야 합니다. 헤더 파일과 라이브러리를 로딩 하는 방법도 있지만 여기서는 dll을 import하는 방법을 사용하도록 하겠습니다. 우리가 사용하는 버젼은 msxml4.0 버젼이므로 아래와 같은 코드를 추가 합니다 :

#import <msxml4.dll>


2. 초기화
초기화 과정에는 COM객체를 사용하기 위한 초기화와 XML Document를 사용하기 위한 초기화 과정. 이렇게 두 단계가 있습니다. 물론 XML 파서가 COM을 사용하고 있으므로 COM에 대한 초기화가 먼저 이루어 져야 합니다 :

// COM 객체를 사용하기 위해 초기화를 한다.
// 윈도우 버젼 버젼이 올라가면서 확장된 초기화 함수를 사용 할 수 있다.
bool initializeCOM() {
#if _WIN32_WINNT >= 0x0400 & defined(_ATL_FREE_THREADED)
CoInitializeEx(NULL, COINIT_MULTITHREADED);
#else
CoInitialize(NULL);
#endif
return true;
}

COM객체를 사용할 준비를 마치고 나면 XMLDocument 객체를 초기화 해야 합니다. 여기서 사용되는 것XMLDOMDocument 인터페이스를 초기화 해야 합니다. 여기서는 편의상 IXMLDOMDocuement를 전역 변수로 놓도록 하겠습니다. 그리고 코드 작성의 편의를 위해 스마트 포인터로 정의 되어 있는 IXMLDOMDocument2Ptr을 사용하도록 하겠습니다 :

// DOM 의 핵심이 되는 객체
// 파싱된 XML을 저장하고 기타 각종 설정들을 담고 있다.

MSXML2::IXMLDOMDocument2Ptr g_pXMLDoc;
...
bool initializeMSXML() {
g_pXMLDoc.CreateInstance(__uuidof(MSXML2::DOMDocument40));
g_pXMLDoc->async = VARIANT_FALSE;
g_pXMLDoc->validateOnParse = VARIANT_TRUE;
g_pXMLDoc->resolveExternals = VARIANT_TRUE;
return true;
}

3. Validation check
MSXML파서의 기능중 하나는 xml파일의 정합성(validation)을 검증하는 것입니다. 여기서 정합성이란 단순한 well-formed 문서 뿐만 아니라, 스키마나 DTD에서 정의한 규칙을 제대로 따르고 있는가하는 것 까지 모두 검사하는 것입니다. 사용된 xml문서는 첨부 파일에서 다운 받으실 수 있습니다 :

bool validateDocument(const std::string& fileName) {
g_pXMLDoc->load(fileName.c_str());

// 최근 실행 된 XML관련 오퍼레이션에 대한 결과를 리턴
MSXML2::IXMLDOMParseErrorPtr pError = g_pXMLDoc->parseError;

_bstr_t strResult = "";
bool isValidate = true;
if (pError->errorCode != S_OK) {
strResult = _bstr_t("Validation failed on ") + fileName.c_str() +
_bstr_t("\n=====================") +
_bstr_t("\nReason: ") + _bstr_t(pError->Getreason()) +
_bstr_t("\nSource: ") + _bstr_t(pError->GetsrcText()) +
_bstr_t("\nLine: ") + _bstr_t(pError->Getline()) +
_bstr_t("\n");
isValidate = false;
}
else {
strResult = _bstr_t("Validation succeeded for ") + fileName.c_str() +
_bstr_t("\n======================\n") +
_bstr_t(g_pXMLDoc->xml) + _bstr_t("\n");
isValidate = true;
}

pError.Release(); // RefCount를 사용하는 포인터이므로 Release과정이 꼭 필요

std::cout << ConverseBstrToString(strResult) << std::endl;
return isValidate;
}

4. _bstr_t를 char* 로 변환 하기
XML파서에서 리턴하는_bstr_t 라는 문자열을 일반 cout이나 printf에서 출력 하면 정상적인 문자열이 출력되는 것이 아니라 알 수 없는 숫자들의 나열로 출력이 됩니다. 이유인즉슨 wide char를 사용하는 xml과 아스키코드를 사용하는 C/C++간의 문자열 바이트 차이 때문인데요, 이 문자열들을 정상적으로 보기 위해서는 간단한 변환 과정을 거쳐야 합니다 :

#include <atlconv.h> // USES_CONVERSION과 W2A를 사용하기 위해 include
...
std::string ConverseBstrToString(const _bstr_t& str) {
USES_CONVERSION;
return std::string(W2A(str));
}

altconv.h 파일을 인클루드 하기 싫으시다면 아래의 방법도 있습니다 :

std::string ConverseBstrToString(const _bstr_t& str) {
char buf[1024] = {0,};
sprintf(buf, "%S", (LPCTSTR)str);
return std::string(buf);
}

5. 엘리먼트의 텍스트 읽어 오기
첨부 되어 있는 예제 xml 문서를 보면 아래와 같은 형식의 comment 라는 엘리먼트가 있습니다 :

<purchaseOrder ... >
....
<comment>Hurry, my lawn is going wild!</comment>
....
</purchaseOrder>

위의 comment가 가지고 있는 텍스트를 읽어오기 위해서는 먼저 comment의 엘리먼트를 얻어오고, 그 엘리먼트에서 텍스트를 가지고 오면 됩니다 :

void selectSingleElement_and_getText() {
MSXML2::IXMLDOMNodePtr singleNodePtr = g_pXMLDoc->selectSingleNode(_bstr_t("/purchaseOrder/comment"));
if(NULL == singleNodePtr) {
return;
}
const BSTR str = singleNodePtr->Gettext();
std::cout << ConverseBstrToString(str) << std::endl;

singleNodePtr.Release();
}

코드 자체가 워낙 간단하여 긴 설명 보다는 개조식의 간단한 설명을 드리겠습니다 :

  1. 엘리먼트를 저장하기 위해서 IXMLDOMNodePtr을 선언 합니다.
  2. XMLDocument객체에서 selectSingleNode를 이용해 엘리먼트 객체를 얻어 옵니다. 이 때 XPath를 이용합니다.
    (이 함수는 특정 하나의 엘리먼트를 얻어오기 위해 사용되는 것입니다. 엘리먼트 리스트를 얻어오기 위한 함수는 뒤에서 다시 한번 살펴 보기로 하겠습니다.)
  3. 얻어진 엘리먼트에서 Gettext()를 호출 하여 텍스트 내용을 얻어 옵니다.
  4. 얻어진 텍스트를 아스키 형태로 변환하기 위해 위에서 작성한 ConverseBstrToString() 함수를 이용 합니다.

6. 엘리먼트의 속성(attribute) 읽어 오기

void selectSingleElement_and_getAttribute() {
MSXML2::IXMLDOMNodePtr singleNodePtr;
MSXML2::IXMLDOMAttributePtr attrPtr;
std::string retString("");

do {
singleNodePtr = g_pXMLDoc->selectSingleNode(_bstr_t("/purchaseOrder/shipTo"));
if(NULL == singleNodePtr) {
break;
}
attrPtr = singleNodePtr->Getattributes()->getNamedItem("country");
if(NULL == attrPtr) {
break;
}
const BSTR str = attrPtr->Getvalue().bstrVal;
// or 'const BSTR str = attrPtr->Gettext() is ok'
retString = ConverseBstrToString(str);
} while(false);

if(NULL != singleNodePtr) {
singleNodePtr.Release();
}
if(NULL != attrPtr) {
attrPtr.Release();
}

std::cout << "Attribute in Element : " << retString << std::endl;
}
  1. selectSingleNode() 함수를 이용하여 엘리먼트를 얻어 오는 것 까지는 위에서 살펴 본 것과 동일합니다.
  2. 엘리먼트를 얻고 난 후에는 Getattributes() 함수를 이용해 어트리뷰트들을 얻고, getNamedItem() 함수를 이용해 특정 어트리뷰트를 이름을 통해 가지고 옵니다.
  3. 어트리뷰트가 가지고 있는 값을 가지고 올 때, 어트리뷰트의 타입을 지정할 수 있습니다. 만일 어트리뷰트가 단순한 문자열이라면 Getvalue().bstrVal 이나 Gettext() 함수를 이용해 가지고 올 수 있습니다. 만일 정수라던지 하는 다른 타입일 경우 Getvalue().xxxVal에서 적절한 타입을 지정해 주시면 됩니다. 지정될 타입에 대해서는 MSDN이나 해당 union 구조체를 확인 하시면 됩니다.

7. 엘리먼트 리스트 읽어 오기

엘리먼트 리스트에서 값을 얻어 오는 것 또한 위의 과정들과 크게 다르지 않습니다 :

void selectElementArray() {
MSXML2::IXMLDOMNodeListPtr nodeListPtr = g_pXMLDoc->selectNodes(_bstr_t("/purchaseOrder/items/item"));
for(int i=0; i<nodeListPtr->Getlength(); i++) {
MSXML2::IXMLDOMNodePtr parentNodePtr = nodeListPtr->Getitem(i);
MSXML2::IXMLDOMNodePtr childNodePtr = parentNodePtr->selectSingleNode(_bstr_t("productName"));
if(NULL != childNodePtr) {
const BSTR str = childNodePtr->Gettext();
std::cout << util::ConverseBstrToString(str) << std::endl;
}
childNodePtr.Release();
parentNodePtr.Release();

}
nodeListPtr.Release();
}
  1. 가장 먼저 하는 일은 selectNodes() 함수를 이용해 엘리먼트 리스트를 얻어 오는 것입니다.
  2. 엘리먼트 리스트를 얻어 오는 것이 성공하면 IXMLDOMNodeListPtr에 그 값이 저장 됩니다. 만일 엘리먼트가 하나 뿐이거나 기타등등 조건에 맞지 않는 경우 NULL을 리턴하므로 다른 함수도 물론 그렇지만 이 함수를 호출 하고난 후에 NULL체크를 하여 성공 여부를 판단하는 것이 중요 합니다.
  3. 몇 개의 엘리먼트를 얻어 왔는지는 Getlength() 함수를 통해 알아 낼 수 있습니다.
  4. 엘리먼트 리스트는 인덱스를 지정하므로써 각각의 엘리먼트들에게 접근 할 수 있습니다. 이 때 사용되는 함수는 Getitem()이며 파라메터로 인덱스를 받습니다.
  5. 그 외의 나머지 과정의 위에서 설명한 과정과 동일 합니다.

결론
간단하게 나마 msxml 파서의 사용법에 대해서 알아 보았습니다. msxml에 대해서 정확하게 알아 보기 위해서는 DOM과 COM 스펙에 대해 좀 더 조사하고, 위에 언급한 API말고도 수많은 편리함을 제공 해 주는 API들이 많지만 필자가 공부를 하면서 가장 많이 사용했던 API 몇 가지에 대해서만 언급을 하였습니다. 보다 더 많은 정보가 필요하신 분들은 MSDN이나 W3C에 방문하셔서 검색해 보시면 심도 있는 정보를 얻으실 수 있으실 겁니다.

전체 소스 보기


Ref.

'C/C++언어' 카테고리의 다른 글

코드 최적화 팁  (0) 2012.09.10
CAtlMap 사용법 정리  (0) 2012.08.25
프로그램 시간 측정  (0) 2012.03.21
버츄얼과 오버라이드 차이 Virtual? Override?  (0) 2012.03.06
Predefined Macros  (0) 2012.01.05

+ Recent posts