반응형



http://ezbeat.tistory.com/206

 

1. 종료 핸들러

종료 핸들러는 __try와 __finally로 이루어져 있습니다.

__try{
        Ezbeat 루틴
}
__finally{
        Hacker 루틴
}
코드를 보시면 __try안으로 들어와서 Ezbeat루틴을 실행하게 되면 __try루틴을 빠져나올 때 항상 __finally루틴을 실행하게 됩니다. 위 코드는 너무 추상적 이므로 직접 소스를 짜서 보도록 하겠습니다.

두 수를 입력 받아서 나누는 프로그램 입니다.

위 소스코드에서는 2가지 실험 결과를 통해 이해를 하실 수 있습니다. 결과를 보시겠습니다.


첫번째 결과에서는 9와 3을 나눠서 Result가 3이었습니다. 
__try를 빠져나오면서 __finally에 있는 코드를 실행 한 것을 볼 수 있습니다.

두번째 결과를 보시면 5와 5를 나눠서 Result가 1이었습니다.
그러면 __try에 있는 if문안으로 들어오게 됩니다. 리턴을 하게 되면서 main함수가 종료가 되겠지요.
하지만 __finally에 있는 코드는 여전히 실행을 하게 됩니다.

위 소스코드와 결과만 봐도 이해를 하셨을 것입니다. 그러면 __try루틴에 들어오게 되면 반드시 __finally루틴이 실행되느냐..
그건 아닙니다. 모든과정에 예외가 있듯이 이것도 예외가 있습니다.

몇몇 종료 함수들을 사용하게 되면 __finally루틴으로 들어오지 않고 프로그램이 끝나게 됩니다.
( ExitProcess, ExitThread, exit .. )
소스코드와 결과를 보시겠습니다.

return부분을 ExitProcess함수로 바꾸었습니다.

결과를 보면 __finally루틴으로 들어오지 않고 종료가 된 것을 볼 수 있습니다.

결론입니다.
__try 루틴을 빠져나오는 경우 ( __finally 루틴 실행 함 )   : return, continue, break, goto, 예외발생
__try 루틴을 빠져나오는 경우 ( __finally 루틴 실행 안함) : ExitProcess, ExitThread, TerminateProcess, exit

상황에 맞게 적절히 종료 핸들러를 사용해 더욱 깔끔한 소스코드를 짜보세요 ^^

2. 예외 핸들러
예외 핸들러는 __try와 __except로 이루어져 있습니다.
__try{
      예외발생 루틴
}
__except(예외처리 방식){
      예외가 발생됬을 때 처리하는 루틴
}
__try 안에서 예외가 발생하면 예외처리 방식에 따라 해당 예외를 처리하게 됩니다.

예외처리 방식에 올 수 있는 경우의 수는 3가지 입니다.
EXCEPTION_EXECUTE_HANDLER : 예외가 발생하면 __try루틴의 나머지 부분을 실행하지 않고 예외처리 루틴으로 감
EXCEPTION_CONTINUE_EXECUTION : 예외가 발생한 지점으로 돌아가 다시 실행하도록 하는 방식
EXCEPTION_CONTINUE_SEARCH : 함수가 호출된 순서를 바탕으로 예외핸들러를 다시 찾아서 예외처리 해라

보통 많이 쓰이는 것은 위 2가지 이고 마지막은 잘 쓰지 않습니다.
먼저 각각의 경우를 소스코드를 짜서 보여드리도록 하겠습니다.
EXCEPTION_CONTINUE_SEARCH 경우는 잘 쓰지 않으므로 설명은 따로 하지 않겠습니다.

ㄱ. EXCEPTION_EXECUTE_HANDLER

위 소스코드와 크게 다른 것은 없습니다. try에서 예외를 발생시켰을 때와 안시켰을 때의 결과를 보도록 하겠습니다.

예외발생 안시켰을 때


예외발생 시켰을 때


예외를 발생시키면 __try에서 남은 코드를 실행 시키지 않고 바로 예외처리 루틴으로 가게 되는 것을 볼 수 있습니다.

ㄴ. EXCEPTION_CONTINUE_EXECUTION

소스코드가 약간 복잡합니다.

출력 결과를 본 후에 설명을 하도록 하겠습니다.

처음에 5와 0을 입력 했더니 예외가 발생해서 Func함수로 들어왔습니다. (GetExceptionCode는 뒤에서 설명하겠습니다.)
해당 예외가 정수를 0으로 나눴을 때 발생한 예외이므로 if문 안으로 들어오게 됩니다.
안에서 입력받는 수가 두개의 정수를 다시 입력 받는게 아니라 뒤에 들어온 수만 입력을 받고 있습니다.

여기서 궁금하신 분이 계실 것입니다. 두 수를 전부다 입력 받아도 되겠지..라고 생각하신 분들이 계실 것입니다.
제가 테스트를 해보았습니다.

Func함수 내부를 살짝 바꾸었습니다. 두 수를 입력 받도록 되어 있습니다. 출력 결과를 보도록 하겠습니다.

마지막 결과를 따르면 Result가 3이 나와야 할 탠데 결과는 5가 나왔습니다.
첫번째 값은 계속 넣어도 무용지물인지에 대해 알아본 결과

예외가 발생한 이후에 나눗셈을 할 때 첫번째 수는 처음 입력한 수를 특정 메모리에 넣어두고 
정상적인 나눗셈을 수행할 때 처음 입력했던 값을 EAX레지스터로 옮긴다음에 두번째 수와 나눗셈을 수행하게 됩니다.
( 메모리에 넣어둔 값을 EAX로 옮기는 과정은 커널영역에서 하는 것 같습니다. )

이 부분은 이해하기가 어려우므로 그냥 그런다고 넘어가시면 됩니다.
쫌더 쉽게 말해보면 두 수를 나눌 때 처음 입력 받은 수는 특정 메모리에 저장되어 있다가 
다시 나눗셈을 수행할 때 가져와서 두번째 수와 나눗셈을 수행한다는 것입니다.
아래그림을 보시면 어느정도 이해가 되실 것입니다.


이정도면 이해가 되셨을꺼 같으니 결론을 내보면 EXCEPTION_CONTINUE_EXECUTION 옵션은 예외가 발생한 지점으로 다시 가서 코드를 실행하라는 뜻이됩니다.

위에서 설명하겠다고 한 GetExceptionCode입니다.
GetExceptionCode은 함수가 아닌 메크로 입니다. 예외가 발생했을 때 어떠한 예외가 발생했는지 알려주는 메크로 입니다.
많은 경우의 수가 있는데
위 링크로 가시면 볼 수 있습니다.

이러한 예외처리 방법을 코드상에서 많이 볼 수 있는데 알고있으면 좋을것 같아서 한번 써보았습니다.
그리고 이러한 예외처리 방법을 사용한 안티리버싱 기법도 있습니다.


반응형
반응형


  1. Structured Exception Handling
  2. Termination Handling
  3. Exception Handling
  4. Stack Unwind
    1. Local Unwind
    2. Global Unwind
  5. Unhandled Exception
  6. First and Second Chance Exception
  7. 링크



1 Structured Exception Handling

  • 윈도우즈에서 제공하는 예외 처리 방식이다. (사실 실제 구현은 컴파일러에서 이루어진다.) 
  • C++ 예외와는 다른 것이다. 
  • 윈도우즈 상에서는 C++ 스탠다드 라이브러리도 내부적으로 SEH를 이용한다. 
  • C++ 예외 처리는 당연히 C++에서만 사용할 수 있으나, SEH는 언어 중립적이기 때문에 다른 언어에서도 사용 가능하다. 
  • Termination Handling과 Exception Handling으로 분류할 수 있다. 


2 Termination Handling

    __try { // Guarded code } __finally { // Termination handler }
    finally 블록 안의 안의 코드는 try 블록 안에서 무슨 짓을 하든 반드시 실행된다. try 블록 안에서 return을 해도, goto를 해도, longjump 명령을 직접 호출해도, 결국은 실행된다. 좀 더 자세하게 말하자면, return이나 goto를 하기 직전에 finally 블록 안의 코드가 실행된다. (exit, abort, ExitProcessTerminateThread 등을 통해 프로세스나 스레드가 종료되는 경우는 예외다. 이런 경우에는 finally 블록의 코드가 실행되지 않는다.) 

    try 블록 안에서 return, goto, longjump 등을 사용하는 것은 자제하는 것이 좋다. 반환값 문제 때문에 컴파일러가 추가적인 코드를 만들어내기 때문이다. 이 코드의 양은 CPU마다 틀려지는데, 수백에서 수천 사이클까지 걸리는 경우가 있다. 그러므로 자주 사용하는 코드에다가 집어넣어 놓으면 프로그램의 성능이 심각한 수준까지 떨어질 염려가 있다. 

    코드의 흐름이 자연스레 finally 블록까지 흘러가는 경우(return 등을 사용하지 않는 경우), 오버헤드는 거의 없다. x86 계열의 CPU에서 마이크로소프트 컴파일러를 사용한 경우, 단 하나의 명령어를 실행할 뿐이다. 

    일반적으로 가장 좋은 방법은 try 블록 안에서 return, continue, break, goto 문 등을 쓰지 않는 것이다. 이들은 try 블록 바깥 쪽으로 빼내줘야 한다. 그래도 어쩔 수 없이 try 블록을 빠져나가고자 한다면 __leave 키워드를 사용하면 된다. 
    bool some_function(const char* param) {
    	 bool bOK = false;
    	 char* pBuffer = NULL; ... 
    	__try { 
    		pBuffer = new char[100];
    		 if (pBuffer == NULL) { 
    			// return false; 
    			// 여기서 false를 리턴할 것이 아니라, __leave를 사용한다.
    			__leave; 
    		}
    		 bOK = true; 
    	} 
    	__finally { 
    		if (pBuffer) 
    			delete [] pBuffer; 
    	} 
    	return bOK; }
    __leave 키워드를 사용하면, 코드의 흐름(instruction pointer)이 try 블록이 끝나는 곳(__finally 바로 앞의 괄호)으로 이동한다. 그 다음 finally 블록 안의 코드가 자연스레 실행된다. 위에서도 언급했듯이 자연스러운 이동은 성능에 영향을 거의 주지 않는다. 


3 Exception Handling

    __try { // Guarded code }
     __except (EXCEPTION_EXECUTE_HANDLER) // Exception filter 
    { // Exception handling code }
    except 부분은 C++ 예외 처리 구문의 catch와 비슷해 보이지만, 다르다. C++ 예외 처리에서는 이 부분에 예외의 종류 밖에 들어갈 수 없지만, SEH에서는 exception filter라고 불리는 C 구문(expression)이 들어간다. exception filter 구문은 계산을 거친 후, 최종적으로 세 가지 값 중의 하나가 되어야 한다. 

    • EXCEPTION_CONTINUE_EXECUTION (-1) 
        예외를 무시하고, 예외가 발생한 지점에서부터 프로그램을 계속 실행한다. 예를 들어 10 / i 에서 i가 0이라서 예외가 발생한 경우, 예외 처리 필터가 이 값이라면, 다시 10 / i부터 실행한다는 말이다. 
    • EXCEPTION_CONTINUE_SEARCH (0) 
        except 블록 안의 코드를 실행하지 않고, 상위에 있는 예외 처리 핸들러에게 예외 처리를 넘긴다. 
    • EXCEPTION_EXECUTE_HANDLER (1) 
        except 블록 안의 코드를 실행한 후, except 블록 아래의 코드를 실행한다. 

    이 세가지 값을 이용해서 예외 처리 코드를 샘플로 만들어 보자면 다음과 같다. 

    • try 블록 안에서 예외가 발생한 경우, 그 예외가 ACCESS_VIOLATION일 경우에는, exception 블록 안의 코드를 실행하고, 다른 예외인 경우에는 상위에 있는 예외 처리 핸들러에게 통제를 넘기는 코드. (GetExceptionCode 함수는 try 블록 안에서 발생한 예외의 종류를 반환하는 함수다. 자세한 것은 MSDN을 참고하도록.) 
      __try { ... // compute something } 
    • __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
    •   EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
    •  { cerr << "Access violation happened!" << endl; }
      
      
      
      
    • try 블록 안에서 발생한 예외에 대한 정보를 검사하는 필터 함수를 만든 후, 이를 exception filter 부분에서 실행해서 어떤 행동을 해야할 지를 판단하는 코드. (GetExceptionInformation 함수는 예외와 예외가 발생한 시점에서의 시스템에 대한 정보를 반환하는 함수다. 자세한 것은 MSDN을 참고하도록.) 
      DWORD decide_what_to_do(PEXCEPTION_POINTERS pExceptions)
    •  { // check exceptions ...
    • 	 return EXCEPTION_CONTINUE_EXECUTION;
    •  }
    •  __try 
    • { ... // compute something } 
    • __except(decide_what_to_do(GetExceptionInformation())) 
    • { ... }
      Exception Handling의 경우, Termination Handling과는 달리, try 블록 안에 return, goto 등이 있어도 코드 크기나 성능에 페널티가 없다. 


4 Stack Unwind

    4.1 Local Unwind

      try-finally 구문에서 try 블록 내부에 return, goto 등의 premature exit가 있을 경우 발생한다. finally 블록 실행 후, 값을 리턴해야하기 때문에, 컴파일러는 임시 변수를 생성해서, 리턴값을 저장한 후, finally 블록을 실행하고, 저장해둔 값을 리턴한다. 위에서도 나와있듯이 이런 코드가 꽤 비용이 크기 때문에 극구 피해야한다. 

    4.2 Global Unwind

      Global unwind는 try-except 구문에서 exception filter가 EXCEPTION_EXECUTE_HANDLER 값인 경우 발생한다. 동작을 요약하자면, EXCEPTION_EXECUTE_HANDLER로 판정된 try 블록 안의 모든 finally 블록을 제일 안쪽의 것부터 실행한 다음, 원래 try 블록의 except 블록 부분을 실행한다가 되겠다. 

      다음은 Programming Applications from Microsoft Windows에서 가져온 예제이다. 짐작하듯이 주석에 있는 번호는 실행 순서를 나타낸다. 
      void function1() 
      { 
       // 1. 여기서 뭔가를 처리 ...
        __try
        { 
       // 2. 다른 함수를 호출 function2();
        // 이 부분의 코드는 실행되지 않는다. ... 
       }
        __except ( /* 6. 필터 구문을 검사 */  EXCEPTION_EXECUTE_HANDLER)
        { // 8. Unwind가 일어난 후에, 예외 처리 핸들러가 실행된다.
        MessageBox(...); 
       } 
      } 
       
      void function2() 
      { // 3. 여기서 뭔가를 처리 ...
        __try { // 4. 뭔가에다 락을 건다.
        EnterCriticalSection(&C); // 5. 여기서 예외가 발생한다.
        int some = 10 / 0; 
       } 
       __finally 
       { // 7. 상위 함수의 exception filter 부분의 값이 
       // EXCEPTION_EXECUTE_HANDLER이기 때문에 Global unwind가 일어난다. 
       // 락을 푼다.
        LeaveCriticalSection(&C);
        } // 이 부분의 코드는 실행되지 않는다. }
      왜 실제로 예외가 발생한 finally 부분이 먼저 실행되지 않느냐고 할 수 있는데, 예외가 발생한 경우, finally 보다는 except를 먼저 찾아서 처리한다고 생각하면 된다. 왜 그런지는 아직까지 잘 이해가 가지 않는다. 어쨌든 중요한 것은 finally 부분은 반드시 실행이 된다는 점이다. 


5 Unhandled Exception

    예외가 발생해서 그걸 처리하는 try-except 부분을 차례대로 검사했는데, 모두가 EXCEPTION_CONTINUE_SEARCH를 반환한 경우를 Unhandled Exception이라고 한다. 즉 해당하는 예외를 처리할 핸들러가 하나도 없다는 말이다. 

    윈도우즈 상에서 모든 스레드는 Kernal32.dll에 있는 BaseProcessStart와 BaseThreadStart 함수를 통해 실행된다. 첫번째는 프로세스의 메인 스레드를 위한 것이고, 두번째는 추가적인 스레드를 위한 것이지만, 결국 똑같은 넘이다. 
    VOID BaseProcessStart(PPROCESS_START_ROUTINE pfnStartAddr) 
    {
      __try { 
      ExitThread((pfnStartAddr)()); 
     }
      __except(UnhandledExceptionFilter(GetExceptionInformation()))
      { 
     ExitProcess(GetExceptionCode()); 
     } 
    } 
    VOID BaseThreadStart(PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam)
     { 
     __try {
      ExitThread((pfnStartAddr)(pvParam)); 
     }
      __except(UnhandledExceptionFilter(GetExceptionInformation())) {
      ExitProcess(GetExceptionCode());
      } 
    }
    결국 Unhandled Exception이 발생한 경우, 시스템에서 이를 잡아서 UnhandledExceptionFilter 함수를 실행한다는 것을 알 수 있다. 이 함수는 사용자들에게 친숙한 다음 대화창을 표시한다. 

    crash.gif 

    여기서 "확인"을 누르게 되면 EXCEPTION_EXECUTE_HANDLER를 반환하고, global unwind를 일으켜, 결국 ExitProcess를 호출하게 한다. 이것이 확인을 누르면 프로그램이 종료되는 이유다. "취소"를 누른 경우, 시스템은 JIT(Just In Time) 디버거를 로드하게 된다. 

    모든 스레드의 주 루프를 손수 만든 try-except (EXCEPTION_EXECUTE_HANDLER) 구문으로 감싸면, 예외가 위의 함수까지 가지 않게 된다. 이는 위의 대화창을 표시하지 않고, 다른 행동을 할 수 있다는 말이다. 그러나 모든 스레드를 이런 식으로 감싸는 것보다는 SetUnhandledExceptionFilter 함수를 이용하는 것이 낫다. 
    LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter( LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter ); 
    // TOP_LEVEL_EXCEPTION_FILTER 함수의 형식 
    LONG UnhandledExceptionFilter( STRUCT _EXCEPTION_POINTERS* ExceptionInfo );
    이 함수를 사용하는 예는 MiniDump 페이지를 참고하기 바란다. 


6 First and Second Chance Exception

    예외 핸들러를 설치한 경우, 예외가 발생하면 핸들러에서 그것을 잡아서 처리한 후, 실행을 계속한다는 개념은 예외 처리의 기본이다. 그런데 실행 중인 프로그램에 디버거가 붙어있는 경우에는 약간 다르다. 디버거가 붙어있는 상태에서의 예외 처리는 다음과 같은 순서로 이루어진다. 

    1. 디버거에서 첫번째로 예외가 발생한다. --> First Chance Exception 
    2. 프로그램 내부의 예외 핸들러에서 예외가 발생한다. 
    3. 디버거에서 두번째로 예외가 발생한다. --> Second Chance Exception 

    물론 첫번째로 디버거에서 예외가 발생했을 때, 프로그램 쪽으로 예외를 넘겨주지 않으면 거기서 끝이다. 또한 프로그램 내부에서 예외 핸들링을 제대로 하지 않은 경우에도 한번으로 끝이다. 이 경우는 원래 프로그램이 크래쉬되는 상황이라는 것은 두말할 나위 없다. 더 자세한 내용은 [WWW]INFO: First and Second Chance Exception Handling를 참고하시라. 


7 링크


출처 http://excel96.cafe24.com/moin.cgi/SEH

반응형
반응형

http://www.cyworld.com/01099811833/3457820

 

마우스 이벤트를 강제로 발생하는 API함수

VOID mouse_event( 
DWORD dwFlags, // 동작 지정 Flag
DWORD dx, // x좌표

DWORD dy, // y좌표

DWORD dwData , // 휠정보

PTR dwExtraInfo // 추가 정보
);

dwFlags 정보

상수설명
MOUSEEVENTF_ABSOLUTE8000x, y mouse의 위치값을 포함하여 event를 발생함
MOUSEEVENTF_MOVE1x, y에 지정한 위치로 이동
MOUSEEVENTF_LEFTDOWN2왼쪽 button down
MOUSEEVENTF_LEFTUP4왼쪽 button up
MOUSEEVENTF_RIGHTDOWN8오른쪽 button down
MOUSEEVENTF_RIGHTUP10오른쪽 button up
MOUSEEVENTF_MIDDLEDOWN20가운데 button down
MOUSEEVENTF_MIDDLEUP40가운데 button up
MOUSEEVENTF_WHEEL800mouse wheel 동작


표의 값은 VB.NET에선 &H로, C#에선 0x로 처리

dx, dy는 이동시킬 좌표를 지정, 단 OUSEEVENTF_MOVE와 MOUSEEVENTF_ABSOLUTE가 지정되어야 실제로 마우스 커서가 이동되는 효과를 볼 수 있음

Mouse의 좌표 지정시 실제 Monitor화면의 해상도를 기준점으로 잡으려면 65535로 가로세로 해상도를 나누고 Mouse Cursor를 위치시킬 위치를 곱하면 됩니다. 예를 들어 화면 해상도가 1024*768일 경우 (65535 / 1024) * y, (65535 / 768) * y 와 같이 처리하시면 원하는 곳으로 이동하게 됩니다.

| (or연산)을 통해서 여러가지 동작을 한번에 지정할 수 있다

ex) 마우스 오른쪽 버튼을 눌렀다 떼는 동작

mouse_event(0x8 | 0x10, 0, 0, 0, 0);

반응형
반응형

디렉토리 생성함수

CreateDirectory(경로,NULL);

NULL 인경우 해당 폴더가 존재하면 생성하지 않는다










출처 : http://blog.daum.net/studiocoma/6521408



Win32 API가 제공하는 CreateDirectory 함수의 응용.

API CreateDirectory의 경우 c:\1\2\3을 만들때 c:\1\2가 이미 존재하지 않으면 실패한다. -_-

이를 보완하고(상위폴더부터 주구장창 만들어 낸다;) 경로에 파일명까지 포함되어 있어도 폴더만 만들도록 해봤다.
- 말은 거창(?)한데 그냥 문자열 파싱해서 CreateDirectory를 반복 호출한다;; 

BOOL _CreateDirectory( LPCTSTR lpszPath )
{
    TCHAR szPathBuffer[MAX_PATH];

    size_t len = _tcslen( lpszPath );

    for ( size_t i = 0 ; i < len ; i++ )
    {
        szPathBuffer[i] = *( lpszPath + i );
        if ( szPathBuffer[i] == _T('\\') || szPathBuffer[i] == _T('/') )
        {
            szPathBuffer[i + 1] = NULL;
            if ( ! PathFileExists( szPathBuffer ) )
            {
                if ( ! ::CreateDirectory( szPathBuffer, NULL ) )
                {
                    if ( GetLastError() != ERROR_ALREADY_EXISTS )
                        return FALSE;
                }
            }
        }
    }
    return TRUE;
}


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


CFileFind fileFinder;
 if(!fileFinder.FindFile(".\\simple\\MyMenu.mdb"))
 {
  CreateDirectory(".\\simple", NULL);
  CopyFile(".\\MyMenu.mdb", ".\\simple\\MyMenu.mdb", TRUE);
 }



부모디렉토리없어도 디렉토리 생성하기, 디렉토리에 파일있어도 하위디렉토리까지 모든파일 삭제하기


출처 :  http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=51&MAEULNo=20&no=7293&ref=7293


API 에서 지원되는 함수들은 어느정도 제약이 있기 떄문에......

 

좀 짜증났거든요.....  

 

물론 만드는게 어렵진 않지만 자주 사용해야 하므로 아예 보관하시고 카피해서 사용하시면 편하실거 같아서요...

 

 

 

#include <windows.h>

#include <stdio.h>

#include <string>

 

using namespace std;

 

/*

 

  기존 디렉토리가 있을경우 안만들어지고 없으면 만든다. 부모디렉토리가 없어도 생성가능

 

*/

void CreateDir(char* Path)

{

    char DirName[256];  //생성할 디렉초리 이름

    char* p = Path;     //인자로 받은 디렉토리

    char* q = DirName;  

 

    while(*p)

    {

        if (('\\' == *p) || ('/' == *p))   //루트디렉토리 혹은 Sub디렉토리

        {

            if (':' != *(p-1))

            {

                CreateDirectory(DirName, NULL);

            }

        }

        *q++ = *p++;

        *q = '\0';

    }

    CreateDirectory(DirName, NULL);  

}

 

 

/*

 

 하위디렉토리를 제외한 해당 디렉토리 모든 파일들을 제거

 

 */

void DeleteAllFiles(char* folderPath)

{

    char fileFound[256];

    WIN32_FIND_DATA info;

    HANDLE hp;

 

    sprintf(fileFound, "%s\\*.*", folderPath);

    hp = FindFirstFile(fileFound, &info); //디렉토리에 파일이 있는지 첫번째 파일만.

    do

    {

        sprintf(fileFound,"%s\\%s", folderPath, info.cFileName);

        DeleteFile(fileFound);

 

    }while(FindNextFile(hp, &info));  //다른 파일이 있을때 까지

 

    FindClose(hp);

}

 

 

/*

 

 해당 하는 디렉토리에 파일이 존재해도  디렉토리가 비어있지 않아도 지울수 있다 .

 

*/

 

void EmptyDirectory(char* folderPath)

{

    char fileFound[256];

    WIN32_FIND_DATA info;

    HANDLE hp;

 

    sprintf(fileFound, "%s\\*.*", folderPath);

    hp = FindFirstFile(fileFound, &info);   //디렉토리에 파일이 있는지 첫번째 파일만.

    do

    {

        if (!((strcmp(info.cFileName, ".")==0)||(strcmp(info.cFileName, "..")==0)))

        {

            if((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)==FILE_ATTRIBUTE_DIRECTORY)  //Sub디렉토리가 존재하는경우

            {

                string subFolder = folderPath;

                subFolder.append("\\");

                subFolder.append(info.cFileName);

                EmptyDirectory((char*)subFolder.c_str()); /// {return (_Ptr == 0 ? _Nullstr() : _Ptr); }

                RemoveDirectory(subFolder.c_str());

            }

            else

            {

                sprintf(fileFound,"%s\\%s", folderPath, info.cFileName);

                BOOL retVal = DeleteFile(fileFound);

            }

        }

 

    }while(FindNextFile(hp, &info));

 

    FindClose(hp);

}




CreateDirectory 에서 폴더 접근 권한 바꾸기


출처 : http://ktr0.egloos.com/757379


Windows API 를 보면 CreateDirectory 라는 함수가 있다.

CreateDirectory("c:\aaa",NULL);

위와 같은 방법으로 첫번째 파라메터에 폴더 이름을 써 주고
두번째 파라메터에 NULL 을 써 주는 경우가 많다.

두 번째 파라메터는 사실 보안에 대한 파라메터이다.

PSECURITY_ATTRIBUTE 형의 변수가 들어가는데, NULL 이면 부모 폴더의
값을 그대로 가져온다.

 SECURITY_ATTRIBUTES sa;
 SECURITY_DESCRIPTOR sd;
 PSID pEveryoneSID = NULL;
 SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
 EXPLICIT_ACCESS ea[2];
 PACL pacl = NULL;
 if(!AllocateAndInitializeSid(&SIDAuthWorld,1, SECURITY_WORLD_RID,0,0,0,0,0,0,0,&pEveryoneSID))
 {
  AfxMessageBox("Fail to get Everyone SID!!!",0,0);
  return;
 }
 ZeroMemory(&ea, 2*sizeof(EXPLICIT_ACCESS));
 ea[0].grfAccessPermissions = GENERIC_ALL;
 ea[0].grfAccessMode = SET_ACCESS;
 ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
 ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
 ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
 ea[0].Trustee.ptstrName = (LPTSTR) pEveryoneSID;

 SetEntriesInAcl(1,ea, NULL, &pacl);
 InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION);
 SetSecurityDescriptorDacl(&sd, TRUE,pacl , FALSE);
 //SetSecurityDescriptorSacl(&sd, TRUE,pacl , FALSE);


 sa.nLength = sizeof (SECURITY_ATTRIBUTES);
 sa.lpSecurityDescriptor = &sd;
 sa.bInheritHandle = TRUE;
 
 ::CreateDirectory(sysDirBuffer_tmp,&sa);
 if(pEveryoneSID){
  FreeSid(pEveryoneSID);
 }
 if(pacl){
  LocalFree(pacl);
 }

위의 예가 바로 CreateDirectory 함수에서 SECURITY_ATTRIBUTE 값을 가지고
폴더에 모든 ACCESS 권한을 주는 예제이다.

 

 

http://www.tipssoft.com/bulletin/board.php?bo_table=QnA&wr_id=380&page=392

GetCurrentDirectory 함수 : 현재의 작업 디렉토리를 얻어온다. 
SetCurrentDirectory 함수 : 작업디렉토리의 경로를 셋팅한다. 

일반적으로 작업디렉토리는 파일이 실행된 경로가 지정됩니다. 
아래는 작업디렉토리의 변경이 일어나는 대표적인 예인 CFileDialog 대화상자를 이용한 경우입니다 

참고하시면 될 듯 싶습니다. 


    char path[MAX_PATH] = {0}; 
    // 현재의 작업디렉토리를 저장한다. 
    GetCurrentDirectory(MAX_PATH, path); 

    // CFileDialog 를 Open 모드로 생성한다. 
    CFileDialog dlg(TRUE); 
    if(dlg.DoModal()==IDOK){ 
                // 사용자가 폴더를 옮겨가며 특정 파일을 지정 
                // 이경우 작업폴더가 변경됨. 
                CString str = dlg.GetFileName(); 
                //얻어온 파일과 관련된 코드 추가.............. 
                
                // 이전에 저장했던 작업디렉토리 경로를 되돌려 셋팅 
                SetCurrentDirectory(path); 
    }


 





http://msdn.microsoft.com/ko-kr/library/system.io.directory.createdirectory(v=vs.80).aspx



Directory.CreateDirectory 메서드

.NET Framework 2.0
3명 중 1명이 도움이 되는 것으로 평가 이 항목 평가

지정된 경로에 모든 디렉터리를 만듭니다.

이름설명
Directory.CreateDirectory (String)지정된 path로 모든 디렉터리와 하위 디렉터리를 만듭니다.

.NET Compact Framework에서 지원됩니다.

Directory.CreateDirectory (String, DirectorySecurity)지정된 경로에 모든 디렉터리를 만들고 지정된 Windows 보안을 적용합니다.

방법: 파일에 텍스트 쓰기 

반응형
반응형

http://infoki.net/613


SHFILEOPSTRUCT

SHFileOperation 함수가 행하는 파일 조작에 관한 정보가 격납됩니다.

typedef struct _SHFILEOPSTRUCT {
    HWND  hwnd;                 // 윈도우 핸들
    UINT  wFunc;                // 실행하는 조작
    LPCTSTR  pFrom;             // 대상 파일명
    LPCTSTR  pTo;               // 목적 파일명
    FILEOP_FLAGS  fFlags;       // 플래그
    BOOL fAnyOperationsAborted; // 결과
    LPVOID  hNameMappings;      // 파일명 매핑 오브젝트
    LPCTSTR lpszProgressTitle;  // 다이얼로그의 타이틀
} SHFILEOPSTRUCT, FAR *LPSHFILEOPSTRUCT;

멤버

hwnd

파일 조작의 상황을 표시하는 다이알로그 박스의 오너 윈도우의 핸들을 지정합니다.

wFunc

실행하는 파일 조작을 나타내는 값을 지정합니다.이하의 값중 한쪽이 격납됩니다.

의미
0x0001 (FO_MOVE)

pFrom 멤버로 지정된 파일을 pTo 멤버로 지정된 위치로 이동합니다.

0x0002 (FO_COPY)

pFrom 멤버로 지정된 파일을 pTo 멤버로 지정된 위치에 카피합니다.

0x0003 (FO_DELETE)

pFrom 멤버로 지정된 파일을 삭제합니다.

0x0004 (FO_RENAME)

pFrom 멤버로 지정된 파일의 이름을 pTo 멤버로 지정된 파일명으로 변경합니다.이 플래그를 사용하고, 한 번의 함수 호출로 복수의 파일의 이름을 변경할 수 없습니다.복수의 파일의 이름을 변경하려면 FO_MOVE 플래그를 사용합니다.

pFrom

조작의 대상이 된다1개이상의 파일명이 격납된 버퍼의 주소를 지정합니다.이러한 파일명은 풀 패스 지정되어 있지 않으면 안됩니다.파일명에 “*” 등의, 표준MS-DOS와일드 카드를 지정할 수 있습니다.

각각의 파일명은1개의 눌 문자로 단락지어지지 않으면 안됩니다.또, 마지막 파일명의 뒤에는2개의 눌 몬지를 들어갈 수 있지 않으면 안됩니다.

pTo

카피·이동처 디렉토리명 또는 변경 후의 파일명이 격납된 버퍼의 주소를 지정합니다.이 멤버를 사용하지 않는 경우에는 0 (NULL) (을)를 지정하지 않으면 안됩니다.

카피 또는 이동의 조작에 대하고, pFrom 멤버와 같은 방법으로 복수의 파일을 지정할 수 있습니다.이 경우, 각각의 파일명은1개의 눌 문자로 단락지어, 마지막 파일명의 뒤에는2개의 눌 몬지를 들어갈 수 있지 않으면 안됩니다.복수의 파일명을 지정하려면 , fFlags멤버에 FOF_MULTIDESTFILES 플래그를 지정합니다.

카피 또는 이동의 조작에 대하고, 존재하지 않는 디렉토리를 지정할 수 있습니다.이 때, 시스템은 디렉토리의 작성을 시도합니다만, 통상은, 다이알로그 박스를 표시해 새로운 디렉토리를 작성할지를 유저에게 확인합니다.파일 조작중에 다이알로그 박스를 표시하지 않게 하려면 , fFlags 멤버에 FOF_NOCONFIRMMKDIR 플래그를 지정합니다.

파일명에 와일드 카드 문자를 사용할 수 없습니다.또, 파일명은 풀 패스 지정되어 있지 않으면 안됩니다.상대 패스를 사용하면, 예기치 않은 결과를 일으킬 가능성이 있습니다.

fFlags

파일 조작을 제어하는 옵션 플래그를 지정합니다. 0 또는 이하의 값을 조합해 지정합니다.

의미
0x0001 (FOF_MULTIDESTFILES)

pTo 멤버가 복수의 파일명을 지정해 있는 것을 나타냅니다.

0x0002 (FOF_CONFIRMMOUSE)

현재는 사용되지 않습니다.

0x0004 (FOF_SILENT)

경과를 나타내는 다이알로그 박스를 표시하지 않습니다.

0x0008 (FOF_RENAMEONCOLLISION)

이동, 카피, 이름의 변경의 조작에 대하고, 지정한 파일명이 벌써 존재하고 있었을 경우에는, 조작 대상의 파일에 새로운 이름을 붙입니다.

0x0010 (FOF_NOCONFIRMATION)

표시되는 다이알로그 박스로 「네」또는 「모두」를 선택하도록(듯이) 합니다.

0x0020 (FOF_WANTMAPPINGHANDLE)

FOF_RENAMEONCOLLISION 하지만 지정되어 있고, 같은 파일명이 존재했기 때문에 새로운 파일명이 붙여졌을 경우에, 낡은 이름과 새로운 이름을 포함한 매핑 오브젝트의 핸들을hNameMappings 멤버에 격납합니다.

0x0040 (FOF_ALLOWUNDO)

가능한 한 un-do 정보를 보관 유지하도록(듯이) 합니다. pFrom 그리고 지정된 파일명이 풀 패스로 지정되어 있을 필요가 있습니다.삭제시로 지정하면, 파일을 쓰레기통에 넣을 수 있습니다.

0x0080 (FOF_FILESONLY)

와일드 카드 파일명(*.*)(이)가 지정되었을 경우에게만 조작을 실행합니다.

0x0100 (FOF_SIMPLEPROGRESS)

경과를 나타내는 다이알로그 박스에 파일명을 표시하지 않습니다.

0x0200 (FOF_NOCONFIRMMKDIR)

새로운 디렉토리를 작성할 필요가 있는 경우에, 작성할지의 확인을 하지 않습니다.

0x0400 (FOF_NOERRORUI)

에러가 발생했을 경우에, 유저 인터페이스를 표시하지 않습니다.

0x0800 (FOF_NOCOPYSECURITYATTRIBS)

Version 4.71 이후: 파일의 시큐러티 속성이 카피되지 않게 합니다.

0x1000 (FOF_NORECURSION)

로컬 디렉토리에만 조작을 행합니다.서브 디렉토리에 대해서 재귀적으로 조작을 행하지 않습니다.

0x2000 (FOF_NO_CONNECTED_ELEMENTS)

Windows Me/2000 이후: 그룹으로서 접속되고 있는 파일을 이동하지 않습니다.지정된 파일만을 이동합니다.

0x4000 (FOF_WANTNUKEWARNING)

Windows Me/2000 이후: 쓰레기통에 넣는 것은 아닌 삭제 조작 시에 파일이 삭제될 때 경고를 합니다.

0x8000 (FOF_NORECURSEREPARSE)

Windows XP 이후: 리파스포인트를 컨테이너는 아니고 오브젝트로서 취급합니다.

fAnyOperationsAborted

지정한 파일 조작이 완료하기 전에 유저에 의해서 중지되었을 경우에는 1 (TRUE) 하지만 격납됩니다.그 이외의 경우에는 0 (FALSE) 하지만 격납됩니다.

hNameMappings

이동·카피·이름 변경된 파일의 낡은 파일명과 새로운 파일명을 포함한 파일명 매핑 오브젝트의 핸들이 격납됩니다.이 멤버는 fFlags멤버에 FOF_WANTMAPPINGHANDLE 플래그가 지정되었을 경우에게만 사용됩니다.이 핸들이 불필요하게 되면SHFreeNameMappings 함수로 해방하지 않으면 안됩니다.이 핸들에 관해서는, 아래의 해설을 참조해 주세요.

lpszProgressTitle

경과를 표시하는 다이알로그 박스의 타이틀 바에 사용하는 문자열의 주소를 지정합니다. fFlags 멤버로 FOF_SIMPLEPROGRESS 하지만 지정되어 있는 경우에게만 유효합니다.

해설

pFrom 멤버 및 pFrom 멤버가 풀 패스가 아닌 파일명의 경우는, 커런트 디렉토리에 있다고 보입니다.

파일 삭제시에 pFrom 멤버에 풀 패스가 아닌 파일명이 지정되었을 경우는, FOF_ALLOWUNDO 플래그를 지정했을 경우에서도 파일은 쓰레기통에 넣어지지 않습니다.

fFlags 멤버에 FOF_WANTMAPPINGHANDLE 플래그를 지정하면, hNameMappings 멤버에는 파일명 매핑 오브젝트의 핸들이 격납됩니다.이 핸들은, 멤버가 UINT 형태 및 SHNAMEMAPPING 구조체의 배열에의 포인터인 구조체의 포인터로서 취급됩니다.즉,

struct HANDLETOMAPPINGS {
    UINT           uNumberOfMappings;  // 배열의 요소수
    SHNAMEMAPPING *lpSHNameMapping;    // 배열에의 포인터
};

그렇다고 하는 구조체에의 포인터이다고 합니다(이 HANDLETOMAPPINGS 구조체는 명시적으로 정의되고는 있지 않습니다).이 때의UINT 형태의 값으로서 SHNAMEMAPPING 구조체의 배열의 요소수가 세트 됩니다.각각의 SHNAMEMAPPING 구조체에는, 변경되었다1개의 파일이 낡은 패스명 및 새로운 패스명이 격납되고 있습니다.

hNameMappings 멤버의 값으로 해서 취득한 핸들은 SHFreeNameMappings 함수를 사용해 해방하지 않으면 안됩니다.

hNameMappings 멤버에 돌려주어지는 파일명 매핑 오브젝트를 참조하는 경우, Windows 95/98/Me 그럼, SHFileOperation 함수는 ANSI 세트의 SHNAMEMAPPING 구조체를 돌려줍니다만,Windows NT/2000/XP 그럼, SHFileOperation 함수는 ANSI 판의 함수 및 Unicode 판의 함수 모두 Unicode 세트의 SHNAMEMAPPING 구조체를 돌려줍니다.따라서, 모든 버젼의 Windows 그리고 조작시키기 위해서는, Windows 의 버젼을 조사해 조건 나누어 하는 코드를 작성할 필요가 있습니다.

대응 정보

Shell32.dll Version 4.00 이후

Windows 95 이후 / Windows NT 4.0 이후

변수와의 대응

HSP 변수멤버
멤버명오프셋사이즈
shfo.0hwnd04
shfo.1wFunc44
shfo.2pFrom84
shfo.3pTo124
shfo.4fFlags164
shfo.5fAnyOperationsAborted204
shfo.6hNameMappings244
shfo.7lpszProgressTitle284








출처 : http://blog.naver.com/wjfeo42?Redirect=Log&logNo=30003505246

          http://blog.naver.com/ollbback?Redirect=Log&logNo=120040302994

 

 

파일 복사, 삭제, 이동  파일명 변경등을 지원하는 함수로써 윈도우 파일 익스플로어 에서 뜨는

파일 전송 이미지가 나오는 다이얼로그를 띄울수 있다는 점.......

 

int WINAPI SHFileOperation(LPSHFILEOPSTRUCT lpFileOp);

Parameter SHFILEOPSTRUCT

 





http://blog.naver.com/imanoos/35359421



SHFILEOPSTRUCT를 이용해서 폴더를 이동시킬 때 주의점



SHFILEOPSTRUCT shop= {0};
shop.hwnd = NULL;
shop.wFunc = FO_MOVE; //FO_COPY
shop.pFrom = strSource;
shop.pTo = strDest;

// shop.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_MULTIDESTFILES;
shop.fAnyOperationsAborted = false;
shop.hNameMappings = NULL;
shop.lpszProgressTitle = "데이타 이동";
SHFileOperation(&shop);


 

보통 위와 같이 초기화를 시키는데, 이 때 shop.pFrom 및 shop.pTo 부분에 들어가는 마지막 문자열은 항상 NULL로 끝나야 한다.

 

예)

 

TCHAR pszPath[1024];
ZeroMemory(&pszPath, sizeof(TCHAR)*1024);

strcpy(pszPath, strPath);

반응형
반응형

#define random(n)  (rand() % (n))   //난수 발생 매크로 0에서 n-1까지
#define randomR(a, b) (rand() % (b-a))+a  //난수 발생 매크로 a에서 b-1까지

 

 

 

HCRYPTPROV   hCryptProv;
 BYTE         pbData[16];
 CryptGenRandom(hCryptProv,  8,  pbData );  // 랜덤R 호출전 선언

 

 

#include <WinCrypt.h> //필요한 헤더

반응형
반응형

 

SendMessage와 PostMessage의 차이점

한구기2010-01-12 15:33:34주소복사
조회 386  스크랩 0

BOOL PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);

LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

 

두 함수의 인수는 완전히 동일하다.

여기서 Post라는 말은 순수 우리 말로는 "붙인다~"라고 번역되며

"Send"라는 말은 보낸다.라고 번역된다.

 

PostMessage 함수는 Msg인수로 지정된 메세지를 hWnd 윈도우의 메세지 큐에

집어넣어 윈도우 프로시져에서 이 메세지를 처리하도록 한다. 메세지를 큐에 넣기만하고

바로 리턴하므로 메세지를 붙인 수 즉시 다른 작업을 할 수 있지만

큐에 대기하고 있는 다른 메세지가 있으면 뒤에 붙인 메세지는 곧바로 처리되지 않는다.

큐에 붙여진 메세지는 GetMessage에 의해 읽혀지고

DispatchMessage에 의해 윈도우 프로시져로 보내져 처리될 것이다.

 

급하게 처리할 필요가 없거나 또는 지금 하고 있는 작업을 완전히 끝내야만 처리할 수 있는 메세지는

PostMessage 함수로 큐에 붙인다. 이 함수로 붙여진 메세지는 언제 처리될지 정확하게

예측하기 힘들고 그래서 붙여지는 메세지는 wParam과 lParam에는 지역 포인터를 사용하지 말아야 한다.

메세지를 붙일 시점에는 포인터가 존재했더라도 메세지가 처리될 시점에는 포인터가 무효해질 수 있기

때문이다.PostMessage는 메세지를 큐에 붙인 후 성공하면 TRUE를 리턴하며,

실패하면 FALSE를 리턴하는데 메세지 큐는 크기가 한정되어 있기 때문에 고속으로 전송되는 모든

메세지를 다 수용하지 못할 수도 있다.다행히 Win32 환경에서는 큐 크기가 대폭 늘어나면서 왠만해서는

큐가 부족한 상황이 잘 발생하지 않는다.

 

SendMessage는 메세지를 큐에 넣는 것이 아니라,

곧바로 윈도우 프로시져로 보내 즉각 처리하도록 하며,

메세지가 완전히 처리되기 전에는 리턴하지 않는다.

 

즉, 블록시켜서 SendMessage는 윈도우간 특히 부모 윈도우와 차일드 컨트롤간의 통신에 자주 사용된다.

예를 들어 리스트 박으세요~ 라는 LB_ADDSTRING 이라는 메세지를 보내면 이는 리스트 박스에게

문자열 항목을 추가하라는 명령이 되며, 항목이 완전히 추가되고 난 후에 SendMessage가 리턴된다.

 

윈도우간에 메세지를 교환할 때 어떤 함수를 사용할 것인가는,

신중하게 결정해야 하는데 대부분의 경우는 SendMessage로 보내는 것이 정석이며,

또 효율적이다. 또 WM_COPYDATA 같은 메세지는 그 특성상 반드시

SendMessage로만 보내야 하며, PostMessage로 붙여서는 안된다.

 

다시, 간략히 정리하자면

SendMessage는 당장 어떤 일을 하라는 명령이며, PostMessage는 한가해질 때 어떤 일을 하라는 신호다.

라고 생각하면 쉬울 것이다.

 

부가적으로,

PostMessage는 큐에 넣고, SendMessage는 WndProc의 case 하나를 호출하는 것과 같다.

 

출처:윈도우즈 API 정복(한빛미디어,김상형)

=====================================================================================================

SendMessage()의 응용 방법...

 

LRESULT SendMessage(

    HWND hWnd,

    UINT Msg,

    WPARAM wParam,

    LPARAM lParam

);

 

윈도우가 다른 두 객체에서 데이타 전달 또는 명령 실행에 유용하게 쓰일 수 있다.

보통 Msg에 동작의 구분 값이 들어가고

wParam과 lParam에 전달할 데이타를 넣는다.

 

그런데 데이타의 전달 뿐만 아니라,

호출 한 부분에서 데이타를 받을 때도 쓸 수 있다.

즉 A에서 B로 SendMessage를 보낼때 값을 wParam과 lParam에 넣어서 전달을 하고,

다시 B에서 A로 LRESULT에 값을 담아서 A로 다시 보낼 수 있는 것이다.

 

ClassB::sendData(){

           ...

           ::SendMessage(ClassA Windows, WM_COMMAND, (LPARAM)nInt1, (WPARAM)nInt2);

           ...

}

 

struct SData{

           int n1;

           int n2;

};

 

IMPLEMENT_MESSAGE_HANDLER(ClassA, OnCommand){

           int nValue1 = (int)wParam ;

           int nValue2 = (int)lParam

           ...

           SData data;

           ...

           pResult = (void * )&data;

           return (LRESULT)pResult ;

}

 

이때 주의해야 할 점은..

B에서 A로 보낼때는 값을 stack에 들어가서 전달 과정에서 사라지지 않게 하는 것이 중요하다.

즉 void * pResult 가 함수내에 선언되어있다면 함수가 리턴되면서 사라질 수 있기 때문에

멤버 변수로 선언하거나, static으로 선언해서 사라지지 않도록 해야 한다.

 

================================================================================================

 

WPARAM과 LPARAM의 역할  API 

2006/05/24 12:19

복사 http://blog.naver.com/samyoung79/110004541846


LPARAM과 WPARAM은 메세지에 대한 추가적인 정보를 가지고 있다.
예를 들어 키보드를 눌렀을 경우 메세지가 발생하는데
무슨 키를 눌렀는지 2개의 키를 동시에 눌렀는지등등..
마우스를 클릭햇을 경우도 메세지가 발생하는데
어느 위치에 눌렀는지 등등

LPARAM은 보통 위치정보,
WPARAM은 보통 어떤키를 눌렀는지 중복키를 눌렀는지 정보, resource의 발생정보

 

(표1) 기본적인 마우스 메시지

  메시지

  내용

 lParam

 wParam

 WM_LBUTTONDOWN

좌측 마우스 버튼을 눌렀을때

 마우스 위치

 키상태정보

 WM_LBUTTONUP

좌측 마우스 버튼을 띄었을때

 마우스 위치

 키상태정보

 WM_RBUTTONDOWN

우측 마우스 버튼을 눌렀을때

 마우스 위치

 키상태정보

 WM_RBUTTONUP

우측 마우스 버튼을 띄었을때

 마우스 위치

 키상태정보

 WM_MOUSEMOVE

마우스가 움직일때

 마우스 위치

 키상태정보

 

마우스가 클릭하였을 때 마우스에 대한 메시지가 발생되는데 클라이언트 영역과 비 클라이언트 영역에 분리되어 다르게 나타난다.

클라이언트 영역이란 윈도우 타이틀바와 외곽선을 제외한 즉 실제로 WM_PAINT메시지에 의해서 그래픽을 출력하는 영역을 말한다.

마우스가 눌려졌을 경우에는 어느 부분이 눌려졌다는 정보를 보내주어야 한다.

이 정보를 lParam에 기록한다.

lParam은 32비트 LONG형이다.

이 형에 마우스 정보를 입력하여 알려주게 된다.

우리는 보통 위치 좌표를 사용할 경우 2차 평면에서는 (X,Y)를 사용한다.

즉 정보값이 최소 2개라는 의미이다. 헌데 lParam은 한 개의 정보 저장 장소이다.

이 저장장소에 두 개의 값을 저장하기 위해서 lParam을 16비트씩 잘라서 상위 16비트에는 Y좌표를 하위 16비트에는 X좌표값을 넣어서 보내준다.

lParam안에 X,Y의 자표값이 나누어져 들어가 있기 때문에 우리는 이 값을 분리해야 한다.

다행히도 윈도우 lParam의 값에서 상위 16비트를 얻고자 한다면 HIWORD함수를 사용해야 한다.

 

int y = HIWORD(lParam)  int x = LOWORD(lParam)

 

좌측 마우스 버튼의 클릭 메시지 처럼 우측 마우스 버튼 클릭 메시지도 같은 방법으로 lParam에 위좌표를 넣어서 전송한다.

 

마우스 더블클릭메시지

더블 클릭이란 연속적으로 빠르게 마우스 버튼을 두번 클릭하는 것이다.

이 메시지를 이벤트를 받기 위해서는 WNDCLASSEX의 멤버변수 style안에 CS_DBCLKS 삽입시킴으로서 이벤트를 받을수 있다.

이 값을 대입하기전 CS_DBLKS라는 값을 대입하면 연속적으로 두번 눌렀을 경우 WM_LBUTTONDOWN이 연속적으로 들어오는 것과 동시에 WM_LBUTTONDBLCKS가 발생

이 더블 클릭 메시지는 사실 시스템에서 발생하는 메시지가 아니라 어플리케이션에서 조합하여 만든 메시지이다.

어느 일정한 시간 구간안에서 WM_LBUTTONDOWN이 발생하면 이것을 WM_BUTTONDBLCKS로 바꾸어서 메시지 큐에 삽입하기 때문이다.

이때 사용하는 함수가 GetMessageTime인데 이 함수는 현재 메시지 큐에서 가장 최근에 얻은 메시지의 시간을 얻는 함수이다.

이 함수를 통해서 어느 시간 사이에 WM_LBUTTONDOWN 메시지가 연속적으로 발생하였을때 이 메시지와 함께 WM_BLUTTONDDBLCLK로 발생하게 되는것이다.

이런 이유로 더블클릭 메시지는 WM_LBUTTONDOWN이나 WM_RBUTTONDOWN과 같이 lParam과 wParam에 저장되는 정보의 값은 같다.

어플리케이션을 만들 경우 좌측 마우스 버튼을 눌렀을때와 더블 클릭을 했을 경우 이 두개를 분리하여 처리할 경우가 있다.

그러나 메시지는 더블 클릭했을 경우 두개의 메시지가 동시적으로 발생하기 때문에 이것을 막기는 어렵다.

 

 

 

비클라이언트 영역

 

  메시지

  내용

 lParam

 wParam

 WM_NCLBUTTONDOWN

좌측 마우스 버튼을 눌렀을때

 마우스 위치

 Hit-test정보

 WM_NCLBUTTONUP

좌측 마우스 버튼을 띄었을때

 마우스 위치

 Hit-test정보

 WM_NCRBUTTONDOWN

우측 마우스 버튼을 눌렀을때

 마우스 위치

 Hit-test정보

 WM_NCRBUTTONUP

우측 마우스 버튼을 띄었을때

 마우스 위치

 Hit-test정보

 WM_NCMOUSEMOVE

마우스가 움직일때

 마우스 위치

 Hit-test정보

 

 

[출처] WPARAM과 LPARAM의 역할|작성자 동키

반응형
반응형

http://blog.naver.com/ljyhs?Redirect=Log&logNo=30014607134







HRESULT는 32비트 signed 형 정수이다.

 

COM의 규정에 의한 것은 아니지만, COM 인터페이스에 소속된 대부분의 함수들은 HRESULT

 

형태의 반환값을 사용한다. 그러나, 이렇게 대부분의 COM 인터페이스에 소속된 함수들이 반

 

환형으로 HRESULT를 사용함으로써 클라이언트에게 일관성 있는 함수의 상태 정보를 전달 할

 

수 있게 된다. 이 반환 값을 사용하기 위해서는 SUCCEEDED나 FAILED 매크로 를 사용해야

 

한다.

 

* HRESULT 형의 구조

 

 0~15 비트 : RETURN CODE 
 

 16~28 비트 : FACILITY 

 29~30 비트 : R 

 31 비트 : SEVERITY ( SEVERITY(31비트)는 함수의 수행의 성공과 실패를 알려주는 코드다.)

 

 

* HRESULT의 상태 정보 요약.

 

  S_OK : 때때로 Boolean TRUE 값(0X0)으로 S_FALSE와 함께 사용되며 함수가 성공하였음을 의미한다. 

 NOERROR : S_OK와 동일한 의미이다. 

 S_FALSE : S_OK와 반대로 Boolean FALSE 값(0X1)으로 함수가 실폐하였음을 의미한다. 

 E_UNEXPRCTED : 치명적인 실패를 의미한다. 

 E_NOTIMPL : 멤버 함수에 구현 코드가 포함되어 있지 않다. 

 E_OUTOFMEMORY : 필요한 메모리를 할당할 수 없음 

 E_INVALIDARG : 하나 혹은 그 이상의 인자가 타당하지 않음 

 E_NOINTERFACE : 요청한 인터페이스를 지원하지 않음 

 E_POINTER : 타당하지 않은 포인터 

 E_HANDLE : 타당하지 않은 처리 

 E_ABORT : 작동 중지 

 E_FAIL : 특정하지 않은 실패 

 E_ACCESSDENIED : 일반적 접근이 금지된 에러

 

 

 

부분출처 : www.pnteam.net/419

 

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

 

LRESULT 은 long 형 타입으로서, WIn32 환경에서 메시지 처리를 마친 후 O/S에게 어떤 신호

 

를 주기 위해 사용되는 값이다.

 

각각의 메시지 case에 대한 처리 값이 때로는 단순히 -1, 0, 1등으로 어떨때는 비트 플래그로

 

O/S가 알 수 있도록 설정한다.

 

대부분의 경우 0을 리턴하면 내가 모든 메시지를 처리할테니 O/S는 더이상 메시지에 대한 처

 

리를 하지 말라는 것으로 인식, 간혹 -1의 경우 O/S가 진행하던 작업을 취소시키는 의미를 가

 

질 때도 있다.

반응형
반응형

http://blog.naver.com/gunner98?Redirect=Log&logNo=110018669558







첨부파일 (2)

우리가 현재 실행되고 있는 프로그램의 현재 작업 디렉토리를 알아 오기 위해서

위해서 "GetCurrentDirectory()"를 주로 이용합니다.

그럼 아주 쉽게 프로그램의 작업 디렉토리를 알아 옵니다.

 

여기에서 프로그램이 .dll 혹은 .ocx를 안고 있다면 좀 생각해 보아야 할 상황이

생길 수가 있습니다.

 

먼저, 프로그램에서 "GetCurrentDirectory()"함수를 이용해서 해당 경로에서 검색을

한다고 할 때, 검색할 폴더의 경로를 GetCurrentDirectory()함수를 통해서 얻어진

경로에 해당 파일을 찾고자 할 때 문제가 발생할 수가 있습니다.

 

그 문제는 .dll 혹은 .ocx에서 SetCurrentDirectory()함수를 이용해서 현재 Process의

Current Directory를 바꾸어 버리면, 프로그램에서 GetCurrentDirectory()함수를 통해

얻어지 경로는 .dll 혹은 .ocx에서 SetCurrentDirectory()함수를 이용해서 설정한 폴더로

바꾸어 버립니다. 그래서 프로그램에서는 엉뚱한 경로에 가서 해당 파일을 찾다가  못

찾았다고 할 것입니다.

 

위의 문제를 해결하기 위해서는 프로그램에서 상대경로를 사용하고 있기 때문에 위와

같은 문제를 발생할 수가 있습니다. 그래서 절대경로를 이용하는 방법이 있을테고,

다른 방법은 .dll 혹은 .ocx에서 SetCurrentDirectory()함수를 사용하지 않는 것입니다.

 

이 문제를 한번 재현해 보고 싶어서 Sample Source를 짜 보았습니다.

정말 우리가 예상했던 결과대로 Current Directory가 바뀌어 버리네요..^^

 

[ 실행 화면 ]

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

< 그림 1. 프로그램의 작업 디렉토리 경로 알아오기 >

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

< 그림 2. dll에서 Current Directory 변경하기 >

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

< 그림 3. dll에서 Current Directory 변경한 후에 다시 작업 디렉토리 알아오기 >

 

위의 결과를 보면 알 수 있듯이, 처음에 GetCurrentDirectory()함수를 이용해서 작업 디렉토리

경로를 알아 오니 "C:\test\Smaple"였습니다.

dll에서 SetCurrentDirectory()함수를 이용해서 현재 Current Direcotory를 "D:\TRACE\UCM"로 변경하였습니다.

그런 다음 다시 프로그램에서 GetCurrentDirectory()함수를 이용해서 작업 디렉토리 경로를

알아 오니 "D:\TRACE\UCM"로 바뀌어져 있는 것을 확인할 수가 있습니다.

반응형
반응형

 

.inl 파일을 헤더파일 하단에 붙일때 inline 은 선언부에 써도 되고 안써도 되지만 정의부에는 inline 키워드를 써야한다

 

 

이하 정리잘된 포스트!!!!!

 


 

 

http://dev.log.mumbi.net/390

 

[C++] inline keyword 와 inl file. ( *.inl )

Language/C++ | 2009/08/24 14:21 | Posted by 임준환( 멈비 )
inline 함수는 함수를 호출하는게 아니라 마치 #define macro 와 같이 치환되어 함수 호출에 대한 overhead 를 줄일 수 있어 좀 더 빠른 처리가 가능하다. 

하지만 inline 함수는 사용자가 지정한다고 해서 inline 함수가 되는 것이 아니라 사용자가 compiler 에게 inline 함수로 사용하겠다는 신청을 하면 compiler 가 inline 함수의 조건에 맞으면 inline 함수로 처리하고 아니면 일반 함수로 처리하게 된다. 

inline 함수의 조건이란 7 line 이하이어야 하고, 재귀 호출과 같은 반복되는 함수는 제외된다.


함수를 inline 함수로 신청하기 위해서는 
  1. 함수의 선언과 동시에 정의를 한다. ( inline keyword 를 붙이지 않아도 된다. )
  2. 함수의 선언 앞에 inline keyword 를 붙인다. 단, 함수의 정의는 선언부와 같은 file 안에 있어야 한다.

 
그러므로 inline 함수는 cpp file ( *.cpp ) 에 정의할 수 없는 것이다. 


위의 사항으로 비추어 보면 선언과 정의가 같은 file 안에 있어야 하므로 선언과 정의를 분리하기 어려워진다.

그래서 등장한 기법이 inl file ( *.inl ) 이다.

inl file 에는 inline 함수의 정의부를 적어 놓고 선언부가 있는 header file ( *.h ) 의 끝에서 #include 로 inl file 을 포함해 주면 된다.

단, 여기서 주의할 점은 선언과 동시에 정의를 했을 경우에는 inline keyword 를 붙이지 않아도 되지만, 그렇지 않으므로 꼭 inline keyword 를 붙여야 한다. 


inl file 이 또 하나 유용한 점이 있는데 바로 template 을 작성할 때이다.

template 또한 inline 함수와 마찬가지로 선언과 정의가 같은 file 에 있어야 한다.

그러므로 template 의 선언은 header file 에 작성하고, 정의는 inl file 에 하면 선언과 정의를 분리할 수 있게 된다.

반응형
반응형

출처 : http://blog.naver.com/kzh8055/140062582343

 

 


[ Effective C++ ] 암시적 타입 변환( Implicit Type Casting )  Effective !!! 2009/02/06 21:51

 

 
흠...

 

이번 글은 암시적인 타입 변환에 대해서다.

사실 EC++ 에 '암시적인 타입변환'이란 항목 자체가 있는건 아니지만 책 전반에 걸쳐

계속 언급돼는 중요한 내용이고 간혹 헷갈리는 부분도 있고해서 정리하고 넘어가야겄다.

 

암시적 타입 변환 VS 명시적 타입 변환

 

암시적( Implicit ) 과 반대돼는 개념은 명시적( Explicit ) 일것이다.

명시적은 말그대로 어떤 의도를 대놓고(?) 나타낸다는 의미이고

그렇다면 명시적 타입변환( Explicit Type Casting )이란

어떤 타입을 또 다른 타입으로 바꾸는데 있어 사용자가 직접 그 타입 변환을 수행하는

캐스팅 명령어를 코딩하는 경우라 볼수 있다.

 

비교적 타입 제한에 관대한 C 언어에서는 명시적 캐스팅 방법 달랑 하나로

거의 대부분의 경우 이방법을 사용해 모든 타입으로의 캐스팅이 가능하다.

- 심지어는 호환돼는 타입이 아닐경우라도( 실수형에서 문자형으로의 타입 변환등 ) 가능하다 ㅡ,.ㅡ

 

float radian= 3.14

int a = ( int )radian;              // C 스타일의 ( 명시적인 )캐스팅< float 에서 int 로 >

 

그럼 C++ 에서 제공하는 타입 변환자 는 어떠한가?

타입 검사에 있어선 한층 까탈스러워진 C++ 의 경우 각각 용도에 맞는 타입변환자가

4 개 씩이나 준비 돼있다. 그게 바로 static_cast, const_cast, dynamic_cast, reinterpret_cast 이다.

- 이것에 대한 자세한 설명은 책이나 웹 상에 널려있는 정보를 활용해라

 

하여간 여기서 중요한 부분은 명시적 타입 변환은 사용자( 프로그래머 )의

타입변환 의도가 직접 코드에 반영( 타입 변환자 등 으로 )돼 있다는 것이다.

- 쉽게 말해 캐스팅 연산을 적용했다는거다.

 

그러면 이와는 반대인 암시적인 타입 변환은 뭔가?

위의 맥락 상으로는 타입 변환자 등 타입을 캐스팅하기 위한 부분이 코드상에

( 명시적으로 ) 반영돼지 않았음에도 불구 하고 자동으로 타입 변환이 이뤄지는것을 말할것이다.

 

사용자가 작성한 코드에는 타입 변환 의도가 없었음에도 자동으로 타입변환이 발생한다면

과연 이것은 누구의 소행일것인가?

 

범인( ? )은 바로 컴파일러다. 컴파일러는 일련의 규칙에 따라( 컴파일러 제작자가 의도한 )

상황에 맞다고 생각하면 기특하게도 사용자가 구지 시키지도 않은 타입변환을

지가 스스로 수행하는 것이다.

 

흔히 발생하는 암시적인 타입 변환의 예를 들어 봅시다.

 

class Base{ ... };                          // Base 클래스

class Derived : public Base{ ... };   // Base 로 부터 상속 받은 Derived 클래스

 

...

/* Client Code */

 

Base* pBase = new Base;     // 오케바리! 동일한 타입의 객체를 동일한 타입의 포인터로 가르킴

pBase = new Derived;            // 이 또한 이상 무! 자식 객체는 부모 객체의 포인터로 가리킬수 있다

 

C++ 을 비롯한 객체 지향언어를 사용해본 인간이라면

 

[ 부모 타입 포인터 ] = [ 자식 타입 객체( 주소 ) ];

 

와 같은 형태의 코딩을 수없이 해왔을것이다.

사실 따지고 보면 엄연히 두 타입이 서로 같지 않은 셈이니

C++ 컴파일러가 왠지 타입 불일치 에러를 뱉어낼 듯 하나

희안하게 아무련 불평없이 깔끔히 컴파일 된다.

 

위 코드가 전혀 문제 없이 컴파일돼는 이유는 앞서 말한' 일련의 규칙 '에 따라 컴파일러가

알아서 캐스팅을 수행하기 때문이다.

 

그러니깐 Base( 기반 클래스 )타입 포인터로 Drived 객체( 파생 클래스 )를 가르키는 상황은

컴파일러 입장에선 자신이 갖고 있는 ' 일련의 규칙' 에 포함돼 있다는 소리다.

 

또 다른 예를 들어 봅시다.

어떤 상수 타입의 객체에 대한 참조( 또는 포인터 ) 는 초기화 될시

비 상수 객체를 가르킬수 있다.

 

class CObject{ ... };

 

CObject nonConstObject;                                // 비 상수 객체

 

const CObject* cpObject = &NonConstObject;    // 상수 객체를 가르키는 포인터로 비상수 객체를

                                                                    // 가르켰지만 이상 없이 통과!

 

요것 또한 컴파일러의 ' 일련의 규칙 '

- 즉, 상수 객체를 가르키는 포인터( 참조 )를 초기화 할시 비 상수 객체를 이용하는 것이 가능하다 

에 포함돼니 컴파일러가 발벗고 나서 자동적으로 캐스팅한 결과 이상없이 컴파일돼는 것이다.

 

 

암시적인 타입 변환에 사용자가 개입(?)

 

컴파일러에 내장된( 즉 하드 코딩된 ) 암시적 타입변환에 대한 규칙 자체는 바꿀수 없다.

그러나 프로그래머가 몇가지 방법을 이용해

컴파일러가 암시적인 타입변환을 할수 있게 끔 힌트를 줄수도 있는데

그 중 하나가 암시적 타입 변환 연산자라고 하는 것이다.

 

이 연산자의 형태는 operator 캐스팅할 타입() 인데

보다시피 반환 값 타입이 존재하지 않고 operator 키워드 옆에 달랑 타입( 캐스팅 ) 만

덩그러니 놓여져 있다.

 

바로 예제로 들어갑시다.

 

//---------------------------------------------

//   클래스 CTest 

//---------------------------------------------

class CTest

{

   public:
      operator float()
      {   return static_cast< float >( m_Var );   }
   private:
      static const int m_Var = 5;
};

 

 

/* Client Code */

 

float Result = 0.0f;
const float Radian = 3.14f;

 

Result = Radian * CTest();

 

실수형 타입인 Radian 을 CTest 객체( 임시 객체 ) 와 곱해

역시 실수형인 Result 변수에 그 결과를 대입하는 실질적인 의미가 전혀 없는 예제이다.

 

컴파일 에러가 발생하지 않는게 오히려 이상한 상황으로 보이겠지만

위 코드는 아무 문제 없이 컴파일이 성공한다.

그럼 과연 위의 코드가 컴파일돼면서 어떤 일들이 벌어졌는지 대충 살펴 봅시다.

 

실질적으론 연산이 이뤄지는 세번째 줄을 보면

첫번째로 컴파일러는 Radian 변수와 CTest 객체간의 곱셈을 시도하려 할것이다.

 

컴파일러는 Radian 의 타입이 float 실수형이 므로 당연히 두번째 피 연산자

즉, 곱셈 연산자( * ) 의 오른쪽 항은 당연히 float 가 될것이라고 예상하지만

안타 깝게도 오른쪽 항엔 왠 CTest 타입의 객체가 자리를 잡고 있다.

 

당황스런(?) 컴파일러는 이제부터 바쁘게 CTest 타입이 float ( Radian 의 타입 ) 타입으로

변환 될수 있는지 알아보러 다니기 시작한다.

 

맨 먼저 컴파일러에 내장된 암시적 타입 변환이 일어 날수 있는 규칙 을 살펴보지만

거기에 사용자 정의 타입인 CTest 에 관한 부분이 있을 턱이 없다.

 

그 다음으로 찾게 돼는 부분이 바로 ( 피연산자에 대한 )암시적 타입변환 연산자 이다.

다행히도 operator float() 란 놈이 있으니 컴파일러는 이제야 안도의 한숨을 쉬며

잽싸게 CTest 객체를 float 형으로 변환하고 무사히 위의 연산은 수행된다.

 

 

매개 변수가 하나인 생성자

 

컴파일러가 암시적인 타입변환을 할수 있게 끔 빌미( ? )를 제공하는 두번째 방법은

매개 변수 를 하나 받는 생성자를 정의 하는 것이다.

 

* 이제 부터 사용할 예는 EC++ 항목 24의 것을 거의 그대로 가져오는것이다.

 

//-------------------------------------

//   유리수를 추상화한 Rational 클래스

//-------------------------------------

class Rational

{

   ...

   //----------------------------------

   //   생성자

   //----------------------------------

   public:

      Rational( int numerator, int denominator = 1 );     // 분자, 분모( = 1 )를 취하는 생성자

   ...

   //----------------------------------

   //   연산자 재정의

   //----------------------------------

   public:

      void operator=( const Rational& rhs );                // 대입 연산자

      void operator*( const Rational& rhs );                // 곱셈 연산자

   ... 

};

 

유리수 체계를 추상화한 Rational 클래스는

생성자의 인자로 분자, 분모에 해당하는 정수를 받는다.

그런데 보다시피 분모인 denominator 는 디폴트 값 1을 취하므로

기본적으로 이 생성자는 하나의 인자를 받는것으로 볼수 있다.

Rational 클래스를 사용하는 예는 아마도 다음과 같을것이다.

 

Rational a( 3, 4 );    // 유리수 3 / 4

Rational b( 2 );        // 유리수 2 / 1, 즉 그냥 정수 2

 

일반적인 상식적으로 정수와 유리수와의 연산( 곱셈, 덧셈 등 )은 전혀 이상한 일이 아니다.

 

b * 3;

 

그러니깐 위와 같은 일,

유리수 b( 사용자 정의 )  와 정수 '3' ( 기본 형 )를 곱하는 것이 유효한 식이

아무래도 자연스럽다는 것이다.

 

상식적으로 보자면 둘의 타입은 전혀 호환 될것 같아 보이지 않아서

위의 식은 보기 좋기 실패할것 같지만 컴파일러에겐 유효한 식이다.

그럼 과연 뭐가 어떻게 돌아가길래 위 식이 성공하는지 살펴 봅시다.

 

일단 컴파일러 입장에서 위 식은 다음과 같이 해석된다.

 

b.operator*( 3 );    // b( Rational 클래스 )의 operator '*' () 이 호출

 

문제는 Rational 클래스의 operator '*' 는 정수를 취하지 않는다는것이다.

 

      void operator*( const Rational& rhs );                // 곱셈 연산자

 

보다시피 Rational 클래스의 정의된 operator '*' 연산자는 인자로 Rational 객체의 참조를 받고 있다.

유리수 객체( 참조 )가 들어갈 자리에 정수를 넣었는데도 컴파일 상 아무런 문제가 없다는것은

결국 컴파일러란 놈이 정수를 유리수 객체 타입으로 암시적 캐스팅을 수행했다는 것이다.

 

그럼 대체 어떻게 정수( int )가 유리수( Rational ) 객체로 탈바꿈 할수 있는것인가?

 

범인은 바로 요놈이다.

 

      Rational( int numerator, int denominator = 1 );     // 분자, 분모( = 1 )를 취하는 생성자

 

앞서 말했다시피 매개 변수가 하나인 생성자는 컴파일러가 암시적인 타입변환을 수행하는데

힌트를 제공한다고 했다.

 

이 생성자는 정수를 Rational 타입으로 바꾸는 역할도 수행한다.

 

즉, 매개변수가 하나인 생성자는 그 매개 변수 타입을 자신의( 생성자가 속한 ) 클래스 타입으로

변환 가능하다는 조건을 컴파일러에게 제공한다는 얘기다.

 

그러니까 결과적으로 위의 b * 3 이란 식은 컴파일러가 다음과 같이 해석한다는 거다.

 

       b.operator*( Rational( 3 ) );

 

그렇다고 ' 그렇다면 정수 타입은 무조건 Rational 객체로 바뀔수 있는 거군. 훗... 역시 난 천재'

라고 생각하면 좀 곤란한다.

 

위의 ' 매개 변수가 하나인 생성자를 통한 타입 변환 '이 성공하기 위해선 한 가지 조건이 필요하다.

- 뭐 앞에서 설명하는 과정에 이미 들어갔지만

 

그 조건은 바로 ' Rational 객체를 매개 변수로 갖고 있는 연산자가 호출될 경우' 이고

정수 타입의 인자가 Rational 타입인 매개 변수에 전달될때

비로서 정수 타입이 Rational 타입으로 캐스팅 된다는 것이다.

 

앞서 봤다시피 b * 3 은 우선 ' b.operator( 3 ) ' 으로 해석된다고 했는데

이때 operator '*' 는 const Rational& 타입을 인자로 취한다고 했다.

이상태에서 정수( 3 )은 const Rational& 타입의 임시 변수에 대입돼는데

- 인자 전달 매커니즘 상

이때야 비로소 ' Rational( int ) ' 생성자가 개입(?)해 정수 형을 Rational 타입으로

바꿀수 있다는 것이다.

 

가령, b * 3 을 뒤집어 3 * b 를 만들어 버리면 결과는 어떻게 될것인가?

 

어떻게든 될거라고 생각 했겠지만 얄짤없이 컴파일 실패가 발생한다.

 

위에서 정수(타입)은 Rational 타입으로 암시적 변환된다고 했지만( Rational( int ) 에 의해 )

전제 조건을 만족 시키지 못했다. 즉, 정수 3 이 Rational 객체로 변환 되기 위해선

그 자리에 Rational 객체 타입을 인자로 받는 operator '*' 가 호출되야 한다.

 

그러니까 이런 놈이 필요하다고 쩝...

 

const Rational operator*( const Rational& Arg1, const Rational& Arg2 );

 

그러면 컴파일러의 해석상 operator*( 3, b ) 이렇게 될것이고

 

3 이 전달될 자리의 매개 변수 타입이 const Rational& 이므로 적법한

( 암시적인 )타입 변환이 발생해 위식은 무사히 컴파일 된다.

- 여기서 b 야 원래 Rational 객체니 두말하면 잔소리고

 

정수 3이 들어갈 자리에 Rational 객체를 받는 '*' 연산자가 필요하다고 했는데

일단 이 '*' 연산자는 멤버 '*' 연산자던 , 전역 '*' 연산자던 상관 없지만 3 이 클래스가 아니므로

저 식이 유효하려면 이경우 무조건 전역 '*' 연산자 만 가능하다.

 

만일, 매개 변수가 하나인 생성자가 암시적 타입변환에 대한 빌미를 제공하는 것에 대해

불만이 있는 사람이라면 ' explicit ' 키워드를 생성자 앞에 붙이도록.

 

      explicit Rational( int numerator, int denominator = 1 );     // 분자, 분모( = 1 )를 취하는 생성자

 

이러면 앞서 가능했던 부분( int 를 Rational 로 )이 불가능해진다.

즉, b * 3 이란 식은 가차없이 컴파일 에러로 지목된다는 말이다.

 

물론 암시적 타입 변환이 좋을 마냥 좋을 경우만 있는 것은 아니므로 explicit 키워드로

막아야 될 때도 있을것이니 알아서 상황에 맞게 조율하도록 합시다.
 

반응형
반응형


그냥 아래처럼 다른것도 응용해서 사용하면 된다

 

std::ostream& operator<<( std::ostream& cout, D3DXVECTOR3 v ){

 return cout;
}

반응형
반응형

http://bgashin.tistory.com/127

 

 

 

 

UX - 유저 경험 기반의 인터페이스 작성.
유저들이 경험한 장점, 단점을 가져와서 UI를 만드는 작업이다.
이것에 관련된 연구가 한창 진행중이며, 책들도 나오고 있다. 게임에 이것을 사용하는 것은 매우 중요하다.

시계 바늘과 눈금이 어긋남
- 3.14대신,  표준 원주율을 써서 구한다.
#define _USE_MATH_DEFINES
#include <math.h>
M_PI를 3.14대신에 쓴다.
 



WM_KEYDOWN:
키보드가 눌러졌는지 상태 값만 본다. Ex)A를 눌렀으면 눌렀다는 값만ㄷ ㅡㄹ어옴
WM_CHAR:
문자를 받는다.
이 부류는 게임에서 잘 안쓴다. 쓰는 공간이 한정되어 있고, 뗏다 눌렀다를 체크가능하기 때문이다.
----------
하지만 게임에서는 얘네들 보다는 GetAsyncKeyState를 쓴다.
눌렀는지, 뗐는지, 누른중인지 체크한다. 아무곳에서나 체크 할 수 있다.
#define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000 ? 1 : 0))
#define KEYUP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000 ? 0 : 1))

Wparam은 어떤 값이 들어오있는지 lparam은 상태
마우스 x는 하위비트 lparam
마우스 y는 상위비트 lparam
버튼에는 wparam 값을 넣고 Button & MK_SHIFT 쉬프트 눌린상태로 버튼이 눌러졌느냐?

더블유엠페인트에서 겟디시쓰면 안되는 이유. 페인트는 반다 그렸다는 반환값을 돌려주지만 겟디씨는 반환값이 없어서 무한루프를 돈다.
 

반응형
반응형

#include <iostream>
using namespace std;

class A{
public :
 virtual void initialize(){
  cout<<"base initialize"<<endl;
 } 
 virtual float integrate(float dt){ return 0;}  
};

class B : public A{
public :
 void initialize(){
  
 }
 static float integrate(float dt){
  cout<<"derived initialize"<<endl;
 }
};


int main(){

 B derived;

 A* pBase= &derived;

 pBase->initialize();                             //A 클래스의 initialize() 가 호출된다

 return 0;
}

 

B 의  static float integrate(float dt)  ==>  float integrate(float dt) 로 고치면 당연히 B 의 initialize() 가 호출된다

 


반응형
반응형

잘 정리된 포스트

const_cast 는 포스트 하지 않았는데 간단하기 때문이다

상수성 const 를 제거한다


아래는 포스트 내용

 

 

http://prostars.tistory.com/55 

 

 

C++의 4가지 캐스트 연산자에 대한 이야기 중 두 번째다.
이번은 그중에서 dynamic_cast 에 대해서 이야기한다.

dynamic_cast 는 상속 관계 안에서 포인터나 참조자의 타입을 기본 클래스에서 파생 클래스로의 다운 캐스팅과 다중 상속에서 기본 클래스 간의 안전한 타입 캐스팅에 사용된다.
안전한 타입 캐스팅이란 런타임에 타입 검사를 한다는 것이며 아래에 조금 더 자세하게 나온다.
const_cast와 같이 다른 용도로는 사용하지 못하며 용도가 명확하다.
참고로 dynamic_cast 를 사용하려면 기본적으로 다형성은 이해를 하고 있어야 하며 RTTI도 이해하고 있다면 이 글을 볼 필요가 없을 것이다.


객체가 위치한 메모리의 시작부분을 찾는 데도 사용된다는데 사용해 본 적이 없다.
객체를 void* 로 dynamic_cast 하면 시작 주소가 나온다.
[예] void* p = dynamic_cast<void*>( ObjectPointer );

 

- dynamic_cast 사용
dynamic_cast 를 사용하기 전에 제약 사항을 확인하자.
나름 제약 사항이 많다.

•상속 관계 안에서만 사용할 수 있다.

•하나 이상의 가상함수를 가지고 있어야 한다.
•컴파일러의 RTTI 설정이 켜져 있어야 한다.

[예제 1]
class Base
{
public :
    virtual void Put( void ) { cout << "Base" << endl; }
};

class Derived : public Base
{
public :
    void Put( void ) { cout << "Derived" << endl; }
 };

int _tmain(int argc, _TCHAR* argv[])
{
    Base* pBase = new Base;
    Base* pDerived1 = new Derived;
    Derived* pDerived2 = new Derived;
    Derived* pDerived = NULL;

    // 컴파일 오류 : 타입 변환을 할 수 없다.
    //pDerived = pBase;

    // 컴파일 성공 : 런타임에 타입 변환에 실패하며 널을 리턴한다.
    pDerived = dynamic_cast<Derived*>( pBase );        
    if ( pDerived == NULL )
        cout << "Runtime Error" << endl;

    // 컴파일 오류 : 타입 변환을 할 수 없다.
    //pDerived = pDerived1;

    // 컴파일 성공 : 런타임에 타입 변환에 성공하며 Derived 타입의 포인터를 리턴한다.
    pDerived = dynamic_cast<Derived*>( pDerived1 );
    if ( pDerived )
        pDerived->Put();

    // 컴파일 성공 : 이런 경우에는 캐스팅이 필요 없다.
    pDerived = pDerived2;
}


위의 [예제 1] 에서 dynamic_cast 의 기본 동작을 볼 수 있다.
[예제 1] 에서 중요한 내용은 'pDerived = dynamic_cast<Derived*>( pBase );' 에서 볼 수 있듯이 포인터가 실제로 가리키는 대상이 기본 클래스의 객체라면 변환은 실패한다는 것이다.
dynamic_cast 가 캐스팅해주는 것은 포인터나 참조자의 타입을 다운 캐스팅하는 것이지 객체의 타입을 캐스팅하지는 못한다.

dynamic_cast 는 캐스팅에 실패할 때 대상이 포인터라면 널을 리턴하고 참조자였다면 bad_cast 예외를 던진다.
이것이 위에서 언급한 '안전한 타입 캐스팅'의 의미다.
A에서 B로 포인터의 타입을 캐스팅하는 것이 문제없는지 런타임에 검사하여 처리할 수 있다.

다중 상속의 상황에서 기본 클래스 간의 타입 캐스팅을 보자.


[예제 2]
class BaseOne
{
public :
    virtual void Put( void ) { cout << "BaseOne" << endl; }
};

class BaseTwo
{
public :
    virtual void Put( void ) { cout << "BaseTwo" << endl; }
};

class Derived : public BaseOne, public BaseTwo
{
public :
    void Put( void ) { cout << "Derived" << endl; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    BaseOne* pBaseOne = NULL;
    BaseTwo* pBaseTwo = new Derived;

    // 컴파일 오류 : 타입 변환을 할 수 없다.
    //pBaseOne = pBaseTwo;

    // 컴파일 성공 : 런타임에 타입 변환에 성공하며 BaseOne 타입의 포인터를 리턴한다.
    pBaseOne = dynamic_cast<BaseOne*>( pBaseTwo );
    if ( pBaseOne )
        pBaseOne->Put();
    
    return 0;
}


[예제 2] 는 아래 [그림 1] 처럼 다중 상속 관계의 클래스 구성에서 기본 클래스 간의 타입 캐스팅을 보여준다.

 

 

사용자 삽입 이미지
[예제 2] 와 같은 캐스팅의 한 사용 예로는 각 기본 클래스 포인터(또는 참조자) 타입의 컨테이너 혹은 배열을 운용하면서 서로 간의 요소를 교환할 때 사용할 수 있다.

[예제 1] 과 같은 캐스팅을 다운 캐스팅이라고 하며 [예제 2] 와 같은 캐스팅을 크로스 캐스팅이라고 한다.
별도로 적지는 않았지만 파생 클래스에서 기본 클래스로의 캐스팅을 업 캐스팅이라고 한다.
업 캐스팅은 캐스팅 연산자가 있으나 없으나 안전하게 캐스팅된다.

- 참고 사항
다중 상속이 발생한다면 설계를 다시 검토하라는 말이 있듯이(있나..-_-?) 다중 상속은 그리 권장하지 않는 방식이다.

 


http://prostars.tistory.com/65  중에서..

 

 - reinterpret_cast 사용
reinterpret_cast 를 사용하기 전에 용도와 제약 사항을 확인하자.
전혀 관계없는 타입 간의 변환
상속관계가 없는 클래스 간의 변환 포함
const_cast 의 기능은 수행하지 못함

 

 


http://prostars.tistory.com/64

 

++의 4가지 캐스트 연산자에 대한 이야기 중 세 번째다.
이번은 그중에서 static_cast 에 대해서 이야기한다.

static_cast 는 기본적으로 C 스타일의 캐스팅과 가장 비슷한 기능을 한다.
물론 C 스타일의 캐스팅처럼 만능은 아니다. 4가지의 캐스트 연산자로 분리된 만큼 const_cast의 역할인 상수성을 날린다거나 하는 등의 다른 캐스트 연산자의 고유 기능은 수행하지 못한다.
다른 캐스트 연산자와 같이 static_cast 도 static_cast 만의 용도가 있다.

- static_cast 사용
static_cast 를 사용하기 전에 용도와 제약 사항을 확인하자.
실수형과 정수형, 정수형과 열거형등의 기본 데이터 타입 간의 변환
상속관계의 클래스 계층 간의 변환
런타임 타입 검사를 하지 않음
다형성이 없어도 변환 가능 (RTTI 옵션이 꺼져있어도 된다)
다중 상속에서 기본 클래스 간의 타입 변환은 못함
void 포인터를 다른 타입의 포인터로 변환
서로 다른 타입의 포인터 간의 타입 변환은 못함

서로 다른 타입의 포인터 간의 타입 변환에 reinterpret_cast 를 사용하기보다는 void 포인터를 경유하는 방식을 추천한다.

[예제 1]
class BaseOne
{
public :
    virtual void Put( void ) { cout << "BaseOne" << endl; }
};

class BaseTwo
{
public :
    virtual void Put( void ) { cout << "BaseTwo" << endl; }
};

class Derived : public BaseOne, public BaseTwo
{
public :
    void Put( void ) { cout << "Derived" << endl; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    int IntValue = 0;
    double DoubleValue = 100.0;

    // 암시적 캐스팅 : 경고 발생
    IntValue = DoubleValue;
    // 명시적 캐스팅 : 경고 없음
    IntValue = static_cast<int>( DoubleValue );

    // 컴파일 오류 : 변환할 수 없는 타입
    //char* pChar = static_cast<char*>( &IntValue );

    // 컴파일 성공 : void 포인터에서 변환은 가능
    void* pVoid = &IntValue;
    char* pChar = static_cast<char*>( pVoid );

    BaseOne* pBaseOne = NULL;
    BaseTwo* pBaseTwo = new Derived;
    Derived*    pDerived = NULL;

    // 컴파일 성공 : 계층간 타입 변환이 가능 (타입 안전 검사는 안함)
    pDerived = static_cast<Derived*>( pBaseTwo );

    // 컴파일 오류 : 변환할 수 없는 타입 (dynamic_cast 필요)
    //pBaseOne = static_cast<BaseOne*>( pBaseTwo );   

    return 0;
}
위의 [예제 1] 에서 static_cast 의 기본 동작을 볼 수 있다.
[예제 1] 의 'pDerived = static_cast<Derived*>( pBaseTwo );' 에서 타입 변환은 컴파일 타임에 끝난다.
pBaseTwo 에 NULL 이 들어가 있어도 컴파일은 성공한다.
즉, 변환하려는 포인터가 계층 관계에 있다면 어떤 값을 가지고 있던지 컴파일은 성공한다는 말이다.
문제는 런타임에 발생할 것이고 대부분 프로그램의 다운으로 이어질 것이다.
이런 문제점 때문에 클래스 계층 간의 타입 변환에 dynamic_cast 를 추천하는 것이다.

[예제 1] 에는 재미있는 내용이 하나 있다.
서로 다른 타입의 포인터 간의 변환은 금지지만 void 포인터를 경유한다면 가능하다.
'char* pChar = static_cast<char*>( &IntValue );' 이것은 컴파일이 안되지만,
'void* pVoid = &IntValue;
 char* pChar = static_cast<char*>( pVoid );' 이것은 컴파일이 된다. 물론 포인터 값도 정상적으로 넘어온다.

 

반응형
반응형

[1번]

 

float dd1=123.00993;
int ddd=*((int*)(&dd1));

float gg= *((float*)(&ddd));

 

이렇게 해야지만 float 값이 다시 gg 에 나타난다

 


 

 

[2번]

float dd1=123.00993;
int ddd=dd1;

float gg= ddd;

 

이렇게 하면 나타나는 값은 gg==123이 된다

 

즉 상황에 따라 부동소수점의 값을 유지하고 싶다면 [1번]을 간단히 정수부분만을 때어내고 싶다면 [2번] 을 사용하면 된다

반응형
반응형

아래글 원문의 주소는

http://cafe.naver.com/cplc/83117

이지만 글을 조금 수정합니다, 추가한 붉은 글씨가 수정한 부분입니다

 

 

32bit 부동소숫점 연산식을 알아야 합니다.

단순히 데이터 자릿수가 실수로 변환되어지는 것은 아니고요.. 실제론 꽤 복잡한 연산식으로 만들어진 것입니다.

 

32비트의 각 자릿수 마다 정해진 규정이 있고요 풀어보자면,,,,,

맨 끝 비트(32번째 비트)가 부호를 결정짓고요 간단히 부호(s)라고 정의합니다.

그다음 24번째 비트부터 31번째 비트까지의 8비트(1byte)가 지수입니다. 이를 e라고 정해두고,

그다음 맨처음 비트부터 23번째 비트까지가 가수입니다.  이를 m이라 정한다면

 

  부동소숫점 = (-1)^s * 2^(e-127) *1.m

 

 

지수부(2^(e-127))는 첫번째 비트부터 2의 승수를 곱해주는데 즉,2^0,2^1, 2^2, 2^3, 2^4, 2^5, 2^6, 2^7의 값을 첫번째 비트부터 순서대로 곱하도록 하고

 그냥 지수부를 비트열로 표현하면 2^e 로 표현되지만 현재는 DWORD 에서 float 로 변환하는 과정임으로 (2^(e-127)) 와 같은 식이 사용된다

 이렇게 변환하는 이유는  float 의 표현 범위를 늘리기 위함

 

가수부(1.m)는 마지막 비트(23번째=>이미지 숫자상으론 22) 부터 2의 마이너스 승수를 곱해야 합니다.

 

                   즉, 2^-1, 2^-2, 2^-3, 2^-4 .......... 2^-23의 값을 마지막 비트부터 순서대로 곱해야 합니다.

                    => 2^-2 = 1/(2^2) 즉 소수점 부분을 표현하기 위한 수치

 

 

 

 

32bit의 데이터값이 16#4015c28f 이란 것을 기준해서 풀어보도록 해볼까요?

먼저 이 데이터를 2진수로 변환해 봐야 해당된 비트의 상태를 알 수 있습니다.

변환해 보면..... 휘리릭...!!~~

 

(16진수)16#4015c28f  => (2진수) 2#0100 0000 0001 0101 1100 0010 1000 1111 로 되는 것을 알 수 있습니다..

앞의 16#, 2# 가 진수 표현을 나타냄

 

어떻게 변환됬냐구요?

그냥 16진수의 1자리를 4자리의 2진수로 바꾸면 됩니다.

 

    첫번째 비트는 2^0   ---  1

    두번째 비트는 2^1   ---  2

    세번째 비트는 2^2   ---  4

    네번째 비트는 2^3   ---  8

                    => 2^n ( 0<=n <= 8 )

 

  - 0000 = 16#0

  - 0001 = 16#1

  - 0010 = 16#2

  - 0011 = 16#3

  - 0100 = 16#4

  - 0101 = 16#5         : 대충 눈치 채셨죠?

  - 0110 = 16#6

  - 0111 = 16#7

  - 1000 = 16#8

  - 1001 = 16#9          : 아직 모르시겠나요?

  - 1010 = 10 = 16#a   : 16진수에선 0 ~ f 까지랍니다.

  - 1011 = 11 = 16#b

  - 1100 = 12 = 16#c   : 이젠 아셨기를...

  - 1101 = 13 = 16#d

  - 1110 = 14 = 16#e

  - 1111 = 15 = 16#f    : 다 해버렸네...

 

그럼 본론으로 들어가서...

정리해서 보면

 

(2진수) 2#0100 0000 0001 0101 1100 0010 1000 1111

 

[float 로 변환]

 

  [부호] = 2#0 = 0  

  [지수] = 2#0100 0000 0 = 1000 0000

              2#0 에서2# 다음에 오는 0 은 이미 부호비트로 취급했음으로 다음인 1 을 포함한 8(지수부)의 비트열을 가져옴 

            = (1*2^7) + (0*2^6) + (0*2^5) + (0*2^4) + (0*2^3) + (0*2^2) + (0*2^1) + (0*2^0)

            = (1*128) + (0*64) + (0*32) + (0*16) + (0*8) + (0*4) + (0*2) + (0*1) = 128+0+0+0+0+0+0+0 = 128

 

  [가수] = 001 0101 1100 0010 1000 1111 = (0*2^-1) + (0*2^-2) + (1*2^-3) + (0*2^-4) + (1*2^-5) + (0*2^-6) + (1*2^-7) +

              (1*2^-8) + (1*2^-9) + (0*2^-10) + (0*2^-11) + (0*2^-12) + (0*2^-13) + (1*2^-14) + (0*2^-15) + (1*2^-16) +

              (0*2^-17) + (0*2^-18) + (0*2^-19) + (1*2^-20) + (0*2^-21) + (0*2^-22) + (0*2^-23)

            = (0*0.5) + (0*0.25) + (1*0.125) + (0*0.625) + (1*0.3125) + (0*0.015625) + (1*0.0078125) + (1*0.00390625) +

              (1*0.001953125) + (0*0.0009765625) + (0*0.00048828125) + (0*0.000244140625) + (0*0.0001220703125) +

              (1*0.00006103515625) + (0*0.000030517578125) + (1*0.0000152587890625) + (0*0.00000762939453125) +

              (0*0.000003814697265625) + (0*0.0000019073486328125) + (1*0.00000095367431640625) +

              (1*0.000000476837158203125) + (1*0.000000238418579101562) + (1*0.000000119209289550781)

            = 0.169999957084656

 

결과를 계산식에 대입해 보면

    (-1)^0 * 1.169999957084656 * 2^(128-127)

  = 1 * 1.169999957084656 * 2

  = 2.33999991416931

  ≒ 2.34 

 

정리하느라 2시간 걸렸습니다.  -_-;;;

직접 계산하려고 하시지 말고 GM-WIN에서 [REAL to DWORD] 와 [DWORD to REAL] 펑션을 이용해서

시뮬레이션 해보시는게 건강에 이로울 듯 하네요...

 


[float 로 변환과 그 역]

 

 이러한 연산 가수부분의 변환 연산 과정이 있음으로 float 에서 DWORD 로 변환 할대는 가수부를 정수부형태 즉

16#...... 의 형태로 변환 해야 하기 때문에

float 값을 (DWORD) 로 그냥 캐스팅하면 정수부분만 나오게 되고 float 전체 값을 캐스팅 하기 위해선

 

float a;

 

*((DWORD*)&a)

 

의 방법으로 캐스팅하면 float 값을 DWORD 로 변환한 값을 얻을 수 있게 된다

 


[더하는 말로..]

 

directx 에서

float PointSize = 10.0f;
   _pD3dDevice->SetRenderState( D3DRS_POINTSIZE , *((DWORD*)&PointSize));

 

의 함수중 *((DWORD*)&PointSize 이것을 이렇게 변환 하는 이유를 추측해 보자면 이 함수안에서 사용 되는 값이

DWORD 값을 float 로 변환해 쓰이기 때문에 이전에 float 값을 DWORD 로 변환한 값 형태로 넘겨줘야 하는 것 같다

 

 

반응형
반응형


A 클래스  함수내에서 함수포인터로 넘어온 함수를 실행시키기 위해선

 

A 클래스의 this 포인터로 넘어온(또는 인자로 받은) 함수포인터 타입을 실행 시켜야 한다

 

fn 이 함수 포인터 타입이고 어떤 함수가 A의 소식이면서 fn 의 변수로 넘어왔을때

 

 

A에선

void method{

  (this->*fn)();

}

 

으로 호출하면 된다

 

괄호가 중요!


BLOG main image






http://blog.naver.com/sorkelf?Redirect=Log&logNo=40135728128



함수포인터이긴 하지만


클래스에 멤버함수를 함수포인터로 호출 하려면 어떻게 할까?


함수포인터도 어디까지 포인터이므로


멤버함수의 주소를 넘겨 주되 클래스에 소속되어 있으므로


클래스의 scope 연산자 (::)를 반드시 명시해야 한다


또한 어디까지나 멤버함수이므로 함수포인터로 호출시에도


반드시 해당하는 그의 객체가 존재해야 한다


백문이 불여일견.. 코드를 보는게 빠를 듯..









http://www.cyworld.com/blue_wons/7209444


[STL 응용] Map으로 문자열과 함수포인터 매핑하기.




@ 이것을 보게된 계기
 cmd창에 명령어를 if else 구문으로 처리하다가, 좀더 깔끔하면서도 관리되는 방식의 명령어 정리는 하지 못할까라는 생각을 했고
명령어가 많아질수록 제어구문의 길이도 길어지는것을 감안하여 검색하던중, 스택오버플로우에 정확한 구현내용을 포착
이에 한번 따라서 만들어봤습니다. 위 참고 출처에는 클래스 객체 자신 내부함수를 참조하는 것이 아니기때문에
비교하면서 봐야 합니다.

#include <map>
#include <iostream>
#include <string>

class Type
{
typedef void (Type::*FuncPointer)();
typedef map<std::string, FuncPointer > FuncMap;
typedef map<std::string, FuncPointer>::iterator FuncMapIter;

FuncMap _funcMap;
FuncMapIter _pos;
string buff;

public:
Type(){};
~Type(){};

LoadFunc()
{
//왼쪽 값이 key 이면서 first, 오른쪽 값이 value 이면서 second
_funcMap["Start"] = &Type::Start; //굳이 이런식으로 넣지 않아도 된다.
//_funcMap.Insert(make_pair("Start", &Type::Start)); //이렇게 쑤셔박아도 된다.
}

ActivateFunction()
{
cin>>buff;
_pos = funcMap.find(buff);
if(_pos != funcMap.end())
{
FuncPointer func = _pos->second;
(this->*func)(); //참조할때가 가장 중요하다.
//이것은 자기 자신 내부라서 (this->*func)() 이런식으로 참조를 했지만
//객체 포인터가 아닌경우 (name).*func() 이런식으로 참조가 가능하다.
//자칫 잘못하면, 컴파일러가 잘못된 참조 갯수*3배만큼 애러를 토한다.
}
}

private:
void Start(){  std::cout<<"Start function !"<<std::endl;
}


[출처] 멤버 함수 포인터|작성자 풍풍풍



반응형
반응형

http://blog.naver.com/qktxhtkdl/10110879615

 


UINT_PTR (32비트 및 64비트 호환 포인터연산)   

#include "stdafx.h"
#include <Windows.h>
UINT_PTR CalDistance(UINT_PTR a, UINT_PTR b)
{
 return a-b;
}
int _tmain(void)
{
 INT32 val1 = 10;
 INT32 val2 = 20;
 _tprintf(_T("distance : %d \n") , CalDistance((UINT_PTR)&val1, (UINT_PTR)&val2));
 return 0;
}
 


32비트 시스템과 64비트 시스템은 주소값의 범위가 다르기때문에
 
상호 호환이 가능한 코드를 사용하는 것이 좋다.
 
Polymorphic타입의 자료형을 사용해서 매크로정의에 따라 32비트 & 64비트 호환이 되는 코드는 위처럼 UINT_PTR을 사용한다.
 
PTR은 포인터 자체가 아닌, 포인터 연산을 위해 존재한다는 뜻으로 해석하면 된다.

 


 

 

http://vvalkyrie.tistory.com/705

 

INT_PTR 형 분류없음 2007/11/13 11:47
64비트 호환 프로그래밍을 위해 _PTR 접미사 (type for pointer precision의 뜻)가 붙는 형들이 새로 정의되었다는 것을 알았다. 포인터 연산을 위해 포인터 변수를 정수형 변수로 type casting 할 때 발생하는 문제를 해결하기 위해 도입된 듯. 이 중 UINT의 _PTR 형인 UINT_PTR 형은 다음과 같이 정의되어 있다.

[CODE]#if defined(_WIN64)
    typedef unsigned __int64 UINT_PTR;
#else
    typedef unsigned int UINT_PTR;
#endif[/CODE]
원래 이걸 찾게 된 이유는 SOCKET 형의 정체가 UINT_PTR 형이었기 때문이다.

winsock2.h:[CODE]typedef UINT_PTR SOCKET;[/CODE]

반응형
반응형

만약 4*4 행렬일 경우

 

<<2  ==  *4 와 같으며 row |  는 0~3 범위의 인덱스가 됨으로 이와 같은 비트 오퍼레이션 연산이 가능하다

 

T & element (int row, int col) {
        return _array[row | (col<<2)];
    }

 

 

 void set_row(int r, const vec4<T> & t) {
        for (int i = 0; i < 4; i++) element(r,i) = t[i];
    }

    void set_column(int c, const vec4<T> & t) {
        for (int i = 0; i < 4; i++) element(i,c) = t[i];
    }

반응형
반응형

class Matrix{

private :

union {
        struct {
            T _11, _12, _13, _14;   // standard names for components
            T _21, _22, _23, _24;   // standard names for components
            T _31, _32, _33, _34;   // standard names for components
            T _41, _42, _43, _44;   // standard names for components
        };
        T _array[16];   

    };

}



15개의 배열과 각 _숫자   는 순서적으로 이름은 다르지만 같은곳을 공유하게된다

반응형
반응형

http://boxbop.tistory.com/45

 

memmove : 어떠한 경우에도 사용할 수 있는 메모리 복사 함수
cf) void* memmove(void* dest, const void* src, sizt_t len);
->매개변수 src로 전달된 값을 시작주소로 하여 len바이트를 읽어 들여서, 매개변수 dest로 전달된 주소에 복사를한다.

 memcpy : 제한된 상황에서의 메모리 복사
cf) void* memcpy(void* restrict dest, const void* restrict src, size_t len);
->memmove와 기능상 차이는 없다. 그러나 dest와 src가 restrict으로 선언되었다. 따라서 함수가 호출되면서 dest와 src로 전달된 주소 값의 메모리는 각각 dest와 src로만 접근이 가능해야 한다.(원본대상과 복사본이 겹치는 경우 사용할 수 없다)

반응형
반응형


부동 소수점 기수법 
floating-point representation system, 浮動小數點記數法  [컴퓨터]


지수를 이용하는 기수 표기법. 매우 큰 수 및 매우 작은 수를 표현하는 데 사용된다. 부동 소수점 기수법에서 수는 부동 소수점 부분과 지수 부분(exponent)의 2부분으로 나뉘어 표현된다. 부동 소수점 부분을 가수부(mantissa)라고도 한다. 가수부는 그 수의 숫자 부분을 지정하고 지수부는 그 수의 크기(소수점의 위치)를 지정한다. 부동 소수점 기수법에서 어떤 수 (n)이 가수부 (a)와 지수부 (b)로 표현되면 n=a×rb가 된다(r는 기수). 예를 들면 10진수 314,600,000과 0.0000451은 각각 3,146E5와 451E-7로 표현되는데 3,146과 451은 가수부이고 E5와 E-7은 지수이다. 따라서 314,600,000=3,146×105가 되고, 0.0000451=451×10-7이 된다.

 

 

 

 

 

규칙들

1.가수는한자리,0이아닌정수로쓰여야만한다.

2.가수는소수부수들의고정된개수를가지고쓰여야만한다.(이것을M이라고정의)

3.지수는고정된개수의수들로쓰여야만한다.(이것을E라고정의)

4.가수와지수는각각개별적인부호를가진다

 

 

최대, 최소 지수 ±(10^E -1) = ±(10^2 -1) =±99

최대 기수 값 10.0 - {10^(-M) } = 10.0 -{10^(-3)} = 10.0 - 0.001 = 9.999

 

최대 표현 가능한 값 : 9.999 * 10^99

최소 표현 가능한 값 : -9.999 * 10^99

가장 작은 양의 값 : 1.000 * 10^99

 

반응형
반응형

#include <iostream>
#include <Windows.h>

using namespace std;

struct UObject{
 int aa;
 int bb;
 int cc;
};


enum EInternal {EC_Internal};


void* operator new(size_t t,EInternal* Mem){

 return malloc(t);
}

void* operator new(size_t t){

 return malloc(t);
}

int main(){

 

 int* pi=new int;

 char* pc=new char;

 void* X=new char;

 new( (EInternal*)X ) UObject;     // X 가 *Mem 으로 전달 되고 UObject 의 크기 12가 t 로 전달된다

 

// new( (EInternal*)X ) UObject( UObject 의 생성자 인수목록 ); 


 delete pi;
 delete pc;

 

 return 0;
}

반응형

'프로그래밍(Programming) > c++, 11, 14 , 17, 20' 카테고리의 다른 글

restrict 포인터 {void* restrict pt}  (0) 2012.11.01
부동 소수점( 기수법 )  (0) 2012.11.01
Protected 생성자  (0) 2012.11.01
win32 에서 출력창에 표시  (0) 2012.11.01
난수 발생 srand, rand  (0) 2012.11.01
반응형

http://blog.naver.com/kofpw?Redirect=Log&logNo=80142050098

 


복사http://blog.naver.com/kofpw/80142050098

 

 

MFC의 동적 객체 생성 방법을 공부하던 중, MFC의 메인이 되는 각 클래스, Doc, View, MainFrm은 App 클래스의 InitInstance() 함수를 통해 동적으로 생성되는데, 웃긴것은 각 클래스의 생성자가 protected로 지정되어있는데 버젓이 다른 클래스에서 접근하여 생성한다는 것이다.

 

이것에 대한 원리가 나와있길래 살펴봤다. 어려운 내용은 아니다.

 

우선 다음과 같은 코드가 있다고 하자.

 

#include <iostream>

using namespace std;

 

class Object

{

protected:

      Object() { cout << "생성자" << endl; }

 

public:

      ~Object() { cout << "소멸자" << endl; }

};

 

void main()

{

     Object obj;

}

 

실행해보면 당연히 에러가 난다. 생성자가 protected로 선언되어있어 외부에서 접근할 수 없기 때문이다.

그럼 어떻게 해야할까? 뭐 제일 간단한 방법은 public에 생성자를 선언하는 방법이지만, 그런 뻔한 방법 말고 여기서의 목적은 MFC에서 어떻게 protected 생성자가 버젓이 호출되는지 파헤치기 위함이니...

 

그럼 어떻게? 그럼 Object 객체를 생성시켜주는 public 함수를 하나 만들어 보는 것이다.

 

#include <iostream>

using namespace std;

 

class Object

{

protected:

      Object() { cout << "생성자" << endl; }

 

public:

      ~Object() { cout << "소멸자" << endl; }

     void CreateObject() { Object obj; }

};

 

void main()

{

     CreateObject();

}

 

또 당연히 에러가 난다. 메인함수에서 CreateObject()라고 쓰면 클래스의 멤버가 아닌 전역 함수를 찾기 때문이다. 하지만 그 어디를 봐도 CreateObject라는 이름을 가진 전역함수는 보이지 않는다. 따라서 CreateObject를 찾을 수 없다고 에러가 뜬다.

그렇다고 먼저처럼 Object 클래스를 생성하고 obj.CreateObject() 라고 할수도 없는 일이다. 생성자는 protected이니까..

 

여기서 떠오르는 것이 바로 static이다.

 

#include <iostream>

using namespace std;

 

class Object

{

protected:

      Object() { cout << "생성자" << endl; }

 

public:

      ~Object() { cout << "소멸자" << endl; }

     static void CreateObject() { Object obj; }

};

 

void main()

{

     Object::CreateObject();

}

 

클래스의 멤버 함수를 static으로 선언해버리면 클래스를 만들지 않고도 해당 함수를 호출 할 수 있게 된다. (static 함수는 메모리의 데이터 영역에 저장된다) 위 코드는 정상적으로 실행이 될 것이다. 그러나, static 함수에서 생성된 객체 obj는 지역 변수이므로, 함수가 종료되는 순간 소멸되어버릴 것이다. 함수가 무용지물이 되는 셈이다.

 

그럼 또 어떻게 해야할까? 뭐긴... 동적할당하면 되지~~

 

#include <iostream>

using namespace std;

 

class Object

{

protected:

      Object() { cout << "생성자" << endl; }

 

public:

      ~Object() { cout << "소멸자" << endl; }

     static Object* CreateObject() { return new Object; }

};

 

void main()

{

     Object* pObj = Object::CreateObject();

     delete pObj;

}

 

이렇게 하면 함수를 통해 생성한 객체를 받아와서 사용할 수 있게 된다.

 

결론은, MFC의 동적 객체 생성 방법도 이런 식으로 진행된다는 것이다. 다만, 그것이 표면적으로 드러나있지 않고 숨어있을뿐.....

[출처] Protected Constructor|작성자 쿨랜드


반응형
반응형


첨부파일 utillilty.cpp 

http://www.nicklib.com/bbs/board.php?bo_table=bbs_util&wr_id=12&sca=Visual+Studio

 

 


#ifdef WIN32
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <crtdbg.h>
#endif
void Trace(char* lpszFormat, ...)
{
#ifdef WIN32
 char szBuffer1[512];
 char* lpBuf;
 char* lpsz;
 char szBuffer[512];

 va_list args;
 va_start(args, lpszFormat);
 _vsntprintf(szBuffer1, sizeof(szBuffer1)/sizeof(szBuffer1[0]), lpszFormat, args);
 va_end(args);
 lpsz = szBuffer1;
 lpBuf = szBuffer;
 while (*lpsz != '\0')
 {
  if (lpBuf > szBuffer + sizeof(szBuffer)/sizeof(szBuffer[0]) - 3)
  {
   *lpBuf = '\0';
   if ((1 == _CrtDbgReport(_CRT_WARN, NULL, 0, NULL, "%s", szBuffer))) 
    _CrtDbgBreak(); 
   lpBuf = szBuffer;
  }
  if (*lpsz == '\n')
   *lpBuf++ = '\r';
  *lpBuf++ = *lpsz++;
 }
 *lpBuf = '\0';
 if ((1 == _CrtDbgReport(_CRT_WARN, NULL, 0, NULL, "%s", szBuffer))) 
  _CrtDbgBreak(); 
#else
 return;
#endif


반응형
반응형

http://pcsak3.com/431

 

 

난수 발생 함수

   

 

srand( (unsigned)time( NULL ) ); // 난수 발생초기화

m_iNowBlockShape = rand()%7; // 난수 발생

  

 

 

 

 

C에서 난수 발생

   

다른 언어와 비슷하겠지만 C언에서 여러 개의 난수표를 가지고 있습니다. 이 난수표에는 임의의 수가 나열되어 있으며 C에서 난수표의 값을 가져오는 방식입니다.

이 때 동일한 난수표와 동일한 위치에서 난수를 가져오게 된다면 어떻게 될까요?

항상 같은 값만 나올 것입니다.

그래서 사용할 난수표와 위치를 항상 변경해 줘야 하는데 srand() 함수로 변경할 수 있습니다.

srand 파라미터에 어떤 숫자를 넣어 주면 난수표와 위치가 변경됩니다.

그렇다면 항상 다른 값의 파라미터를 입력해야 하는데, 어떻게 해야 할까요?

또 난수를 발생시켜 넣어 줄까요? ;;

이 때는 시간을 파라미터로 넣어 주면 됩니다. 시간은 항상 변하니까요^^.

 

그래서 C 에서는 아래처럼 사용됩니다.

 

srand( (unsigned)time( NULL ) ); // 난수 발생초기화

m_iNowBlockShape = rand(); // 난수 발생

  

 

 

 

난수 발생하는 방법을 알았는데,

만약 0 ~ 6까지의 난수를 생성하려면 어떻게 해야 할까요?

생성된 난수를 7로 나눈 나머지를 사용하면 되겠죠^^

 

srand( (unsigned)time( NULL ) ); // 난수 발생초기화

m_iNowBlockShape = rand()%7; // 0 ~ 6까지의 난수 발생

  

 

 

 

그렇다면 난수의 최대값을 얼마일까요?

컴파일러 마다 다를 수 있는데 헤더 파일에 선언되어 있습니다.

 

printf("RAND_MAX: %d", RAND_MAX); // 생성할 수 있는 난수의 최대값

  

 

 

 

 

RAND_MAX 는 어디 있으며, srand(), rand() 함수는 어디에 있나요?

아래 헤더 파일을 추가해 줘야 합니다.

 

#include <stdlib.h> // RAND_MAX, srand(), rand() 함수

#include <time.h> // time() 함수

  

 

 

 

 

난수 초기화 함수를 여러 번 실행하면 난수 발생이 더 효과적인가요?

=> 그렇지 않습니다. 초기화를 한 번하던 열번 하던 효과는 같습니다.~~

 

 

 

 

난수 초기화를 하지 않으면 항상 같은 수가 나오나요?

=> 컴파일러마다 약간 다를 가능성도 있지만 이론상으로는 항상 같은 값이 나옵니다.

 

아래는 C에서 난수를 발생한 예입니다.

 

 

#include <stdio.h>

#include <stdlib.h> // RAND_MAX, srand(), rand() 함수

#include <time.h> // time() 함수

 

 

int main(int argccharargv[])

{

 

//    srand( (unsigned)time( NULL ) ); // 난수 발생초기화

 

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

    {

        printf("\n%d",rand());

    }

      

    

    return 0;

}

 

 

 

41

18467

6334

26500

19169

15724

11478

29358

26962

24464

반응형
반응형

 

0과 1이나 true/false같은 switch개념의 변수를 사용하려면 C언어에서 최소사양에 해당하는 char형을 생각해 볼 수 있습니다. 물론 int형이나 그 밖에 다른 형을 써도 상관은 없지만 1Bit만 있으면 되므로 굳이 많은 Data형을 할당하여 Memory를 낭비할 필요는 없습니다.

그런데 char형도 그리 효츌적이지는 않습니다. 왜냐하면 char는 1Byte단위(8bit)이므로 0과 1을 표시하는데 1Bit를 할당하고 나면 나머지 7Bit는 결국 낭비되기 때문입니다.

따라서 C에서는 Bit단위의 Data를 다룰때 해당 Bit영역을 최대한 활용하기 위해 Bit Field라는 것을 사용합니다.

Bit Field는 선언하고자 하는 Data형의 크기 만큼 Bit Field를 구조체 형식으로 선언하면 됩니다.

struct bitfield{
    unsigned char a : 1;
    unsigned char b : 1;
    unsigned char c : 1;
    unsigned char d : 1;
    unsigned char e : 1;
    unsigned char f : 1;
    unsigned char g : 1;
    unsigned char h : 1;
};

bitfield라는 이름으로 unsigned char형(8bit)의 a부터 h까지 Bit Field를 선언하였습니다.

unsigned char라고 한 이유는 char형의 전체 8Bit중 최상위 Bit(부호를 정하는 bit)까지 모두 사용하기 위해서 입니다.(만약 unsigned int라고 한다면 16Bit만큼을 의미하게 됩니다.)

unsigned char는 전체 8bit이므로 a부터 h까지 1비트씩 차지하여 결국 합이 1Byte의 Memory를 할당하게 됩니다. 이때 a부터 h까지 1Bit씩 차지한다는 것은 a : 1 등에 의해 정해지게 됩니다.

따라서 1Bit가 아닌 그 이상의 Bit를 할당하고자 할 경우에는 오른쪽의 숫자만 바꾸어 주면 될것입니다.

struct bitfield{
    unsigned char a : 2;
    unsigned char b : 3;
    unsigned char c : 1;
    unsigned char d : 1;
    unsigned char e : 1;
};

a는 2Bit, b는 3Bit.. 나머지는 모두 1Bit를 할당하였습니다. 따라서 a는 2Bit이므로 0~3까지 그리고 b는 3Bit이므로 최소 0~7까지의 Data를 담을 수 있습니다.

이때 만약 Data형의 범위를 벗어나도록 bit field를 지정하면 어떻게 될까요?

struct bitfield{
    unsigned char a : 2;
    unsigned char b : 3;
    unsigned char c : 1;
    unsigned char d : 1;
    unsigned char e : 1;
    unsigned char f : 1;
};

f 1Bit까지 전체 9Bit를 할당하였습니다.

unsigned char는 8bit이므로 f를 수용할 수 없습니다. 하지만 Compiler는 오류를 표시하지 않고 위에서 순서대로 각 Bit를 모두 담은 뒤 그 이상의 Bit가 존재하면 다음 영역을 또 다시 확보하고 해당 Bit를 담게 됩니다.

즉, 처음 a부터 e까지 8Bit(1Byte)에 담고 그 다음 f를 위해 다시 8Bit(char = 1Byte)영역을 확보한 후 처음 Bit부분에 f를 담게 되는 것입니다.(나머지 7bit는 낭비됩니다.)

#include <stdio.h>

main()
{
  struct bitfield{
    unsigned char a : 2;
    unsigned char b : 3;
    unsigned char c : 1;
    unsigned char d : 1;
    unsigned char e : 1;
  };
  
  struct bitfield bit;
  
  bit.a = 2;
  bit.b = 5;
  bit.c = 1;
  bit.d = 0;
  bit.e = 1;
  
  printf("%d\n", bit.a);
  printf("%d\n", bit.b);
  printf("%d\n", bit.c);
  printf("%d\n", bit.d);
  printf("%d\n", bit.e);
}


각 Bit에 값을 설정하고 해당 값을 확인합니다.


bit field라고 해서 특별히 정해진 구문이 있는것은 아닙니다. 단지 bit field를 선언하는데 구조체의 선언방법을 빌려 선언되는것 뿐이며 구문상의 차이도 몇 Bit의 영역을 차지하는가를 나타내는 : x 형식의 구현부분만 다를 뿐입니다.

때문에 실제 bit filed와 구조체사이에는 그리 큰 차이가 없습니다.

#include <stdio.h>
#include <string.h>

main()
{
  struct bitfield{
    unsigned char a : 2;
    unsigned char b : 3;
    unsigned char c : 1;
    unsigned char d : 1;
    unsigned char e : 1;
    
    char name[10];
    int age;
  };
  
  struct bitfield bit;
  
  bit.a = 2;
  bit.b = 5;
  bit.c = 1;
  bit.d = 0;
  bit.e = 1;
  
  strcpy(bit.name, "youngsoo");
  bit.age = 30;
  
  printf("%d\n", bit.a);
  printf("%d\n", bit.b);
  printf("%d\n", bit.c);
  printf("%d\n", bit.d);
  printf("%d\n", bit.e);
  
  printf("%s\n", bit.name);
  printf("%d\n", bit.age);
}


구조체와 bit field를 동시에 구현합니다.


bit field를 선언할때 몇 Bit를 할당할지에 대한 내용은 생략하고 Data형만 쓰는 경우가 있습니다. 이는 실제 Bit영역을 사용하지 않으면서 Program구조상에 구분영역을 만들어 주는 역활을 수행하기 위함입니다.

또한 Bit를 처음부터 채우지 않고 일정부분 비워둬야 하는 경우에도 자주 쓰이는 방법입니다.

#include <stdio.h>

main()
{
  struct bitfield{
    unsigned char a : 2;
    unsigned char   : 3;
    unsigned char c : 1;
    unsigned char   : 1;
    unsigned char e : 1;
  };
  
  struct bitfield bit;
  
  bit.a = 2;
  bit.c = 0;
  bit.e = 1;
  
  printf("%d\n", bit.a);
  printf("%d\n", bit.c);
  printf("%d\n", bit.e);
}


bit field의 3번째 부터 5번째(이전의 b영역)과 7번째(이전의 d영역)은 사용하지 않습니다. 또한 a와 c, e를 영역별로 구분하도록 합니다.


참고 :
각 Field의 구분은 Program이 아닌 개발자 입장에서 입니다.

반응형
반응형

strtok(_tcstok)는 문자열을 특정 기준에 따라 나누어 주는 함수이다.

 TCHAR.H routine _UNICODE & _MBCS not defined _MBCS defined _UNICODE defined
 _tcstok strtok _mbstok wcstok
 _tcstok strtok_l _mbstok_l wcstok_l


예를 들어

string[] = _T("My Friends are honest. \n But, JK is very stupid");

 라는 문장이 있다 이 문장에서 \n을 기준으로 두개의 문자열로 나누고 싶다면

다음과 같이 사용하면 된다.

특정 기준을 사용하기 위해 배열에 기준에 대한 항목을 넣어준다.

TCHAR seps[] = _T("\t\n");  //\t와 \n이 나오면 문자열을 나눈다.

아래는 위에서 설명한 내용에 따라 예제로 나타낸 것이다.
(MSDN 참고)
#include <string.h>
#include <stdio.h>
#include <tchar.h>
TCHAR string[] = _T("My Friends are honest. \nSo, I'm happy");
TCHAR seps[] = _T("\t\n");
TCHAR *token;

int _tmain(int argc, TCHAR argv[])
{
//문자열을 기준에 따라 token에 임시 저장한다.
token = _tcstok( string, seps );

while( token != NULL )
{
  //기준에 의해 나눈 문자열을 출력한다.
_tprintf(_T("%s\n"), token );

// 다음 문자열을 구한다.
token = _tcstok( NULL, seps ); // C4996
}
}

실행결과
My Friends are honest.
So.I'm happy

반응형
반응형

위의 표 중 Parameters in registers 에서 32bit 일 경우 _fastcall 은 파라미터 전달시 저장을 레지스터에 저장한다는 것 한개도 아닌 두개로

_thiscall 의 경우엔 한개로,  그런데 표준함수 호출인 _stdcall 은 레지스터를 사용하지 않는다 _cdecl 경우도 마찬가지

 

하지만 64 bit 컴퓨터에서는 레지스터 사용량이 대폭 증가한다,

 

속도면에서 상당히 우월한 레지스터이니 빨라질 수 밖에없다 

반응형

'프로그래밍(Programming) > c++, 11, 14 , 17, 20' 카테고리의 다른 글

비트필드(Bit Field)  (0) 2012.11.01
_tcstok 사용방법(문자열 분리)  (0) 2012.11.01
입,출력 스트림 - IO stream  (0) 2012.11.01
strcmp.c  (0) 2012.11.01
c++ 무한대 표기  (0) 2012.11.01

+ Recent posts