태그 보관물: c++

CDockablePane에 Dialog 등록

http://cafe.naver.com/ssua/517

CDockablePanel에 Dialog를 등록하여 사용할경우 컨트롤러를 직접 생성하는것보다 편리하다.

처리해야할 부분

기본적으로 CDockablePanel를 상속받아 Panel을 만들고 OnCreate 에서 다이얼로그를 생성하여 등록 하면

되지만 부가적으로 해야할 처리들이 몇가지 존재한다.

1. 페널의 사이즈가 변경될시 다이얼로그의 사이즈도 변경해 줘야 한다.

2. 페널이 포커스되면 다이얼로그에  포커스를 설정해 줘야 한다.

3. 페널위 다이얼로그에서 마우스 오른쪽 클릭시에도 DockablePane Context menu (도킹관련 팝업메뉴)가 떠버려

다이얼로그를 가리게 되며 다이얼로그 위에서 마우스 왼쪽 클릭을 해도 사라지지 않는 문제점이 발생한다

이를 위해 Context Menu를 차단 해줘야 한다.

세부 작업 설명

1. 다이얼로그 생성한후 아래와 같이 셋팅하자.

Dialog.Properties.Border = None

Dialog.Properties.Style = Child

2. 페널 생성.

클래스 위자드->MFC클래스-> Base Class(상속받을 클래스)에 CDockablePane 설정후 페널 생성.

3. 페널에 다이얼로그 등록.

페널에 WM_CREATE 메시지 핸들러 등록.

int DockingPane::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDockablePane::OnCreate(lpCreateStruct) == -1)
return -1;

// TODO:  Add your specialized creation code here
BOOL bRet = m_Dialog.Create(IDD_TEST_DOCKING, this);
ASSERT( bRet );
m_Dialog.ShowWindow(SW_SHOW);

return 0;
}

4. 사이즈 변화에 대한 다이얼로그 적용.

페널에  WM_SIZE 메시지 핸들러 등록.

void DockingPane::OnSize(UINT nType, int cx, int cy)
{
CDockablePane::OnSize(nType, cx, cy);

// TODO: Add your message handler code here
m_Dialog.MoveWindow( 0, 0, cx, cy );
 //m_Dialog.SetWindowPos (NULL, -1, -1, cx, cy, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
}

5. 페널이 포커스시 다이얼로그포커스 설정.

페널에 WM_SETFOCUS 메시지 핸들러 등록.

void DockingPane::OnSetFocus(CWnd* pOldWnd)
{
CDockablePane::OnSetFocus(pOldWnd);

// TODO: Add your message handler code here
 m_Dialog.SetFocus();
}

6. DockablePane Context menu 차단.

OnShowControlBarMenu 함수를 오버라이딩하여 Base클래스에서의 처리를 막아버린다.

//: h

virtual BOOL OnShowControlBarMenu(CPoint point);

//: cpp

BOOL DockingPane::OnShowControlBarMenu( CPoint point )
{
return FALSE;
}

기타

! vs2010 에서는 다이얼로그 페널(CPaneDialog)을 자체적으로 지원하는 걸로 알고 있다.

Drag & Drop기능

[참조사이트]http://blog.naver.com/iperfume/100048381739

1. 파일 드래그앤 드롭했을때 파일명과 경로를 입력하는방법

http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=278&MAEULNo=20&no=21664&ref=21664

2. codeproject 자료

http://www.codeproject.com/clipboard/ddmgr.asp

3. Devpia 자료

3-1. http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=50&MAEULNo=20&no=494120&ref=494120

3-2. http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=50&MAEULNo=20&no=466864&ref=466864

4. 탐색기에서 응용프로그램으로 드래그 앤 드롭

http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=50&MAEULNo=20&no=469642&ref=469609

Drag & Drop은 주로 두개의 서로 다른 프로그램 사이에서 일어나는 과정이므로 COM 인터페이스를 이용해 표준화 되어있다.

IDropSource, IDropTarger, IDataObject 인터페이스를 통해 구현하도록 되어 있으며 RegisterDragDrop, DoDragDrop이라는 API함수를 호출하여 Drap&Drop을 시작한다.

DoDragDrop함수가 호출된 이후의 나머지 과정(DragEnter, DragOver, Drop, DragLeave)은 운영체제가 맡아서 처리해준다.

MFC에서는 Drag&Drop을 쉽게 구현할 수 있도록 COleDropSource, COleDropTarget, COleDataObject클래스를 제공하고 있다.

5. Drag&Drop 으로 URL얻어오기(소스포함)

  – 인터넷 익스플로우에서 응용프로그램으로 Drag&Drop을 했을때 URL얻어오는 방법

http://www.codeguru.com/Cpp/I-N/internet/article.php/c3363

6. ListCtrl간 드래그앤 드롭

http://blog.naver.com/sjhghj/140104051130

 

Size가 0인 배열은 도대체 무엇에 쓰이는 물건인가?

http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=51&MAEULNO=20&no=7804&page=3

Size가 0인 배열은 도대체 무엇에 쓰이는 물건인가?

 

 

                                        작성자 고임

                                        최초 작성일 : 2007년 11월 30일 ~

                                        마지막 작성일 2007년 12월 5

 

– 들어가기

 

  우리는 컴퓨터 언어의 표준을 따르며 험난한 코딩의 나날을 보내고 있다매일 매일 새로운 기술 홍수 속을 키보드와 마우스로 헤쳐나가며코딩의 앞길마다 떨어지는 버그를 씹어 삼키며코딩의 기술 연마하고 있다.

 

고임 역시 중후한 표준의 사명을 따르며코딩을 하던 중컴파일러는 내게 다음과 같은 경고를 보내왔다.

warning C4200: nonstandard extension used : zero-sized array in struct/union (.cpp)

 

물론 실행하는데는 문제가 없으나 컴파일러가 도전장을 내밀듯이 불쑥 튀어나온 경고는 기분이 나쁘지 않는가?

 

한동안 위와 같은 경우를 겪어보지 않아적잖히 당황을 했지만이내 몇 년전 구글의 포럼의 쓰레드에서 사이즈가 0인 배열의 글타래를 기억해냈다물론 비표준이었는지 아니었는지 기억이 가물가물하여 내가 알고 있는 프로그래머 중 가장 파워풀한 지식과 다이나믹한 코딩 스킬을 가지고 계신 분에게 여쭈어보았다.

 

고임 사이즈가 0인 배열은 표준이 아니죠?

그분 표준은 아니지만 대형 벤더들은 전부 지원해요.

 

  그때의 다시금 기억을 되살리려 노력을 한다그러나금붕어도 울고갈 기억력의 소유자인 고임은 “후후 내가 기억할리가 없잖아!”라며.. 너무 쉽게 인정해버리고 자료를 찾아본다그러나 별다른 자료가 없다예전에 보았던 구글에서의 쓰레드는 어디로 갔는지 잘 찾질 못하겠고.. 다만 기억이 있는 것은 그 글타래에서 나왔던 이야기 중에 이런게 된다정도.. 였던 걸로 기억이 된다그 당시에는 사실 별 관심은 두지 않았다.

 

 그리하여 내가 하고 있는 프로젝트에서 해당 경고가 나온 줄을 찾아본다그것을 기반으로 하여 사이즈가 0인 배열의 정체를 파혜쳐 보기로 하였다.

 

 이런 식의 강좌 글은 느낌 상으로 수백년만에 다시 써보는 것 같아조금은 설레이나.. 할 것이 태산인 상황에서 이런 짓을 하니 조금은 눈치가 보이기는 한다.

 

 허나벤더사가 자신있게 내놓은 라이브러리에 비표준이 존재한다는 것에 무슨 의미가 있지나 않을까하는 강항 의구심을 해결하기 위하여 이 거대하고도 멋지고도 다 알고 나면 허무 개그를 본 듯한 주제를 가지고 달려보도록 하자.

 

 

– 사이즈가 0인 배열의 비표준의 경고와 에러 사이.

 

사이즈가 0인 배열이라고 하는 것은 단순히 다음과 같다.

int nZeroArray[0];

 

 즉 배열을 선언하는 순간 그 배열의 크기가 0이라는 것을 이야기 한다.

그러나 이것은 비표준이다따라서 당연히 에러가 발생이 된다상식적으로 크기가 0인 배열을 선언한다는 것이 말이 안되는 것 아닌가사이즈가 0인데 뭐하러 선언하는가?

 

그래서 우리의 컴파일러께서는 다음과 같은 에러 문장을 내주신다.

 

error C2466: 상수크기 0의 배열을 할당할 수 없습니다.

 

의역하자면 “장난하냐크기가 0인 배열을 어디에 위치시키라고?” 크기가 없기에 실존할 수 없다는 철학적인 메세지를 주시는 것이다.

 

여기서 우리가 눈치를 채야 할 것은 바로 사이즈가 0인 배열은 독립적으로 사용할 수 없다 라는 사실이다.

자 반복 학습이 중요하다.

사이즈가 0인 배열은 절대 독립적으로 사용할 수 없다.

 

자 그렇다면 이건 어디서 사용해야하는 것일까?

이것은 눈치가 빠른 분들은 아시겠지만구조체 공용체 클래스에서 사용할 수 있다뭐 사실 그것 밖에 더 있겠는가? -_-;

 

그 이유는 나중에 이야기 하기로 하고다음을 보도록 하자.

 

union UZeroArray{

     int nZeroArray[0];

};

struct SZeroArray{

     int nZeroArray[0];

};

class CZeroArray{

     int nZeroArray[0];

};

 

이 세 개의 구조체공용체클래스(이거 한글 표현이 뭐지?)안에는 멤버로써 크기가 0인 배열을 갖는다.

이 삼총사는 컴파일링의 시험에 간신히 합격하시며다음과 같은 코멘트를 달고 빌드가 되신다.

 

warning C4200: 비표준 확장이 사용됨구조체/공용구조체의 배열 크기가 0입니다.

UDT에 크기가 0인 배열이 있는 경우 복사 생성자 또는 복사 할당 연산자를 생성할 수 없습니다.

 

이 경고는 봐주긴 봐주는데일반적으로 사용하면 죽어! 라는 의미를 갖는다따라서 사이즈가 0인 배열은 용도가 매우 좁으니까너가 신경써서 사용하라는 이야기이다.

 

자 여기서 강좌 예제의 범위를 축소하기로 한다공용체나 클래스의 예제는 과감히 건너뛰기로 하고 구조체에 대해서만 살피도록 하겠다. (알고 싶은 분은 직접 코딩해서 알아보시길.)

 

 

struct SEndZeroArray{

     int ndummy;

     int nZeroArray[0];

};

struct SStartZeroArray{

     int nZeroArray[0];

     int ndummyEnd;

};

struct SMiddleZeroArray{

     int ndummyStart;

     int nZeroArray[0];

     int ndummyEnd;

};

struct STwoZeroArray{

     int ndummy;

     int nZeroArray1[0];

     int nZeroArray[0];

};

 

위의 크기가 0인 배열의 예제들이 아주 훌륭히 짜여져 있다여기서 컴파일 에러를 내지 않는 것은 무엇일까?

컴파일을 해보면 알겠지만첫 번째 예제만이 무난히 컴파일링을 통과하시게 된다나머지 세 개의 예제는 다음과 같은 에러를 당하시게 된다.

error C2229: struct ‘SstartZeroArray’에 크기가 0인 잘못된 배열이 있습니다.

 

사이즈가 0인 배열의 멤버는 공용체구조체클래스의 선언에서 가장 마지막에 선언을 해줘야하며해당 구조체공용체클래스에서 유일한 멤버변수여야 한다는 이야기이다여기서 매우 중요한 이야기가 두 개나 나오게 된다자 기억하자!!

 

사이즈가 0인 배열은 공용체구조체클래스 안에서 가장 마지막 멤버 변수로 선언되어야 한다.

사이즈가 0인 배열이 선언이 되는 공용체구조체클래스 안에서 오직 하나여야 한다.

 

여기까지 사이즈가 0인 배열을 에러를 피하기 위해 어떤 식으로 선언을 해야할지를 알아보았다세줄로 요약하면 다음과 같다이것을 명심하고 다음 단계로 넘어가기로 하자.

 

사이즈가 0인 배열은 절대 독립적으로 사용할 수 없다.

사이즈가 0인 배열은 공용체구조체클래스 안에서 가장 마지막 멤버 변수로 선언되어야 한다.

사이즈가 0인 배열이 선언이 되는 공용체구조체클래스 안에서 오직 하나여야 한다.

 

 

– 버그의 경계를 넘어..

 

자 여기서 부터는 왜 이런 비표준을 여전히 아직도 쓰고 있는지에 대해서 알아보겠다라고는 했지만역으로 추적하면서 이 강좌를 쓰는 것이므로 역사적인 배경은 전혀 알 수 없다그리고 역시 C99이나 새로 계정된 C++ 표준에 반영이 되려 했는지도 알 수 없다최근에 보았던 C99에 자료에서는 보지 못했던 것 같다사실 이런 기법에 대해서 관심이 없었다고 하는 것이 더 맞는 표현이겠지만말이다.

 

기존에 MS가 제공하는 라이브러리의 헤더에서 보여지는 크기가 0인 배열의 흔적을 거꾸로 찾아보며 느낀 점을 바탕으로 만들어진 예제를 가지고 왜 이런 비표준을 아직도 사용하는지에 대해서 이야기 해보도록 한다. (사실 크기가 0인 배열을 활용할 수 있는 기법은 하나 밖엔 없을 것으로 확신한다. )

 

 

자 시작해 보도록 합시다.

아래 예제는 일반적으로 어떤 정보 목록의 크기가 정적으로 정해져 있지 않고, 동적으로 변경이 될 가능성이 높을 때 사용되는 일반적인 정보 목록 관리 방법과 크기가 0인 배열을 이용한 정보 목록 방법을 비교하여크기가 0인 배열을 사용하면 어떤 이득이 있는지에 대해 알아보는 시간을 갖도록 하겠다.

 

다시 쉬운 문장으로 이야기 하자면, 아.. 예전엔 쉽게 잘(-_-;) 썼던 것 같은데. 음…..

예를 들어,

공연 티켓을 사는 사람들의 성향을 알아보기 위한 프로그램을 설계한다고 가정하자.

(여기서 Db를 사용하는 건 제외합시다.)

 

기본적으로 공연 티켓을 사는 사람들의 정보는 이름나이구매티켓수지역구입처 등등이 있을텐데.

구매하는 사람에 대한 정보를 담을 구조체가 필요할테고,

이 정보를 통합 관리할 구조체가 또 필요하다.

 

바로 이런 경우의 상황에 대해서 앞으로 논의할 것이다.

 

아래 예제를 보시면서,

 

기술적으로 저건 올바른 방법이 아니네.

이렇게 해야 더 효율적이네.

저게 무슨 일반적인 방법이야?

크기가 0인 배열은 됐구요도는 믿고 계십니까?

저 예제는 어디서 본 것 같아당신 표절 쟁이지?

 

라고 평하실 분들.. 여기서 읽기를 그만두시길 바랍니다.

더 읽어봤자 서로 정신건강에 해롭습니다.

 

다시 한번 강조하지만, 이 강좌는 “크기가 0인 배열이 무엇인가?”를 알아보는 것이다.

설명하기 편하게 UNICODE는 사용하지 않았다는 걸 미리 이야기한다.

 

예제의 빌드 환경

OS : Windows XP Professional

Complier : VS2005

CPU : Intel Core 2 6600

 

 

typedef struct tagVirtualDeviceInfo

{

     int DummyN;

     char DummyString[30];

     float DummyFloat;

}VirtualDeviceInfo, * pVirtualDeviceInfo;

Device의 정보를 담을 구조체.

 

typedef struct tagVirtualDevice

{

     int DeviceSize; // 가상장치의 갯수.

     pVirtualDeviceInfo Info; // 가상장치의 정보에 대한 주소

}VirtualDevice, *pVirtualDevice;

Device 정보를 통합 관리할 구조체일반적인 방법)

 

이 데이타 구조는 여러개의 가상 장치의 정보를 가상 장치 정보의 주소를 담을 수 있도록 했으며데이타 구조에서 관리되는 가상 장치의 정보의 갯수도 기록할 수 있겠금 구성되어있다.

 

이 구조가 우리가 흔히 접할 수 있는 구조이며이런 형태로 구성하는 것이 거의 정석이라 할 수 있겠다.

 

typedef struct tagVirtualDeviceZeroSize

{

    int DeviceSize; // 가상 장치의 갯수.

     VirtualDeviceInfo Info[0]; // 바로 이 분이 이 강좌의 주인공이심.

}VirtualDeviceZeroSize, * pVirtualDeviceZeroSize;

Device 정보를 통합 관리할 구조체크기가 0인 배열을 이용한 방법)

 

이 데이타 구조는 2번째의 데이타와 구조와 마찬가지이지만가상 장치 정보를 기록하는 멤버 변수가 크기가 0인 배열로 선언이 되어 있다.

 

처음 보는 사람들은 아마 “이 뭐병” 이란 단말마를 내실지 모르겠으나이 코드는 굳건히 컴파일링을 통과하십니다.

 

 

01 : void ZeroLengthArrayTest(void)

02 : {

03 : int nDeviceNumber = 2;

여기서 가상 장치가 2개만 있다고 생각하기로 하자.

 

04 : int nSizeVirtualDeviceInfo = sizeof(VirtualDeviceInfo);

05 : int nSizeVirtualDevice = sizeof(VirtualDevice);

06 : int nSizeVirtualDeviceZero = sizeof(VirtualDeviceZeroSize);

 

07 : int nSizeVirtualDeviceMem = nSizeVirtualDevice;

08 : int nSizeVirtualMemZero = sizeof(VirtualDeviceZeroSize) + sizeof(VirtualDeviceInfo)*nDeviceNumber;

 

 

 

여기서 우리는 VirtualDevice 크기가 8바이트이고, VirtualDeviceZeroSize의 크기가 4바이트 라는 것을 눈여겨봐야한다. VirtualDeivce의구조는Device의 목록의 갯수와 DeviceInfo의 정보를 가리키는 포인터로 구성되어 있으므로당연히 8바이트가 되게 된다. (물론 32비트 OS 상에서 32비트 어플리케이션을 제작할때의 이야기이다설마 이런 것도 트집 잡으려나? )

 

VirtualDeviceZeroSize는 Device의 목록의 갯수를 저장하는 변수가 4바이트 DeviceInfo의 정보를 저장하는 배열의 크기가 0이다.

 

이런 걸 처음 보는 사람은 리엑션을 크게 취해주도록 하자그래야 기억에 남는다고 하더라.

 

허허 그렇담 VirtualDeviceInfo Info[0]; 는 어디에 저장이 될 것인가?

그 이야기는 잠깐 뒤에서 이야기하도록 한다.

 

일반적인 방법구조체 초기화

 

09 : pVirtualDevice pVD1 = new VirtualDevice;

Virtual Device의 목록을 관리할 데이타를 생성합니다.

 

10 : VirtualDeviceInfo* pVirtualDeviceMemInfo = new VirtualDeviceInfo[nDeviceNumber];

11 : memset(pVirtualDeviceMemInfo, 0, nDeviceNumber* sizeof(VirtualDeviceInfo));

Virtual Device의 정보를 기록할 메모리를 확보하고 초기화한다.

 

12 : pVD1->DeviceSize = nDeviceNumber;

디바이스의 갯수를 기록한다.

 

13 : pVD1->Info = pVirtualDeviceMemInfo;

VirtualDeviceInfo를 가리키는 멤버 변수를 초기화시킵니다이 부분은 크기가 0인 배열을 이용한 방법에선 필요 없는 코드가 된다.

 

크기가 0인 배열을 이용한 방법구조체 초기화

 

14 : void* pVirtualDeviceMemZero = new BYTE[nSizeVirtualDeviceMemZero];

15 :memset(pVirtualDeviceMemZero, 0, nSizeVirtualDeviceMemZero);

위와는 다르게 Virtual Device의 목록을 관리할 데이타와 Virtual Device의 정보를 나타내는 데이타의 크기를 선형적으로 한꺼번에 생성하고 초기화하는 것에 유의하며 봅시다.

이 부분이 바로 크기가 0인 배열을 사용하는 이유가 되며일반적인 방법과는 달리 VirtualDeviceInfo

를 가리키는 멤버 변수를 초기화 할 필요가 없다.

 

16 : pVirtualDeviceZeroSize pVD2 = (pVirtualDeviceZeroSize) (pVirtualDeviceMemZero) ;

 

자 위에서 생성한 메모리를 형변환하여 pVirtualDeviceZeroSize 형의 변수에 대입해보자이때 VirtualDeviceInfo를 가리키는 Info의 주소를 살펴보면정확히 pVD2라는 변수가 할당된 메모리 위치의 바로 다음을 가리키게 된다.

호오 놀랍지 않은가? 일반적인 방법의 13번째 줄 처럼 Info 변수에 초기화도 하지 않았는데 말이다.

이것이 가능한 이유는 바로 배열이 선형적인 메모리를 갖는다는 특성 때문이다.

 

 

위의 그림을 보면.. pVD2의 메모리 위치는 0x00ee9f30이고, pVD2->Info가 가리키는 메모리 위치는 0x00ee9f34이다정확히 4만큼의 offset을 가지고 있는 것을 확인 하도록 하자.

 

17 : pVD2->DeviceSize = nDeviceNumber;

디바이스 갯수를 기록한다.

 

 

일반적인 방법 데이타 입력

 

18 : pVD1->Info[0].DummyN = 0x11;

메모리를 알아보기 쉽게 하기 위해 0x11을 삽입하도록 하자.

 

19 :memcpy(pVD1->Info[0].DummyString, “Virtual Device 1”sizeof(“Virtual Device 1”) + 1);

20 : pVD1->Info[1].DummyN = 0x22;

21 :memcpy(pVD1->Info[1].DummyString, “Virtual Device 2”sizeof(“Virtual Device 2”) + 1);

 

크기가 0인 배열을 이용한 방법데이타 입력

 

22 : pVD2->Info[0].DummyN = 0x11;

23 :memcpy(pVD2->Info[0].DummyString, “Virtual Device Zero 1”sizeof(“Virtual Device Zero 1”) + 1);

24 : pVD2->Info[1].DummyN = 0x22;

25 :memcpy(pVD2->Info[1].DummyString, “Virtual Device Zero 2”sizeof(“Virtual Device Zero 2”) + 1);

 

비록 크기가 0인 배열이라고 선언을 하였더라도배열의 이름인 Info가 배열이 시작되는 지점을

가리키는 주소가 되므로, pVD2 메모리 위치 뒤에 VirtualDeviceInfo 크기를 기준으로 배열의 크기가

확보된 상태에선 배열처럼 사용이 가능하다.

 

또 다른 장점으로는 어셈 코드가 간단해진다는 장점이 존재한다.

구조체에서 맴버 변수로 접근을 할때는 구조체 변수의 주소를 알아내고 나서해당 구조체의 멤버 변수의 주소를 알아낸 뒤 접근을 하게 되는데.

 

크기가 0인 배열을 이용하게 되면크기가 0인 멤버 변수가 메모리에 생성 되지 않기 때문에 Info란 맴버 변수는 코드상으로만 존재할 뿐 컴파일을 하게 될 때는 그저 offset으로만 인식하게 된다.

따라서 위의 일반적인 방법보다 조금은 명령어가 적게 들게 된다.

 

26 : delete pVD1;

27 : delete [] pVirtualDeviceMemInfo;

28 : delete [] pVirtualDeviceMemZero;

37 : }

 

정리하자면 크기가 0인 배열은 선형적으로 크기가 변하는 경우에 사용할 수 있는 일종의 치트 문법이다.

배열이란 문법적인 특징을 아주 치사하게 이용하고 있다고나 해야 할까?

이걸 처음 이용한 사람이 누구인진 모르겠으나, 이 점에 대해선 기립박수를 쳐주고 싶다. .

뭐 어찌 되었던, 크기도 작고, 명령어도 적게 쓰면서 동일한 효과를 내게 되었으니 말이다.

 

C언어에서 기존에 많이 사용이 되고 있어서, C++에서는 C와의 호환 유지 차원에서 봐 준 것 같다.

사실 마이크로 콘트롤러 래벨로 내려가면 막강한 기법이라고 생각이 든다.

하지만, 트릭처럼 사용되는 문법이기 때문에 언제까지 이것을 유지할지는 확신은 들지 않는다.

문법상으로 명백히 틀린 부분은 없으나, 버그의 경계선에서 왔다 갔다 하는 코드를 유지해야 하는 것도

점점 강화되고 있는 문법 성향에 안 맞는 것 같기도 하고,

C언어는 모르겠지만 아마 앞으로는 C++ 언어에선 사라질지도 모를 일이라고 조심스레 예견해본다.

 

부록 – 예제 코드 전문

 

typedef struct tagVirtualDeviceInfo

{

int DummyN;

char DummyString[30];

float DummyFloat;

}VirtualDeviceInfo, * pVirtualDeviceInfo;

 

typedef struct tagVirtualDevice

{

int DeviceSize; // 가상장치의 갯수.

pVirtualDeviceInfo Info; // 가상장치의 정보에 대한 주소

}VirtualDevice, *pVirtualDevice;

 

typedef struct tagVirtualDeviceZeroSize

{

int DeviceSize; // 가상 장치의 갯수.

VirtualDeviceInfo Info[0];

}VirtualDeviceZeroSize, * pVirtualDeviceZeroSize;

 

 

void ZeroLengthArrayTest(void)

{

int nDeviceNumber = 2;

int nSizeVirtualDeviceInfo = sizeof(VirtualDeviceInfo);

int nSizeVirtualDevice = sizeof(VirtualDevice);

int nSizeVirtualDeviceZero = sizeof(VirtualDeviceZeroSize);

 

int nSizeVirtualDeviceMem = nSizeVirtualDevice;

int nSizeVirtualMemZero = sizeof(VirtualDeviceZeroSize) + sizeof(VirtualDeviceInfo)*nDeviceNumber;

 

pVirtualDevice pVD1 = new VirtualDevice;

VirtualDeviceInfo* pVirtualDeviceMemInfo = new VirtualDeviceInfo[nDeviceNumber];

memset(pVirtualDeviceMemInfo, 0, nDeviceNumber* sizeof(VirtualDeviceInfo));

pVD1->DeviceSize = nDeviceNumber;

pVD1->Info = pVirtualDeviceMemInfo;

 

void* pVirtualDeviceMemZero = new BYTE[nSizeVirtualDeviceMemZero];

memset(pVirtualDeviceMemZero, 0, nSizeVirtualDeviceMemZero);

pVirtualDeviceZeroSize pVD2 = (pVirtualDeviceZeroSize) (pVirtualDeviceMemZero) ;

pVD2->DeviceSize = nDeviceNumber;

 

pVD1->Info[0].DummyN = 0x11;

memcpy(pVD1->Info[0].DummyString, “Virtual Device 1”sizeof(“Virtual Device 1”) + 1);

pVD1->Info[1].DummyN = 0x22;

memcpy(pVD1->Info[1].DummyString, “Virtual Device 2”sizeof(“Virtual Device 2”) + 1);

 

pVD2->Info[0].DummyN = 0x11;

memcpy(pVD2->Info[0].DummyString, “Virtual Device Zero 1”sizeof(“Virtual Device Zero 1”) + 1);

pVD2->Info[1].DummyN = 0x22;

memcpy(pVD2->Info[1].DummyString, “Virtual Device Zero 2”sizeof(“Virtual Device Zero 2”) + 1);

 

delete pVD1;

delete [] pVirtualDeviceMemInfo;

delete [] pVirtualDeviceMemZero;

}

함수 포인터

문장(statement)이 정수, 혹은 실수처럼 어떤 값(value)을 가질 때 이러한 문장(statement)을 표현식(expression)이라고 한다. 문장은 ;으로 끝나므로 쉽게 구분 가능ㅎ다. 문장에서 ;을 제외한 부분이 표현식이 되려면, 그것이 값이어야 한다. 예를 들면, 2+3; 이라는 문장에서 2+3dms 5라는 값을 가지므로 표현식이다. 하지만 return; 이라는 문장에서 return은 값을 가지지 못하므로 표현식이 아니다. 표현식은 등호(=)의 오른쪽에 사용될 수 있음을 의미한다. 함수의 이름이 표현식임을 아는 것이 중요하다.

[code lang=”cpp”]
int sum(int a, int b)  {  return a + b; }

void main()
{
  int (*fp)(int, int);    // 함수 포인터 변수
  int i;

  fp = Sum;
  i = fp(2, 3);
  printf("%d \n", i);
}
[/code]

실행파일이 메모리에 로드되면 함수나 변수들이 고유한 메모리 주소를 가지게 된다. 이렇게 함수나 변수의 실제 주소를 결정하는 것을 바인딩이라고 한다.