Native Application

c++ unhanlde exception

7월 25, 2016 C & C++ No comments

c++에서 unhandle exception control 하는 경우

SetUnhandledExceptionFilter() 함수에 LONG WINAPI my_top_level_filter(__in PEXCEPTION_POINTERS pExceptionPointer) 형태의 callback pointer를 전달 합니다.

SetUnhandledExceptionFilter(my_top_level_filter);

callback함수 안에서는 minidump 파일을 생성하여 write 해두면 추후 !analyze -v를 통해 쉽게 stack trace가 가능 합니다.

아래는 간단한 mini dump 예제 입니다.

MINIDUMP_EXCEPTION_INFORMATION MinidumpExceptionInformation;
PEXCEPTION_RECORD pExceptionRecord = pExceptionPointer->ExceptionRecord;
MinidumpExceptionInformation.ThreadId = ::GetCurrentThreadId();
MinidumpExceptionInformation.ExceptionPointers = pExceptionPointer;
MinidumpExceptionInformation.ClientPointers = FALSE;
HANDLE hDumpFile = ::CreateFileW(dump_filename.c_str(),
    GENERIC_WRITE, 
    FILE_SHARE_WRITE, 
    NULL, 
    CREATE_ALWAYS,
    FILE_ATTRIBUTE_NORMAL, NULL);

MiniDumpWriteDump(GetCurrentProcess(),
    GetCurrentProcessId(),
    hDumpFile,
    MiniDumpNormal,
    &MinidumpExceptionInformation,
    NULL,
    NULL);

::TerminateProcess(::GetCurrentProcess(), 0);

pointer part 3

7월 20, 2016 C & C++ No comments

대학교에 다니면서 잠시나마 적었던 C언어 기초 강좌 입니다.

그나마 Pointer는 정성들여 썼길래 이 blog에 옮겨서 적어 놓습니다.

14강 3부 포인터의 활용

지금까지 힘들게 1부(http://scanhand.cafe24.com/226-2), 2부(http://scanhand.cafe24.com/pointer-part-2)를 통해서 포인터란 무엇인지 알아보았다.

그런데 과연 이 포인터가 무슨 쓸모가 있길래 이렇게 힘들게 배워야 하는걸까?

굳이 포인터를 쓰지 말고 그냥 기존에 배웠던대로 int a = 3; 이런식으로 쓰면 될 것을 힘들게 포인터란 것을 만들어서 우리를

시험에 들게(^^;) 하는 이유는 무엇일까? 내가 생각 하는 포인터의 가장 큰 용도는 아래 두가지 이다.

  1. 함수의 전달인자로서의 용도

  2. 변수의 동적할당을 위한 용도

(C Language 기준)

제목만 봐서는 무슨 용도인지 모를것이다. 그럼 하나하나 어떤용도로 쓰이지는 예를 들어가면서 살펴보도록 하겠다.

  • 함수의 전달인자로서의 용도

우리가 지금까지 함수를 배우고 만들면서 함수의 전달인자로 int, long, double과 같은 일반적인 변수형을 이용하였다. 이처럼 포인터도

하나의 변수의 형으로서 다른 형과 마찬가지로 함수의 전달 인자로 사용할 수 있는 것이다. 예를 들어

int main()
{
    int  a = 0;
    test(&a);
    printf("a = %d", a);
    return 1;
}

void test(int  *pA)
{
   *pA = 3;
}

이런식의 함수를 만들 수 있는것이다. 기존의 전달인자인 int a대신 int의 포인터형인 int *pA를 전달인자로 사용하였다. main()함수에서

는 test()함수를 호출할때 변수 a의 주소값을 넘기는 것을 볼수 있다. 즉 int형 포인터 변수 pA는 a변수의 주소를 가지고 있는것이다.

pA = &a;

다음으로 test()함수 내에서 pA에 3이라는 값을 대입하고 있다. pA는 현재 a변수의 메모리 주소를 가리키고 있기에 3이라는 값은 a변수

의 메모리에 저장이 된다. 그래서 printf()로 a의값을 출력하면 0이 아닌 3이라는 값이 출력되는 것이다.

이것을 다른 방식으로 생각하면 기존에 함수에서 결과값을 받고자 할때는 단 하나의 return값만을 받을 수 있었지만 전달인자로 포인터

형 변수를 이용하면 여러개의 값을 받을 수 있다.

int  main()
{
    int a, b, sum, sub;
    a = 10;
    b = 3;
    sum = sub = 0;
    process(a, b, &sum, &sub);
    printf("sum = %d, sub = %d\n", sum, sub);
    return 1;
}

void process(int c, int d, int *pSum, int *pSub)
{
   *pSum = c+d;
   *pSub = c-d;
}

결과화면

c_14_3_1

위예제에서 처럼 process()함수에 4개의 전달인자를 넘겨주고있다. 이 전달인자는 아래와 같은 방식으로 전달 된다고 생각 할 수 있을

것이다.

c = a;
d = b;
pSum = ∑
pSub  = ⊂

우리가 배열을 배울때 배열의 이름은 그배열의 시작주소를 뜻한다고 했다. 그리고 실제로 포인터변수를 이용해서 배열을 다루는 것을 지

난 강좌에서 알아보기도 했었다. 그렇다면 이얘기는 함수의 전달인자로 배열을 넘길 수 있다는 것이다. 실제로 예를 들어보자.

int  main()
{
    int a[10], i;
    for(i=0; i<10; i++)
        a[i] = 0;
    process(a);
    for(i=0; i<10; i++)
        printf("%d\n", a[i]);
    return 1;
}

void process(int *pA)
{
    int i=0;
    for(i=0; i<10; i++)
        pA[i] = i;
}

결과화면

c_14_3_2

역시 예상대로 배열을 전달인자로서 사용 할수 있다.

이처럼 함수의 전달인자 전달 방식은 크게 두가지로 나눌 수 있는것이다. 일반적인 변수형을 사용하여 데이터 값을 전달하는 방식인 값

전달 방식 call by value 과 포인터형 변수를 사용하여 주소를 전달하는 방식인 주소 전달 방식 call by reference 으로 구분 한다.

  • 변수의 동적할당을 위한 용도

우선 변수의 동적 할당이란것은 어떤 변수에 내가 원할때 메모리를 할당하고 또, 내가 원할때 메모리를 삭제 하는 것을 뜻한다.

즉 정해져 있는 대로 메모리를 쓰는게 아니라 내맘대로 메모리를 할당하고 삭제 하겠다는 것이다.

int  main()
{
   int  a, b;
   int  sum;
   a=5;
   b=3;
   sum = a+b;
   printf("sum=%d\n", sum);
   return 1;
}

위 예제에서 int a,b;이라고 선언을 하게 되면, a,b 라는 변수에 자동으로 메모리가 할당 되게 된다. 그리고 main()함수가 끝나는 시점인

return 1; 다음의 } 이곳에서 a,b의 메모리는 자동으로 해제되게 된다. 이것은 우리가 어찌 할수가 없이 정해져 있는 대로 메모리가 할당되

고 해제 되는 것이다.

그런데 여기서 sum=a+b; 이 문장 이후에는 a,b라는 변수는 더이상 필요치가 않다. 즉 쓸데 없는 메모리가 사용되고 있다고 볼수있다.

(너무 억지스럽지만 그냥 대충 그렇다고 하자^^;) 이렇게 변수를 더이상 사용하지 않을때 내가 원하는 곳에서 삭제하고 내가 쓰고 싶을

때 할당을 하려면 아래와 같이 사용하면 된다.

#include <stdio.h>
#include <malloc.h> /*malloc()함수를 쓰기위해서 필요한 Header File*/

int  main()                   
{
   int  *pA, *pB;
   int  sum;
   pA   =(int*) malloc(sizeof(int));
   pB   =(int*) malloc(sizeof(int));
   *pA =5;
   *pB = 3;
   sum = *pA+*pB;
   free(pA);
   free(pB);
   printf("sum=%d\n", sum);
   return 1;
}

붉은색 부분에 보지 못했던 함수들을 사용하였다. 하나씩 알아보도록 하자.

우선 포인터변수에 메모리를 할당하고자 할때는 malloc()이라는 함수를 사용한다.

이 함수의 전달인자로 메모리의 크기를 byte단위로 넘겨주면 해당 크기만큼의 메모리를 할당하고 그주소값을 return해 준다. 여기에

(int*) 를 사용하는것은 할당된 주소를 저장하고자 하는 변수가 int * 형이기에 써주는 걸로 이해하자. 자세한 내용은 기회가 생기면 언급

하도록 하겠다.

sizeof() 함수는 전달인자로 넘긴 변수형의 크기를 return한다. 즉 malloc()함수는 int형의 크기인 4byte만큼의 메모리를 할당하여 그 주

소값을 pA에 저장하는 것이다. 이렇게 할당된 메모리를 해제하고 싶을때는 free()함수에 해당 포인터 변수를 넘겨주면 된다.

동적할당은 위 예제처럼 메모리의 할당 및 삭제를 내가 원하는 순간에 할 수 있는 기능도 있지만, 실제적으로 주 사용처는 동적배열이라

고 할 수있다. 지금 까지 배열이라고 하면 int a[10]; 과 같이 미리 그 크기를 할당하고 사용해 왔다. 하지만 동적배열은 배열의 크기를 시

시때때로 내가 맘대로 조정할수 가 있게 된다. 사용방법은 기존 동적할당과 거의 비슷하다.단지 malloc()함수의 전달인자에 데이터 형

에 배열의 크기 만큼 곱한값을 전달 해 주면 된다.

index = 100;
pA   =(int*) malloc(sizeof(int) * index );
pB   =(int*) malloc(sizeof(int) *  index);
free(pA);
free(pB);

malloc()함수에는 400이라는 값이 전달이 되고 400byte의 메모리가 할당된후 그 시작 주소값을 pA에 저장하게 된다. 이것은 index변수

에 들어가 있는 값에 따라 pA에 할당되는 배열의 크기가 달라지는 것과 같다고 얘기할 수 있다. 즉 내가 원하는 대로 배열을 만들수 있는 것이다.

지금 까지 우리는 포인터에 대해서 대략적으로 알아보고 가장 기초적인 사용방법에 대해서 알아 보았다. 이 포인터 강좌는 실제 포인터에

대한 모든것을 다루고자 작성된것이 아니다. 단지 포인터를 공부하는데 있어서 조금이나마 도움이 되었으면 하는 마음에서 작성된것

이라는 것을 기억해 주기 바란다. 참고로, 처음기획단계에서 포인터를 3부로 설명을 마치려 했지만, 많이 부족한듯 하여 부록으로서

int**pA; 이런형식의 2차 포인터에 대해서 간략하게 설명하도록 하겠다.

pointer part 2

7월 20, 2016 C & C++ 1 comment

대학교에 다니면서 잠시나마 적었던 C언어 기초 강좌 입니다.

그나마 Pointer는 정성들여 썼길래 이 blog에 옮겨서 적어 놓습니다.

14강 2부 포인터의 개념

그럼 본격적으로 포인터에 대해서 알아 보도록 하자. 우선 C언어 책에 나와있는 정의를 보고 넘어가자.

포인터란? 값자체가 아니라 그 값이 있는 곳의 주소가 저장되어 있는 변수

역시 어렵다 ㅡㅡ; 일반적으로 우리가 지금까지 사용해온 변수는 어떠한 데이터값 즉 작은수 35, 큰수 90451234, 소수점 0.0432 혹은

문자 ‘A’ 와 같은 데이터 값을 저장하는 용도로 써왔다.

그런데 포인터란 어떤 특정한 데이터값이 아닌 주소를 저장하는 변수의 형을 뜻한다.

지난 14강 1부 시간에 우리가 변수의 주소에 대해서 배운 것을 기억 할 것이다. 즉 포인터는

0x0012FF7C와 같은 주소값을 저장한다. 일반적인 포인터 변수의 선언은 아래와 같은형식으로 한다.

int *pA1;

참고적으로, 일반적으로 다른 일반 변수와 구분하기 위해 포인터 변수일경우 이름앞에 p를 붙인다.

int형 변수를 선언하는 것과 똑같은데 다른점은 변수 pA1앞에 * 기호가 있다는 것이다.

*는 포인터 연산자라고 부르며 변수선언시에 *를 앞에 붙여주면 이 변수를 포인터 변수로 선언한다는 의미다. int에 *를 붙이듯

다른 변수형에 모두 *를 붙이면 포인터 변수가 된다.

double *pD; long *pL;

이렇게 선언된 포인터 변수에 주소값을 집어 넣어보자.

int a1 = 3; 
int *pA; 
pA = &amp;a1;

우선 int a1=3; 에서 a1이라는 int형 변수를 선언후 a1에 3을 저장한다. 그리고 pA라는 포인터 변수를 하나 선언했다.

다음 pA에 &a1을 대입한다. 여기서 &a1은 a1변수의 주소값인 0x0012FF7C가 되는 것이고, 이 주소값을 pA에 저장하게 된다. 실제 pA의

값을 출력하면 0x0012FF7C가 된다.

int a1 = 3; int *pA; pA = &a1; int a2; a2 = *pA;

위 예제에 2개의 문장이 추가가 되었다. int형 변수 a2를 선언하고 a2에 *pA를 대입하고 있다.

*(포인터)의 의미는 변수를 선언할때는 포인터 변수선언이라는 뜻이지만 그외에는 pA가 저장하고 있는 주소가 가리키는 데이터 값을 뜻

한다. 현재 pA는 0x0012FF7C라는 주소값 즉 변수 a1의 주소값을 가지고 있다. 그렇다면 *pA는 0x0012FF7C 주소번지에 저장된 데이터

값, 즉 3 이라는 값을 뜻하게 된다. 그리하여 a2에는 3이라는 값이 저장되게 된다.

지금 까지 설명 한것을 직접 눈으로 보도록 하자.

c_14_2_1

현재 int a1=3; int *pA; 까지의 상태는 a1의 값이 3, &a1(a1의주소값)이 0x0012FF7C이다.

pA의 데이터 값은 일반변수와 마찬가지로 현재 선언만 되어 있는 상태이기에 0xCCCCCCCC라는 값이 저장되어 있다.

c_14_2_2

pA = &a1; 을 실행후 pA의 값에 a1의 주소값인 0x0012FF7C라는 값이 저장된 것을 볼수 있다.

c_14_2_3

a2 = *pA; 후에 a2에 데이터값 3이 저장되는 것을 볼수 있다. 이는 단순한 3이라는 값을 a2변수의 저장공간에 복사만 할뿐 a2와 pA간

에 주소값은 서로 다른것을 볼수 있다.

pA = 0x0012FF7C, &a2 = 0x0012FF74

지금까지 가장 기본적인 포인터를 사용해 보면서, int형 포인터 변수를 이용해 보았다. 다른 포인터형 변수라고 다를것은 없다. int형이

든, double형이든, char형이든 모든 포인터 변수는 단지 주소만을 저장 할 뿐이다. 그렇다면 이 포인터형의 크기는 결국 0x0012FF7C라

는 주소만을 저장하면 되기 때문에 그 종류에 상관없이 크기가 4Byte면 충분하다. 여기서 포인터형변수가 왜 4byte인지 의문점이

해결된다.

다음으로 포인터를 사용해서 배열을 다루는 것에 대해 알아보자.

배열은 이미 예전 강좌에서 설명을 했었다. 필요한 부분만 다시한번 짚고 넘어가면,

int a[3];

int형 배열 a를 선언하는 선언문이며, 여기서 배열의 이름인 a라는 것은 a배열의 시작주소값을 뜻한다고 했었다. 시작주소값이라고 하면

0x0012FF74같은 주소를 의미하는 것인데… 그럼 이것을 포인터 변수에 넣을 수 있지 않을까?

한번 시도해 보자.

int a[3]; 
a[0] = 1;
a[1] = 2;
a[2] = 3;
int *pA; 
pA = a;

c_14_2_4

int a[3]; 배열 선언후 각각 1,2,3이라는 값을 삽입했다. 배열의 이름인 a의 값을 출력해보니 역시 우리가 공부했던대로 배열의 시작주소

값인 0x0012FF74라는 값을 보여주고 있다.

c_14_2_5

우리의 예상대로 포인터 변수 pA에 a배열의 시작주소값인 0x0012FF74가 저장되는 것을 눈으로 확인할 수 있다.

그렇다면 여기서 *pA의 값을 출력하면 어떻게 될까? *pA는 pA가 가지고 있는 주소값이 가리키는 데이터 값이라 설명했다. 그렇다면 현

재 pA는 a의 배열의 시작주소값이므로 a[0]의 주소값(&a[0])을 가지고 있다고 할수 있다. 즉 *pA는 a[0]의 값인 1이 출력된다.

c_14_2_6

그렇다면 포인터 변수를 이용해서 a배열의 시작인 a[0]말고 a[1]이나 a[2]에 접근 할수 있을까?

방법은 아래 방식을 이용한다.

pA + 1

포인터 변수 pA + 1은 pA가 가리키고 있는 주소값에 포인터 변수의 형(int)의 크기만큼 더하게 된다. 즉 현재 pA가 0x0012FF74라고 하면

+1은 int형이 4byte이기 때문에 pA+1은 0x0012FF78의 주소값을 가지게 된다. 연속적으로 pA+2는 4byte*2 인 8btyte를 더한 0x0012FF7C

가 되는 것이다.

기존에 배열에 관해 배웠을때 배열에 할당된 메모리는 연속적으로 위치해 있다고 했다. 그렇다면 a[0]다음 4byte가 a[1]이듯 현재 pA

가 a[0]의 주소값을 가지고 있다면 pA+1은 a[1]의 주소를 가지게되고 pA+2은 a[2]의 주소를 가지게 된다.

실제 결과를 보자.

c_14_2_7

아마 많은 분들이 포인터에 기본적인 방법들인 pA는 주소값을 저장한다던가 *pA가 그 주소가 가리키는 값이라는 것은 쉽게 이해하지만

배열을 포인터변수에 집어넣는것과 같은 곳에서 많이 난해해 할것이다. 이것을 좀더 이해하기 쉽게 하는 것은 아래와 같은 방식으로 이

해 해보도록 하자.

int a[2];
a[0] = 11110;
a[1] = 4440;
char *pA;
pA   = (char*)a;

기존에 보던 방식과 약간다른 점이 있다. int형 배열의 시작주소인 배열의 이름 a를 char형 포인터 변수 pA에 삽입한다. 이것은 단순하게

이해하는데 도움을 주고자 하는것이기에 컴파일 오류를 없애기 위해 (char*)가 들어갔다고만 생각하고, 단순하게 int형 배열의 주소를

char형 포인터 변수에 저장했다고만 생각하자

이것을 실행한 결과화면은 아래와 같다.

c_14_2_8

a의 값은 배열의 시작주소이기에 pA에 저장된 값인 0x0012FF78일것이다.

그렇다면 *pA는 char형 포인터변수가 가리키는 값을 의미한다. 메모리상에서 0x0012FF78 번지의 값인 66(16진수), 102(10진수) 가 출력

되는것을 확인할수 있다.

여기에 pA+1은 char형의 크기만큼 즉 1byte만큼 주소를 증가 시킨값이기에 0x0012FF79번지 가 된다. 그 번지에 저장된 값은 2B(16진수)

즉 10진수 43이다. 마찬가지로 pA+4는 0x0012FF78+4 = 0x0012FF7C번지를 뜻하면 그 값은 58(16진수), 10진수로는 88이다.

위와 같이 포인터라는 것은 메모리의 주소를 프로그래머가 손쉽게 접근할수 있도록 해준다.

포인터와 관련된 소스코드를 보면서 이해하기에는 너무 난해한 어려움이 있다.

그것을 위 예제와 같이 메모리를 직접 그려보고 실제 메모리 주소에서 데이터 값이 어떻게 변하는지를 생각한다면 조금은 이해

하는데 도움이 될 수 있을것이다.

pointer part 1

7월 20, 2016 C & C++ 1 comment

대학교에 다니면서 잠시나마 적었던 C언어 기초 강좌 입니다.

그나마 Pointer는 정성들여 썼길래 이 blog에 옮겨서 적어 놓습니다.

14강 1부 변수와 메모리

조금이나마 C언어책을 훑어본 사람이라면 제목만 읽고서도 짜증이 밀려오기 시작 할꺼다. 나도 처음에 혼자서 C언어를 공부하면서 참

재밌게했던 사람중에 한 사람이다. 하지만 포인터라는 단원을 공부할때 난 처음에 이길이 내길이 아닌 줄 알았다.(^^;) 내 머리의

한계를 느끼기도 하고 내가 이렇게 멍청한가라는 생각도 수 없이 많이 했을 정도로 이단원은이해하기 전에는 참 난해하고 어려운 큰 장벽과 같았다.

우선 어려운 정의를 말하기 전에 사람들이 쉽게 놓치고 있는 메모리와 프로그램에 대해서 알아보도록 하자.

모든 C언어 책 맨 앞에는 컴퓨터 구조와 프로그램이 어떻게 컴퓨터란기계에 실행이 되고 우리가 어떻게 이것을 모니터를 통해서 보고하는지 계략적인 개념이

나와있다. 이것을혹시 무심코 지나왔다면 다시한번 머리속에 생각 하면서 계속 이 글을 읽기 바란다.

아래와 같은 아주 심플한 프로그램이 하나 있다.

#include <stdio.h>
int main()
{
    int a1, b1, sum;
    a1 = 3;
    b1 = 5;
    sum = a1 + b1;
    printf("sum = %d", sum);
    return 1;
}

위 예제는 a1, b1이라는 변수에 각각 3, 5라는 숫자값을 저장해서 그합을 sum 이라는 변수에 저장 시키고이 sum이라는 변수값을 화면

에 출력하는 프로그램 이다. 위프로그램은 지금까지 강좌를 읽어 왔다면 쉽게 머리속으로 충분히 컴파일 하고 실행까지 시킬수 있을 것이다.

그런데 과연 실제 우리가 머리속에서 실행시키는 방법 하고 실제 컴퓨터가 실행시키는 방법하고 똑같을까?

실제 이 프로그램을 실행시 킬때 그 과정을 우리 눈으로 볼수 있을까?

누구나 한번쯤 이런 생각을 가졌을 거라고 생각된다. 아래를 보자. 실제 프로그램이 실행되는 세세한 과정을 눈으로 보여 주겠다.

c_14_1_1

지금이 화면은 VC++ 6.0의 디버깅 화면이다. VC++이란실제 우리가 만든 프로그램소스를 실행 파일로 만들어주는 프로그램이라고 생각 하면 쉽다.

지금 이화면에서는크게 프로그램소스를 보여주는곳, 변수의값을 보여주는곳, 실제 메모리를 보여주는곳 이렇게 3가지로 나눌수 있다.

프로그램소스를 보여주는 곳에는 실제 우리가 구현했던 프로그램 소스가보이고, 변수의 값이 보이는곳의 Name라는 항목에는

우리가 선언한 변수 세개(a1,b1,sum)가보이고 value에는 알수 없는 Error라는 것이 보인다. 지금은 Value에 Error라고표시가 되지

만 조금 후에 실제 각 변수에 들어가 있는 데이터 값이 보이게 된다. 왜 여기서 Error라고 보이는 지는 추 후에 설명하겠다. 마지막으로

실제 메모리를 보여주는 곳에는 말 그대로 메모리에 저장 되어 있는 값을그대로 보여준다.

우선프로그램소스부분을 보자. 여기서 노란색 화살표는 지금 프로그램이 실행되려고 하는 위치를 뜻한다. 지금 이화면은 프로그램을 시

작하자 마자 멈춰놓은 상태이기에 우리가 배운대로main()이라는 시작위치에서 프로그램이 시작 된다는것을 눈으로 확인 할 수 있다.

c_14_1_2

a1, b1, sum 이라는 변수를 선언한 문장까지의 상태다. 우리가 배운대로 a1, b1, sum에는 현재 변수가 선언 되기만 한 상태이기에 각 변

수에는 이상한값(쓰레기값)이 들어있다(-858993460)

c_14_1_3

a1 = 3; b1 = 5; 이 두문장을 실행 한 후에 a1이라는 변수에는실제 3이라는 값이, b1이라는 변수에는 실제 5라는 값이 저장되어 있는것을

볼수 있다. sum에는 아직 아무런 값을 저장하지 않은 상태이기에 아무런 변화가 없다. 여기서 지금 화살표 문장을 실행하면 a1+b1이라

는 결과값인 8이라는 값이 sum1에 저장될 것이다. 실제로 그렇게 되는지 보자.

c_14_1_4

역시우리 예상대로 sum이라는 곳에 8이라는 값이 저장이 된다. 그럼 당연히 printf()로 출력을 하면 아래와 같은 결과 화면을볼수 있다.

c_14_1_5

여기까지 살펴본 결과 우리가 머리속으로 컴파일했던 것과 결과가 일치 한다.

그럼다음으로는 각변수가 실제 메모리상에서 어떻게 저장되는지 눈으로 보도록 하자.

메모리정보를 보기에 앞서 사전에 알고 있어야 할 몇가지를 짚고 넘어가도록 하겠다.

int a1 = 3;

위문장은 a1이라는 int형 변수를 선언하고 그변수에 3이라는 값을 저장한다.

이 말은내부적으로 메모리의 어딘가에 int형의 크기만큼(4byte) 주소를할당하고 그 메모리의 공간에 우리가 접근할수 있도록 이름표를

하나 달았다고 생각하면 쉽다. 말로는 이해 하기 어렵다.

아래 그림을 보자.

c_14_1_6

여기서 그 변수의 주소값을 가져오고자 할경우 우리는 변수의 이름앞에 &(주소연산자)를 붙이게 된다.즉 a1이라는것은 3이라는 저장된

값을 의미하지만 &a1이라는 것은 a1의 주소를 뜻하게 된다. 여기서 주소란 실제 메모리의 주소를 뜻한다. 참고로 메모리의 주소는 아래

의 형식으로 표현된다.

0x00200100

0x : 16진수를 의미

00200100 : 실제 메모리 주소

주소 형식을보면 실제메모리 주소는 000000~FFFFFF 까지 표현 할 수 있다는 것을 알수있다.

즉 이론적으로 프로그램에서 사용할 수 있는 메모리가 4Gbyte인 이유가 여기에 있는것이다.

c_14_1_7

이전의 실행 했던 프로그램과 동일한 프로그램을 실행시킨 상태이다.단 여기에 기존에는 변수의 값을 보여주는곳에 &(주소연산자)를

붙여 각 변수의 주소를 볼 수 있도록 하였다.

즉 int형 변수 a1의 주소(&a1)는 0x0012FF7C라는 의미이다. 이것은 다시 반복하면 메모리의 0x0012FF7C 번지에 변수의 데이터값이 들

어가 있다는 것을 뜻한다. 눈치챗듯이 왼쪽 0012FF7C는 메모리의 주소를 그 오른쪽에는 실제 메모리에 저장된 값을 보여주는 것이다.

현재변수 a1의 메모리 주소 0x0012FF7C에는 CC CC CC CC 라는 4byte의 데이터 값이 들어있다. 실제 a1의 데이터 값은

CCCCCCCC를 10진수로 변환한 -858993460이라는 값이 들어있는것이다.변수를 선언한다는 것은 메모리 주소를 할당 받는다는 것을 뜻

한다. 하지만 할당받은 메모리의 안에는 초기화 전까지는 어떠한 값이 들어 있는지를 알수가 없다. 즉 쓰레기 값이 들어 있는 것이다.

a1이라는 변수의 메모리에 CCCCCCCC라는 쓰레기값이 들어 있듯이 말이다.

c_14_1_8

다음은 sum = a1+b1; 까지 실행 한 상태이다. 참고로 메모리주소가 보이는곳에 빨간색 글씨가 보일 것이다. 지금 우리는 메모리의

0x0012FF74번지에 있는 실제 데이터 값을보고 있다. 0x0012FF74는 int형 변수 sum의 주소 이기도 하다. 그렇다면 이 주소에 sum의

데이터 값인 8이라는 값이 들어 있어야 할 것이다. 실제 메모리상에서도 8이라는 값이 들어 있다.(빨간색글씨부분)

즉 0x0012FF74 ~ 0x0012FF77 까지의 총 4byte는 sum의 데이터 값인 8이 저장되어 있다라고 말할 수 있다. 그럼 이것을 응용해서

0x0012FF77의 다음 4Byte의 값을 보자.

05 00 00 00

이렇게 5라는 값이 저장되어있다. 이 4byte의 시작주소 값은 0x0012FF78(0x0012FF77+1) 이다.

5라는 숫자값 우리가 b1이라는 변수에 넣었던 것을 기억 할 것이다. 실제 b1의 주소값은 0x0012FF78 이다.

참고적으로변수의 주소값을 printf()를 이용해서 결과를 보고 싶다면 다음과 같은 방법으로 볼수 있다.

printf("a1의 주소값 = %x, b1의 주소값 = %x, sum의 주소값 = %x", &amp;a1, &amp;b1, &amp;sum);

이 문장에서 %x는 16진수로 출력하는 것을 뜻하고, 각 변수에 주소연산자(&)를 붙여 그 변수의 주소값을 출력하도록하였다.

지금까지 프로그램 실행 중에 변수가 어떻게 변하는지, 실제 메모리에는 어떻게 들어가 있는지 등을 알아보았다. 혹시 포인터를 하는데 이

게 왜 필요한지 묻는 분이있다면, 포인터라는것은결국 변수의값이 아닌 메모리 주소를 이리저리 요긴하게 쓰기 위한 도구라고 생각 하면 수긍할 수 있을 것이다.

unit test by googletest

7월 15, 2016 C & C++ No comments

Unit Test Project에 설정

1.기존 gtest site에서 source download (https://code.google.com/p/googletest/)

2.압축을 푼 후 msvc 폴더의 gtest.sln solution파일 열기

3.Release로 전체 build로 gtest.lib 생성

4.include폴더의 gtest폴더와 gtest.lib 파일을 새로운 UnitTest Project에 복사

5.새로운 UnitTest Project에 (Win32 Console Project) Project 속성페이지->C/C+ 항목에 추가포함 디렉토리에 gtest include폴더의 위치를 설정. ex)../Include

6.UniteTest Project의 속성페이지->Linker의 입력항목에 gtest.lib 추가

7.속성페이지 C/C+에서 code generation(코드 생성)에서 Runtime Library설정을 Multi Thread로 변경(/MT)

  1. main() 함수에 추가

    testing::InitGoogleTest(&amp;argc, argv); return RUN_ALL_TESTS();

9.기타 google test 항목 추가

10.Run

TEST_F() 사용법

1.Fixture class 만들기

  • ::testing::Test를 상속하여 Test용 class 새로 생성

  • 생성자, 소멸자 만들기

  • SetUp(), TearDown()을 통해 consruct, deconstruct 구현

2.TEST_F()함수로 Test 추가

  • 1.parameter에 Fixture class

  • 2.parameter에 enum {}으로 선언한 type 설정

    ex) enum dsp_open {}; 
    TEST_F(SampleLibUnitTest, dsp_open)
    { 
         int ret = test_init(0); 
         EXPECT_EQ(1, ret); 
    }
    

3.main()함수에 InitGoolgleTest()와 RUN_ALL_TESTS()수행

ex)
GTEST_API_ int main( int argc, char **argv) {
     printf ("Running main() from gtest_main.cc\n");
     testing ::InitGoogleTest(&amp; argc, argv );
     return RUN_ALL_TESTS();
 }

using c# dll in c++

7월 15, 2016 C# No comments

1.기존 C# DLL을 COM으로 노출 시켜야 함

  • (1)프로젝트->속성->Assembly 정보 -> 어셈블리를 COM에 노출 check

  • (2)프로젝트->속성->Build->COM Interop 등록 check

2.노출하고자 하는 Function을 포함하는 Interface를 구현

[ Guid ("8F42A31B-7C69-4E14-BF7A-56F243134852" )]
public interface INDTComponent
{
    int ShowTGCControl(byte[] tgcValue);
}

3.해당 Interface를 상속하영 Class구현

[ Guid ("379AA4DF-991F-4A72-B091-9DCEE25D8DD5" )]
public class NDTComponent : INDTComponent
{
    public int ShowTGCControl( byte [] tgcValue)
    {
        frmImageAdjustmentCurve form = new frmImageAdjustmentCurve ();           
        form . SetTGCValue(tgcValue);
        form . ShowDialog();           
        return 1 ;
    }
}

4.C# DLL을 Build시 *.tlb 생성됨

5.C++ 프로젝트에서 연동하고자 하는 C# DLL의 tlb파일을 Import

#import "..\Bin\Release\NDTComponent.tlb" no_namespace named_guids

(참고)C++ 프로젝트 Compile후 tlh가 자동 생성됨

6. C# DLL을 호출

NDTComponentPtr ptr ( __uuidof( NDTComponent ));
ptr->ShowTGCControl();
ptr->Release();

– Array 전달하기 : SAFEARRAY를 이용하여 전달

NDTComponentPtr ptr ( __uuidof( NDTComponent ));

uchar tgcValue [ 256];
memset ( tgcValue, 0, sizeof( tgcValue ));

SAFEARRAYBOUND sab ;
sab . lLbound = 0;
sab . cElements = 256;
SAFEARRAY * pSA = SafeArrayCreate (VT_UI1 , 1 , &sab );

LONG index ;
for (int i =0 ; i<256 ; i ++)
{
     tgcValue [ i] = NDTData :: getInstance(). m_TGCValue [i ];          
     index = i;
     SafeArrayPutElement ( pSA, &index , &tgcValue [i ]);
}      

ptr->ShowTGCControl(pSA);
ptr->Release();

callback 함수 전달하기

1.C# DLL에 callback용 Delegate 선언

[UnmanagedFunctionPointer( CallingConvention.StdCall)]
public delegate void CurveChangedCallback(byte [] curveValue);

2.C# DLL에 callback Parameter 추가

int ShowTGCControl([MarshalAs(UnmanagedType .FunctionPtr)]CurveChangedCallback callback);

3.C++에 callback용 함수 선언 및 구현

static NDTComponentCOM::CurveChanged(uchar* tgcValue);

4.C++에서 Parameter로 전달 (long)형으로 casting

void * curveCallback = NDTComponentCOM:: CurveChanged ;
ptr->ShowTGCControl( nX , nY , pSA, (long ) curveCallback);
ptr->Release();

struct parameter in c++ dll function

7월 15, 2016 C# No comments

struct parameter를 C++ dll과 C# 간의 parameter 전달 방식을 정리 했습니다.

1.C++에서 Struct 선언

typedef struct tag ULTRASOUNDREGION {
             char RegionDataType[LEN_US + 1];
             float PhysicalDeltaX;
} ULTRASOUNDREGION

typedef struct tag DICOMINFO{
             char SOPClassUID[LEN_UI + 1];
             ULTRASOUNDREGION UltrasoundRegion[ 5 ];
} DICOMINFO

2.C#에서 Struct 선언

[Serializable]
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct ULTRASOUNDREGION
{  
        //배열을 전달시 UnmanagedType을 ByValArray로 설정 후 SizeConst에 배열의 크기(Index Count) 를 전달
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = VR.LEN_US + 1)]
        public char[] RegionDataType;

        [MarshalAs(UnmanagedType.R4)]
        public float PhysicalDeltaX;

        //선언 배열은 Struct 생성자(c# sturct는 최소1개의 Parameter 필요)에서 할당
        public ULTRASOUNDREGION(bool bInit) : this()
        {
            RegionSpatialFormat = new char[VR.LEN_US + 1];
        }
}

[Serializable]
[StructLayout( LayoutKind .Sequential, CharSet = CharSet.Ansi)]
public struct DICOMINFO
{  
        //구조체 배열도 동일하게 ByValArray로 설정, SizeConst는 구조체 배열의 Index Count로 설정
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5 ]
        public ULTRASOUNDREGION[] USRegion;

        //배열이 아닌 그냥 구조체로 전달시에는 UnmanagedType.Struct로 설정하여 전달, SizeConst는 Struct 실제 Size
        Ex)
        [MarshalAs(UnmanagedType.Struct, SizeConst = 40]
}

3.C++ DLL Function의 Parameter에 구조체 Pointer전달

Ex)
extern "C" DLLDECL int SaveDICOMFile( DICOMINFO *pDicomInfo , char *pBMPFilePath, char *pDICOMFilePath);

4.C# Module 선언부분에 구조체에 ref 추가

Ex)
[ DllImport("DCMTKInterface.dll", CallingConvention = CallingConvention .Cdecl)]
public static extern int SaveDICOMFile( ref DICOMINFO dicomInfo, char [] bmpFilePath, char [] dicomFilePath);

5.C#에서 C++로 return 받고자 하는 Struct Array를 전달시

C#)
void LoadNDTRawFile(NDTParam10[] pNDTParam);

c++)
void LoadNDTRawFile(NDTParam10* pNDTParam);

C++에서 data관련 처리후 pNDTParam[0] or pNDTParam[index]를 통해 값을 설정하면 C#측은 설정된 NDTParam10 array를 받을 수 있다.

c++ dll function call

7월 15, 2016 C# No comments

C++ DLL에서 생성한 Instance를 Parameter로 Return 받고자 할때

1.C++ DLL에서 void** pInstance형으로 전달 받기

Ex) int LoadDICOMImage( void ** hImage)
{
    *hImage = (void*)new DicomImage();
}

2.C#에서 해당 DLL Function 선언시 ref IntPtr로 설정

Ex) [DllImport(“DCMTKInterface.dll”)]
    public static extern int LoadDICOMImage( ref IntPtr hDicomImage)

3.C# Main에서 해당 함수 호출시 IntPtr 전달 및 ref 설정

Ex) IntPtr hDicomImage = new IntPtr();
    DCMTKInterface.LoadDICOMImage( ref hDicomImage)

C++ DLL에 long과 같이 일반자료형 call by reference로 return 받기

1.C++ DLL에서 long*형으로 전달 받기

Ex) int GetImageBufferLength(void* hDicomImage, long* nBufferSize)

2.C#에서 해당 DLL Function 선언시 ref long로 설정

Ex) [DllImport(“DCMTKInterface.dll”)]
public static extern int GetImageBufferLength( ref long nBufferSize)

3.C# Main에서 해당 함수 호출시 long 전달 및 ref 설정

Ex) long nBufferSize = 0;
   DCMTKInterface.GetImageBufferLength(hDicomImage, ref nBufferSize);

C++ DLL에 byte[]같이 배열을 전달하여 값 삽입후 return 받기

1.C++ DLL에서 byte*형으로 전달 받기, Array size도 같이 전달

Ex) int GetImageBuffer(void* hDicomImage, byte* pBufferData, long nBufferSize)

2.C#에서 해당 DLL Function 선언시 byte[]로 설정

Ex) [DllImport(“DCMTKInterface.dll”)]
public static extern int GetImageBuffer (IntPtr hDicomImage, byte[] pBufferData, long nBufferSize);

3.C# Main에서 해당 함수 호출시 Memory 할당 후 전달

Ex) byte[] pBuffer = new byte[nBufferSize];
DCMTKInterface. GetImageBuffer (hDicomImage, pBuffer, nBufferSize );

byte[] to Struct

7월 15, 2016 C# No comments

Marsharing을 이용하여 memory copy형식으로 변환하는 예제입니다.

대신 struct에 [Serializable] 추가 해주셔야 합니다.

byte[] to Struct

IMAGEHEADER header = new IMAGEHEADER ()
int size = Marshal.SizeOf(header);
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(arr, 0, ptr, size);
header = ( IMAGEHEADER) Marshal .PtrToStructure(ptr, header.GetType());
Marshal.FreeHGlobal(ptr);

struct to byte[]

int size = Marshal .SizeOf(header);
byte [] arr = new byte[size];
IntPtr ptr = Marshal .AllocHGlobal(size);

Marshal.StructureToPtr(header, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);

Reactive Extensions(Rx)

7월 15, 2016 C# No comments

Reactive Extensions(Rx : https://msdn.microsoft.com/en-us/data/gg577609.aspx)

1.간단한 background 작업 및 작업 완료 기다리기(Async)

Debug. WriteLine( "Shows use of Start to start on a background thread:" );
_Observer = Observable . Start(() =&gt;
{
    //This starts on a background thread.
    Debug .WriteLine( "From background thread. Does not block main thread." );
    Debug .WriteLine( "Calculating..." );
    Thread .Sleep( 5000 );
    Debug .WriteLine( "Background work completed." );
});
_Observer . Wait();
Debug .WriteLine( "Form1() End." );

Reactive Framework인데 생각보다 사용법도 복잡하고, 최근에는 update도 잘 이루어 지지 않아 비추 합니다.

하지만 이런게 있다는 것만 알면 될듯 하네요.