정규식 쓸모가 많은데 활용을 잘 못하고 있다. 정리를 좀 하고 가자.
테스트 사이트 www.debuggex.com
최신목록
2018년 7월 19일 목요일
2017년 6월 28일 수요일
[perl] VS2015로 perl 빌드하기
perl 5.24.0을 사용하다가 기존 프로젝트 빌드 환경을 VS2015로 변경하면서 perl을 VS2015에서 빌드를 시도하였다.
perl 5.24.0은 VS2015 고려가 안되있어서 Makefile에 일일이
CCTYPE이 MSVC140일때 vcruntimed.lib와 ucrt.lib를 넣어주어야 한다.
귀찮다...perl 5.26.0은 처리가 다 되서 배포된다. ㅎ
일단 perl 5.26.0을 다운받고 압축을 풀면
아래와 같이 win32/Makefile에서 MSVC140을 활성화해준다.
CCTYPE = MSVC60 -> #CCTYPE = MSVC60
#CCTYPE = MSVC140 -> CCTYPE = MSVC140
그럼 일단 가이드하는 상의 셋팅은 끝이다.
그런데 빌드를 해보니 빌드가 되지 않는다.
perl-5.26.0/win32/perlhost.h에서 나는 에러다.
_iobuf 멤버를 correct_wstdio.h에서 참조하고 있다.
_base, _cnt는 보이지 않는다.
perlhost.h에서 가리키는 코드는
FILE_base를 define 한곳을 막아야한다.
perl-5.26.0/win32/config.h에 보면
두 군데가 활성화 되있다. USE_STDIO_PTR과 USE_STDIO_BASE의 정의되있어서 활성화되었던 코드이다. 이걸 막으면 일단 컴파일은 된다.
그런데 나는 압축 푸는과정에 cmake를 이용해서 자동으로 Makefile과 config.h의 코드를 수정해서 빌드를 하려고 하는데 압축 풀면 config.h가 없다. 빌드되는 과정에서 생성이 된다. 빌드 과정에 config.h가 없으면 config.vc에서 카피한다고 떠서 그런줄 알고 config.vc도 수정해서 해봐도 config.h에는 적용되지 않았다. 한참 헤매다 아래 두 파일을 이용해서 생성이 됨을 알 수 있었다.
- perl-5.26.0/config_h.SH
- perl-5.26.0/win32/config.vc
config.vc의 d_stdstdio, d_stdiobase 의 값을 'undef'로 변경해주면 된다.
perl 5.24.0은 VS2015 고려가 안되있어서 Makefile에 일일이
CCTYPE이 MSVC140일때 vcruntimed.lib와 ucrt.lib를 넣어주어야 한다.
귀찮다...perl 5.26.0은 처리가 다 되서 배포된다. ㅎ
일단 perl 5.26.0을 다운받고 압축을 풀면
아래와 같이 win32/Makefile에서 MSVC140을 활성화해준다.
CCTYPE = MSVC60 -> #CCTYPE = MSVC60
#CCTYPE = MSVC140 -> CCTYPE = MSVC140
그럼 일단 가이드하는 상의 셋팅은 끝이다.
그런데 빌드를 해보니 빌드가 되지 않는다.
perl-5.26.0/win32/perlhost.h에서 나는 에러다.
_iobuf 멤버를 correct_wstdio.h에서 참조하고 있다.
#ifndef _FILE_DEFINED
#define _FILE_DEFINED
typedef struct _iobuf
{
void* _Placeholder;
} FILE;
#endif
_base, _cnt는 보이지 않는다.
perlhost.h에서 가리키는 코드는
#ifdef FILE_base
FILE *f = pf;
return FILE_base(f);
#else
return NULL;
#endif
FILE_base를 define 한곳을 막아야한다.
perl-5.26.0/win32/config.h에 보면
#define USE_STDIO_PTR /**/ #ifdef USE_STDIO_PTR #define FILE_ptr(fp) ((fp)->_ptr) #define STDIO_PTR_LVALUE /**/ #define FILE_cnt(fp) ((fp)->_cnt) #define STDIO_CNT_LVALUE /**/ /*#define STDIO_PTR_LVAL_SETS_CNT / **/ #define STDIO_PTR_LVAL_NOCHANGE_CNT /**/ #endif
#define USE_STDIO_BASE /**/ #ifdef USE_STDIO_BASE #define FILE_base(fp) ((fp)->_base) #define FILE_bufsiz(fp) ((fp)->_cnt + (fp)->_ptr - (fp)->_base) #endif
두 군데가 활성화 되있다. USE_STDIO_PTR과 USE_STDIO_BASE의 정의되있어서 활성화되었던 코드이다. 이걸 막으면 일단 컴파일은 된다.
그런데 나는 압축 푸는과정에 cmake를 이용해서 자동으로 Makefile과 config.h의 코드를 수정해서 빌드를 하려고 하는데 압축 풀면 config.h가 없다. 빌드되는 과정에서 생성이 된다. 빌드 과정에 config.h가 없으면 config.vc에서 카피한다고 떠서 그런줄 알고 config.vc도 수정해서 해봐도 config.h에는 적용되지 않았다. 한참 헤매다 아래 두 파일을 이용해서 생성이 됨을 알 수 있었다.
- perl-5.26.0/config_h.SH
- perl-5.26.0/win32/config.vc
config.vc의 d_stdstdio, d_stdiobase 의 값을 'undef'로 변경해주면 된다.
[perl]@INC에 경로 추가하기
perl 5.26.0으로 업데이트 하는데 갑자기 current directory의 pm을 찾지 못하였다.
perl -V로 configuration을 확인한 결과 @INC에 current direcotry가 빠져있다.
5.26.0으로 오면서 빠졌다고 되있다
http://blogs.perl.org/users/sawyer_x/2017/05/perl-5260-is-now-available.html
친절하게 @INC에 current directory 추가하는 방법도 있다.
perl -V로 configuration을 확인한 결과 @INC에 current direcotry가 빠져있다.
5.26.0으로 오면서 빠졌다고 되있다
http://blogs.perl.org/users/sawyer_x/2017/05/perl-5260-is-now-available.html
친절하게 @INC에 current directory 추가하는 방법도 있다.
2017년 4월 28일 금요일
[linux][symbol]리눅스 바이너리 심볼 관련 옵션
윈도우즈는 debug/release가 명확한데 linux는 심볼이 일단 그냥 들어가있다. 음...
근데 또 심볼을 추가하는 옵션이 있긴한데 도대체 무슨 차이일까?
gcc compile
> gcc test.c -o nosym.out(기본적으로 심볼이 있다. 제한적인)
> gcc -s test.c -o strip.out(공용라이브러리 정보 외 심볼이 없다)
> gcc -g test.c -o sym.out(심볼이 모두 들어있다)
> gcc test.c
> strip a.out(기본심볼이 들어있었으나 심볼 제거)
위의 결과물이 아래와 같이 나왔다.
이제 차이를 좀 보자.
심볼이 보임
-g 를 붙이면 뭘 더 할수 있을까?
nosym.out을 디버깅을 시작해보자. 뭔가 부족함이 느껴지겠지 ㅎ
변수와 소스 등이 심볼테이블이 없다고 보이지 않는다.
sym.out을 디버깅해보자.
요차이군요 ㅎ 다보임~
근데 또 심볼을 추가하는 옵션이 있긴한데 도대체 무슨 차이일까?
gcc compile
> gcc test.c -o nosym.out(기본적으로 심볼이 있다. 제한적인)
> gcc -s test.c -o strip.out(공용라이브러리 정보 외 심볼이 없다)
> gcc -g test.c -o sym.out(심볼이 모두 들어있다)
> gcc test.c
> strip a.out(기본심볼이 들어있었으나 심볼 제거)
위의 결과물이 아래와 같이 나왔다.
이제 차이를 좀 보자.
심볼이 보임
심볼이 없고 공유 라이브러리에 대한 정보정도는 보임심볼이 보임
-g 를 붙이면 뭘 더 할수 있을까?
nosym.out을 디버깅을 시작해보자. 뭔가 부족함이 느껴지겠지 ㅎ
변수와 소스 등이 심볼테이블이 없다고 보이지 않는다.
sym.out을 디버깅해보자.
요차이군요 ㅎ 다보임~
2017년 3월 30일 목요일
[파일로그]파일 시스템 미니 필터 드라이버(minispy)
이전에는 파일로그 구현시 파일 필터 드라이버를 만들었었다. 그러다 드라이버가 가지는 리스크를 조금이라도 줄여보기 위해서 미니필터 드라이버를 지원하여 callback 형식으로 처리를 하게 하였다. WDK에 보면 이 드라이버 샘플이 존재하는데 이것이 minispy이다.
이 샘플을 빌드하는건 정리를 하지 못하였다. 이 글을 쓸 당시 기존에 빌드하던 드라이버를 다시 사용하려니 정리를 안해놔서 헤맨 상태여서 헤맨 부분만 일단 정리한다.
현재 테스트 하고 싶은 부분은
ondemand 타입의 드라이버에서 메모리 릭이 발생하면 드라이버 unload 시 메모리가 free가 되는가?
이부분은 나중에 테스트 해서 다시 올리도록 하겠다.
지금은 minispy 로드하고 start 하고 상태 확인 하는 방법...정도??ㅎㅎ
일단 MS에서 fltmc라는 기본적인 테스팅 툴을 제공한다.
이를 이용하면 driver의 상태를 쉽게 확인할 수 있다.
fltmc는 현재 설치되고 로드된 필터 드라이버들을 보여준다.
minispy는 존재하지 않는다. minispy.inf를 이용하여 설치 후에도 필터 드라이버는 보이지 않는다. 설치 확인은 msinfo32.exe를 이용하여 확인이 가능하고 sc query minispy 를 이용하여도 상태를 확인할 수 있다.
driver 상태 확인
> sc query driver_name
driver 시작
> sc start driver_name
또는
> fltmc load driver_name
driver 중지
> sc stop driver_name
driver 제거
> sc delete driver_name
2017년 3월 15일 수요일
[C/C++]Name Mangling 과 GetProcAddress
이기종이 사용하는 라이브러리를 만드는 미션~ 윈도우 프로그래밍에 익숙해서 당연한 거라 여겨졌던 부분들에 대한 내용이다.
1. __declspec(dllexport) 를 사용하여 함수를 export 해야한다 ?
이것은 MS에서 만든 키워드로 unix/linux에는 사용할 수 없는 코드이다. 아래와 같이 구분하여 정의를 해준다.
2. def 파일로 define을 해주어야 한다? extern "C"는 C코드에서 사용하기 위해서 넣어진 코드이다?
_declspec(dllexport)를 사용하면 def 파일을 따로 만들어주지 않아도 함수가 export 된다. 하지만 depends 로 보면 아래와 같이 name mangling 이 적용된다.
C++에서 import 할거면 이대로 좋은거라 생각하고 Loadlibrary와 GetProcAddress를 이용하여 로드 하였으나 Error code 0x7f (ERROR_PROC_NOT_FOUND) 를 내놓는다. GetProcAddress로는 C type 의 name만을 콜할 수 있고 mangling된 풀네임으로 콜 할 수는 있다.
GetProcAddress("EncodeBase64@@YAHPBDP...") (앞에 ?를 붙여야되나?? ㅎ)
그러므로 extern "C"를 해주거나 def 파일을 정의해주어서 mangling을 피한다. Visual C++ compiler에서는 원래 C 코드로 컴파일하도록 설정(c/c++->고급->컴파일옵션) 하여 이를 대신할 수도 있다. extern C는 C++에서만 사용할 수 있는 코드로 C 코드로 컴파일시는 extern "C"가 [Error C2059 구문 오류 : 문자열] 이란 에러를 생성한다.
결론적으로 아래와 같은 선언문 하나면 def 파일이나 C 코드로 컴파일 옵션 없이 export가 된다.
**__stdcall과 __cdecl중 무엇을 사용해야하는지는 선택이며 caller가 맞는 형식으로 로드하면 된다. [Name Mangling 과 calling convention] 을 참고한다.
depends로 본 결과는 아래와 같다.
그런데 이기종은 def파일을 사용해야하는건가? 이건또 다음에 정리~
이제 caller에서는
typedef int ( __stdcall *PFN_ENCODEBASE64)(const char* in_str, char* out_str, size_t& out_size);
이렇게 dynamic load 시 함수포인터 얻는다~
1. __declspec(dllexport) 를 사용하여 함수를 export 해야한다 ?
이것은 MS에서 만든 키워드로 unix/linux에는 사용할 수 없는 코드이다. 아래와 같이 구분하여 정의를 해준다.
#ifdef _WIN32
# define EXPORT_API(ret_type) __declspec(dllexport) ret_type
#else //WIN32
# define EXPORT_API(ret_type) ret_type
#endif //WIN32
EXPORT_API(int) EncodeBase64(const char* in_str, char* out_str, size_t* out_size)
{
.....
}
2. def 파일로 define을 해주어야 한다? extern "C"는 C코드에서 사용하기 위해서 넣어진 코드이다?
_declspec(dllexport)를 사용하면 def 파일을 따로 만들어주지 않아도 함수가 export 된다. 하지만 depends 로 보면 아래와 같이 name mangling 이 적용된다.
C++에서 import 할거면 이대로 좋은거라 생각하고 Loadlibrary와 GetProcAddress를 이용하여 로드 하였으나 Error code 0x7f (ERROR_PROC_NOT_FOUND) 를 내놓는다. GetProcAddress로는 C type 의 name만을 콜할 수 있고 mangling된 풀네임으로 콜 할 수는 있다.
GetProcAddress("EncodeBase64@@YAHPBDP...") (앞에 ?를 붙여야되나?? ㅎ)
그러므로 extern "C"를 해주거나 def 파일을 정의해주어서 mangling을 피한다. Visual C++ compiler에서는 원래 C 코드로 컴파일하도록 설정(c/c++->고급->컴파일옵션) 하여 이를 대신할 수도 있다. extern C는 C++에서만 사용할 수 있는 코드로 C 코드로 컴파일시는 extern "C"가 [Error C2059 구문 오류 : 문자열] 이란 에러를 생성한다.
결론적으로 아래와 같은 선언문 하나면 def 파일이나 C 코드로 컴파일 옵션 없이 export가 된다.
#ifdef __cplusplus # define EXTERN_C extern "C" #else # define EXTERN_C #endif #ifdef _WIN32 # define EXPORT_API(ret_type) EXTERN_C __declspec(dllexport) ret_type __stdcall #else //WIN32 # define EXPORT_API(ret_type) EXTERN_C ret_type #endif //WIN32
**__stdcall과 __cdecl중 무엇을 사용해야하는지는 선택이며 caller가 맞는 형식으로 로드하면 된다. [Name Mangling 과 calling convention] 을 참고한다.
depends로 본 결과는 아래와 같다.
그런데 이기종은 def파일을 사용해야하는건가? 이건또 다음에 정리~
이제 caller에서는
typedef int ( __stdcall *PFN_ENCODEBASE64)(const char* in_str, char* out_str, size_t& out_size);
m_pfnEncodebase64 = (PFN_ENCODEBASE64) GetProcAddress(m_hModule, "EncodeBase64");
이렇게 dynamic load 시 함수포인터 얻는다~
2016년 12월 1일 목요일
[C/C++]Name Mangling 과 calling convention
c 로 static library를 하나 만들고 c project에서 함수를 call 하는 상황에서 당연히 Name Mangling 문제가 없어야 하는데...
library에 분명 해당 함수가 존재하는데도 함수 호출시 외부 기호에서 확인하지 못했다는 링크 에러가 발생했다.
dumpbin을 사용하여 library에서의 함수 이름을 확인했더니
_test 라고 되있고 호출할때 에러에서는 _test@0 이라고 호출을 하고 있다.
Mangling 문제가 발생하였다...음...뭘까요?
XXX@0 이거는 C++에서 Name Mangling 이 적용된거 같은데....
자세히 보면 또 Name Mangling 치고는 또 너무 단순하다.
보통은 XXX@IHKGDA 뭐 이런식으로 좀 더 복잡해야하는데...
원인은 __stdcall과 __cdecl 의 차이였다.
C형식으로 잘 컴파일 되었으나 __cdecl형식으로 dll export시는 함수 이름이 그대로 생기고
__stdcall로 dll export시는 XXX@N 이런식으로 뒤에 숫자가 더 붙었다.
아래는 teststdcall 함수를 __stdcall 형식으로 export 하고 testcdecl 함수를 __cdecl 형식으로 export 하여 두개의 library 안에서의 Naming을 dumpbin을 이용하여 확인하였다.
teststdcall함수는 teststdcall@0로 이름이 생기고 testcdecl 함수는 testcdecl 그래도 이름이 되있음을 볼수 있다. calling convention의 차이가 심볼이름에도 영향을 미치고 있었다.
2016년 4월 28일 목요일
사용자 계정 정보 얻기
USER_MODE
1. 현재 스레드의 계정 정보를 구할 수 있는 API 제공(GetUserName, LsaGetUserName등등)
2. 스레드의 토큰을 이용하는 방법
- OpenProcessToken, OpenThreadToken
- GetTokenInformation(HANDLE tokenhandle, TokenUser,.....)
3. SID 를 이용하는 방법
- LookupAccountSid
KERNEL_MODE
1. 스레드 토큰 이용
- 스레드토큰을 이용해서 사용자 토큰을 얻음
RtlCopySid 이용하여 전달함.
2. driver의 callback 데이타를 이용함.(mini filter driver)
PACCESS_TOKEN pToken;
PTOKEN_USER pUser = NULL;
pToken = SeQuerySubjectContextToken(&(Data->Iopb->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext));
SeQueryInformationToken(pToken, TokenUser, &pUser);
........
ExFreePool(pUser);
2016년 4월 21일 목요일
[WINDOWS]FltGetFileNameInformation 시 BAD_POOL_CALLER 발생
minifilter dirver 개발시 pre callback에서 context를 만들어서 post callback으로 넘겨주기 위해 ExAllocatePoolWithTag를 이용하여 allocation 하여
FltGetFileNameInformation 파라미터로 넘기고
그 포인터를 post callback에서 ExFreePool 하였을대 BAD_POOL_CALLER가 발생하였다.
FltGetFileNameInformation 선언은 아래와 같다.
NTSTATUS FltGetFileNameInformation(
_In_ PFLT_CALLBACK_DATA CallbackData,
_In_ FLT_FILE_NAME_OPTIONS NameOptions,
_Out_ PFLT_FILE_NAME_INFORMATION *FileNameInformation
);
FltGetFileNameInformation 파라미터로 넘기고
그 포인터를 post callback에서 ExFreePool 하였을대 BAD_POOL_CALLER가 발생하였다.
FltGetFileNameInformation 선언은 아래와 같다.
NTSTATUS FltGetFileNameInformation(
_In_ PFLT_CALLBACK_DATA CallbackData,
_In_ FLT_FILE_NAME_OPTIONS NameOptions,
_Out_ PFLT_FILE_NAME_INFORMATION *FileNameInformation
);
아래와 같이 사용하였다.
PFILE_NAME_INFORMATION pFileInfo = ExAllocatePoolWithTag(NonPagedPool, 1024, MEM_TAG);
FltGetFileNameInforamtion(Data, FLT_FILE_NAME_NORMATLIZED | FLT_FILE_NAME_QUERY_DEFAULT, &pFileInfo);
.....
ExFreePool(pFileInfo);
아래와 같이 사용하였다. ExFreePool시 CRASH!!!
음...왜그러징..다시 FltGetFileNameInformation의 msdn을 천천히 읽어보았다.
파라미터로 들어가는 FLT_FILE_NAME_INFORMATION은 이중포인터로 들어가서 내부에서 ALLOCATION을 해서 포인터를 리턴해주고 있더라...결국 위에서 내가 ExFreePool한 포인터는 내가 allocation 한 그놈이 아니고 system이 allocation 한 놈을 내가 함부로..free한 격이 되었다. 그래서 BAD_POOL_CALLER 에러가 났군~!!!
system이 allocation 한 놈들은 보통 free하는 API가 존재하는 법!
그놈이 FltReleaseFileNameInformation!
제대로 된 사용법은 아래와 같다.
PFILE_NAME_INFORMATION pFileInfo = NULL;
FltGetFileNameInforamtion(Data, FLT_FILE_NAME_NORMATLIZED | FLT_FILE_NAME_QUERY_DEFAULT, &pFileInfo);
...
FltReleaseFileNameInformation(pFileInfo);
2016년 3월 4일 금요일
2016년 3월 2일 수요일
[WIN32]_WIN32_WINNT와 WINVER 그리고 NTDDI_VERSION
파일시스템 미니필터 드라이버를 작업하고 있는데 Fltxxxx 함수들이 동작하는 OS 버전이 달라서 컴파일이 안될때가 있다.
PINSTANCE_FULL_INFORMATION 구조체가 컴파일이 안되서 FltuserStructures.h를 열어보았다.
FLT_MGR_BASELINE이 무슨이유에서인지 define이 안되고 있는 상태이다. FLT_MGR_BASELINE을 따라가 보면 OS VERSION의 조건이 만족되어야 한다. 음...
몇부터 되는것이지?? define 내용은 아래와 같다.
음... 하지만 나의 헤더에는 아래와 같이 선언이 되있었다.
_WIN32_WINNT 0x0502이면 windows 2003 sp1 이란얘긴데...왜 안되지???
그럼 NTDDI_VERSION은 몇으로 되있는 것일까? 찾아보자 skddkver.h 에 아래와 같이 선언되있다.
이는 NTDDI_VERSION이 따로 선언되있지 않고 _WIN32_WINNT일때는 _WIN32_WINNT를 NTDDI_VERSION으로 컨버팅해서 따라간다는것인데...음...WINDOWS 2003 SP1인데 잘 컨버팅이 됬다면 되야하는데..왜 안되지???
음...별거없네 그냥 뒤에 0000만 더 붙인것...그럼 0x05020000 인데...
NTDDI_VERSION 정의를 살펴보자.
NTDDI_WS03SP1은 0x05020100 으로 _WIN32_WINNT에 0000만 붙여서 해결되지 않는다.
NTDDI_VERSION_FROM_WIN32_WINNT 믿을놈이 못된다..ㅋㅋ
잘 확인하고 사용하자.
PINSTANCE_FULL_INFORMATION 구조체가 컴파일이 안되서 FltuserStructures.h를 열어보았다.
#if FLT_MGR_BASELINE
//
// Disable warning for this file
//
FLT_MGR_BASELINE이 무슨이유에서인지 define이 안되고 있는 상태이다. FLT_MGR_BASELINE을 따라가 보면 OS VERSION의 조건이 만족되어야 한다. 음...
몇부터 되는것이지?? define 내용은 아래와 같다.
#define FLT_MGR_BASELINE (((OSVER(NTDDI_VERSION) == NTDDI_WIN2K) && (SPVER(NTDDI_VERSION) >= SPVER(NTDDI_WIN2KSP4))) || \정리해보면 NTDDI_VERSION이 NTDDI_WS03SP1 보다 같거나 높아야한다는것...
((OSVER(NTDDI_VERSION) == NTDDI_WINXP) && (SPVER(NTDDI_VERSION) >= SPVER(NTDDI_WINXPSP2))) || \
((OSVER(NTDDI_VERSION) == NTDDI_WS03) && (SPVER(NTDDI_VERSION) >= SPVER(NTDDI_WS03SP1))) || \
(NTDDI_VERSION >= NTDDI_VISTA))
음... 하지만 나의 헤더에는 아래와 같이 선언이 되있었다.
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0502
#endif
_WIN32_WINNT 0x0502이면 windows 2003 sp1 이란얘긴데...왜 안되지???
그럼 NTDDI_VERSION은 몇으로 되있는 것일까? 찾아보자 skddkver.h 에 아래와 같이 선언되있다.
#ifndef NTDDI_VERSION
#ifdef _WIN32_WINNT
// set NTDDI_VERSION based on _WIN32_WINNT
#define NTDDI_VERSION NTDDI_VERSION_FROM_WIN32_WINNT(_WIN32_WINNT)
#else
#define NTDDI_VERSION 0x06010000
#endif
#endif
이는 NTDDI_VERSION이 따로 선언되있지 않고 _WIN32_WINNT일때는 _WIN32_WINNT를 NTDDI_VERSION으로 컨버팅해서 따라간다는것인데...음...WINDOWS 2003 SP1인데 잘 컨버팅이 됬다면 되야하는데..왜 안되지???
#define NTDDI_VERSION_FROM_WIN32_WINNT2(ver) ver##0000
#define NTDDI_VERSION_FROM_WIN32_WINNT(ver) NTDDI_VERSION_FROM_WIN32_WINNT2(ver)
음...별거없네 그냥 뒤에 0000만 더 붙인것...그럼 0x05020000 인데...
NTDDI_VERSION 정의를 살펴보자.
NTDDI_VERSION_FROM_WIN32_WINNT 믿을놈이 못된다..ㅋㅋ
잘 확인하고 사용하자.
2016년 2월 4일 목요일
[WIN32]Security Descriptor 생성(Users 그룹 권한 변경)
service에서 파일을 생성했는데 user 계정으로 로그인해서 파일 write를 하려니 access denied가 떴다. 이유는 파일의 보안 권한 때문! 파일 속성의 보안탭에서 권한을 추가해주면 잘 동작한다. 하지만 내가 만든 파일인데...권한을 상속받지 않고 내가 마음대로 주려면..어찌해야하는가....SD(Security Descriptor)를 만들어서 적용해주면 된다.
1. 원하는 계정의 SID(Security Identifier) 생성(AllocateAndInitializeSid)
2. SID마다 부여한 권한정보를 담고 있는 EXPLICIT_ACCESS를 각각 만듬.
3. EXPLICIT_ACCESS 를 적용한 ACL(access control list)을 생성한다.(SetEntriesInAcl)
4. 새로운 Security Descriptor를 생성한다.(InitializeSecurityDescriptor)
5. 생성한 ACL을 SD에 적용한다.(SetSecurityDescriptorDacl)
6. 생성한 SD를 가지고 CreateFile등에 사용한다.
SD가 NULL인 상태로 CreateFile을 이용해서 파일생성시 Users에게 부여된 권한은 아래와 같았다.
Users 권한에 대한 쓰기 권한이 제한되어 있음을 볼 수 있다.
아래는 EveryOne과 Users 그룹 Administrators 그룹에 대한 권한 설정한 샘플 소스이다.
위와 같이 생성된 SD를 적용하여 파일을 생성하였을때 부여된 권한은 아래와 같습니다.
쓰기 읽기 권한이 모두 주어진것을 볼 수 있습니다.
1. 원하는 계정의 SID(Security Identifier) 생성(AllocateAndInitializeSid)
2. SID마다 부여한 권한정보를 담고 있는 EXPLICIT_ACCESS를 각각 만듬.
3. EXPLICIT_ACCESS 를 적용한 ACL(access control list)을 생성한다.(SetEntriesInAcl)
4. 새로운 Security Descriptor를 생성한다.(InitializeSecurityDescriptor)
5. 생성한 ACL을 SD에 적용한다.(SetSecurityDescriptorDacl)
6. 생성한 SD를 가지고 CreateFile등에 사용한다.
SD가 NULL인 상태로 CreateFile을 이용해서 파일생성시 Users에게 부여된 권한은 아래와 같았다.
Users 권한에 대한 쓰기 권한이 제한되어 있음을 볼 수 있다.
아래는 EveryOne과 Users 그룹 Administrators 그룹에 대한 권한 설정한 샘플 소스이다.
#include#include #pragma comment(lib, "advapi32.lib") BOOL makepSD(PSECURITY_DESCRIPTOR pSD) { DWORD dwRes; PSID pEveryoneSID = NULL, pAdminSID = NULL, pUserSID; EXPLICIT_ACCESS ea[3]; SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY; PACL pACL = NULL; if(!AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0,0,0,0,0,0,0, &pEveryoneSID)) { _tprintf(_T("AllocateAndInitializeSid Error %u\n"), GetLastError()); goto CleanUp; } ea[0].grfAccessPermissions = GENERIC_READ; ea[0].grfAccessMode = SET_ACCESS; ea[0].grfInheritance = NO_INHERITANCE; ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; ea[0].Trustee.TrusteeType = TRUSTEE_IS_USER; ea[0].Trustee.ptstrName = (LPTSTR)pEveryoneSID; if(!AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0,0,0,0,0,0, &pAdminSID)) { _tprintf(_T("AllocateAndInitializeSid Error %u\n"), GetLastError()); goto CleanUp; } ea[1].grfAccessPermissions = GENERIC_ALL; ea[1].grfAccessMode = SET_ACCESS; ea[1].grfInheritance = NO_INHERITANCE; ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP; ea[1].Trustee.ptstrName = (LPTSTR)pAdminSID; if(!AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS, 0,0,0,0,0,0, &pUserSID)) { _tprintf(_T("AllocateAndInitializeSid Error %u\n"), GetLastError()); goto CleanUp; } ea[2].grfAccessPermissions = GENERIC_READ | GENERIC_WRITE; ea[2].grfAccessMode = SET_ACCESS; ea[2].grfInheritance = NO_INHERITANCE; ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID; ea[2].Trustee.TrusteeType = TRUSTEE_IS_GROUP; ea[2].Trustee.ptstrName = (LPTSTR)pUserSID; dwRes = SetEntriesInAcl(3, ea, NULL, &pACL); if(dwRes != ERROR_SUCCESS) { _tprintf(_T("SetEntriesInAcl Error %u\n"), GetLastError()); goto CleanUp; } if(!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) { _tprintf(_T("LocalAlloc Error %u\n"), GetLastError()); goto CleanUp; } if(!SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE)) { _tprintf(_T("SetSecurityDescriptorDacl Error %u\n"), GetLastError()); goto CleanUp; } return TRUE; CleanUp: if(pAdminSID) FreeSid(pAdminSID); if(pEveryoneSID) FreeSid(pEveryoneSID); if(pUserSID) FreeSid(pUserSID); return FALSE; }
위와 같이 생성된 SD를 적용하여 파일을 생성하였을때 부여된 권한은 아래와 같습니다.
쓰기 읽기 권한이 모두 주어진것을 볼 수 있습니다.
2016년 1월 20일 수요일
[C++]static 멤버 변수와 함수
C에서는 특정함수 안에서 static 변수를 선언하여 특정 함수 안에서만 전역변수처럼 사용한다.
C++에서는 객체지향이기때문에 전역변수를 사용할 것을 권하지 않는다. 때문에 전역변수를 대체할 수 있는 클래스 멤버 변수, 클래스 멤버 함수 라는 static 멤버 변수, 함수가 생기게 되었다. 전역변수처럼 클래스와 독립적인 곳에 존재하게 되면 모든 클래스에서 접근이 가능하다. private 이냐, public이냐에 따라 접근 범위에 제한을 둘 수 있다. staic 멤버 함수는 클래스와 독립공간에서 모든 클래스가 공유하여야 하므로 내부에서 사용하는 변수 또한 static 멤버 변수만을 사용하여야 하고 일반 멤버 변수를 사용해서는 안된다.
1. static 멤버 변수
static 멤버 변수는 보통 클래스의 데이터 멤버를 const로 선언하였을때 이 값은 변경되지 않는 상수값이기 때문에 어차피 클래스 객체간에 모두 동일하게 사용되어 메모리방지를 막기 위해 static으로 선언하여 사용하는 경우가 많다. 아래는 const 변수가 아닌 일반 변수를 얘를 들었다.
위의 프로그램에서 static 멤버 변수 c의 변화를 생각해보자. 10으로 초기화가 되었으며 main 함수에서 클래스 객체 생성전에 1이 증가하였고(test::c++;) test 클래스 두개를 차례대로 생성하였고 생성할때마다 c를 1씩 증가시켰으므로 t1.c 와 t2.c 는 모두 13이 되어있게된다. 이는 전역변수를 사용한 효과와 똑같은 효과라 볼 수 있다.
그러나 이 변수를 test라는 클래스 객체만이 공유 하고 싶다면 어떻게 할까? 이 때 private으로 변수를 선언하여 사용한다.
일반적으로 클래스 데이터 멤버를 public으로 선언하여 외부에 노출하는것은 바람직하지 않다. private이나 protected로 선언하여 public get/set을 통해 접근 권한을 얻도록 하는것이 좋다.
class test
{
int a;
private:
static int c;
public:
test() {a = 10; c++;}
};
int test::c = 10;
int _tmain(int argc, _TCHAR* argv[])
{
test::c++; //--> error C2248: 'test::c' : private 멤버('test' 클래스에서 선언)에 액세스할 수 없습니다.
test t1;
test t2;
return 0;
}
위처럼 private인 static 멤버 변수를 다른 영역에서 호출시 에러가 발생하며 위 프로그램은 test::c++ 을 주석처리하면 t1.c와 t2.c는 모두 12가 된다.
2. static 멤버 함수
class test
{
int a;
static int c;
public:
test() {a = 10; c++;}
void change()
{
c = 0;
}
static void change_static()
{
c = 20;
a = 20; //-->error C2597: 비정적 멤버 'test::a'에 대한 참조가 잘못되었습니다.
}
};
위에 예제처럼 일반 멤버함수에서는 static 멤버 변수를 사용할 수 있으나 static 멤버 함수에서는 static 멤버 변수만을 사용할 수 있음을 보여준다. 이는 전역함수에서 다른 함수의 지역변수를 가져다 쓰는 것과 같은 이치라 생각하면 된다.
이처럼 static 멤버 함수는 클래스 객체의 멤버 변수들과 독립적이고 변경이 불가하다. 그러므로 static 멤버 함수의 const 지정은 무의미하다. static 멤버 함수에 const 지정시 컴파일 오류가 난다.
C++에서는 객체지향이기때문에 전역변수를 사용할 것을 권하지 않는다. 때문에 전역변수를 대체할 수 있는 클래스 멤버 변수, 클래스 멤버 함수 라는 static 멤버 변수, 함수가 생기게 되었다. 전역변수처럼 클래스와 독립적인 곳에 존재하게 되면 모든 클래스에서 접근이 가능하다. private 이냐, public이냐에 따라 접근 범위에 제한을 둘 수 있다. staic 멤버 함수는 클래스와 독립공간에서 모든 클래스가 공유하여야 하므로 내부에서 사용하는 변수 또한 static 멤버 변수만을 사용하여야 하고 일반 멤버 변수를 사용해서는 안된다.
1. static 멤버 변수
static 멤버 변수는 보통 클래스의 데이터 멤버를 const로 선언하였을때 이 값은 변경되지 않는 상수값이기 때문에 어차피 클래스 객체간에 모두 동일하게 사용되어 메모리방지를 막기 위해 static으로 선언하여 사용하는 경우가 많다. 아래는 const 변수가 아닌 일반 변수를 얘를 들었다.
class test
{
int a;
public:
static int c;
public:
test() {a = 10; c++;}
};
int test::c = 10; //-->static 멤버 변수 초기화
int _tmain(int argc, _TCHAR* argv[])
{
test t1;
test t2;
return 0;
}
위에서처럼 static 멤버 변수는 초기화를 클래스 외부에서 해주어야 하면 초기화를 안하면 컴파일시 아래와 같은 외부 기호를 확인할 수 없다는 에러 메시지가 난다. 문법처럼 기억해두면 될듯.
c++11 이상부터 static 멤버 변수 선언시 초기화가 가능하다. 이는 컴파일러 버전에 영향을 받으므로 Visual Studio 사용시 아래 msdn 사이트를 참고하여 확인하면 된다.
https://msdn.microsoft.com/ko-kr/library/hh567368.aspx
c++11 이상부터 static 멤버 변수 선언시 초기화가 가능하다. 이는 컴파일러 버전에 영향을 받으므로 Visual Studio 사용시 아래 msdn 사이트를 참고하여 확인하면 된다.
https://msdn.microsoft.com/ko-kr/library/hh567368.aspx
위의 프로그램에서 static 멤버 변수 c의 변화를 생각해보자. 10으로 초기화가 되었으며 main 함수에서 클래스 객체 생성전에 1이 증가하였고(test::c++;) test 클래스 두개를 차례대로 생성하였고 생성할때마다 c를 1씩 증가시켰으므로 t1.c 와 t2.c 는 모두 13이 되어있게된다. 이는 전역변수를 사용한 효과와 똑같은 효과라 볼 수 있다.
그러나 이 변수를 test라는 클래스 객체만이 공유 하고 싶다면 어떻게 할까? 이 때 private으로 변수를 선언하여 사용한다.
일반적으로 클래스 데이터 멤버를 public으로 선언하여 외부에 노출하는것은 바람직하지 않다. private이나 protected로 선언하여 public get/set을 통해 접근 권한을 얻도록 하는것이 좋다.
class test
{
int a;
private:
static int c;
public:
test() {a = 10; c++;}
};
int test::c = 10;
int _tmain(int argc, _TCHAR* argv[])
{
test::c++; //--> error C2248: 'test::c' : private 멤버('test' 클래스에서 선언)에 액세스할 수 없습니다.
test t1;
test t2;
return 0;
}
위처럼 private인 static 멤버 변수를 다른 영역에서 호출시 에러가 발생하며 위 프로그램은 test::c++ 을 주석처리하면 t1.c와 t2.c는 모두 12가 된다.
2. static 멤버 함수
class test
{
int a;
static int c;
public:
test() {a = 10; c++;}
void change()
{
c = 0;
}
static void change_static()
{
c = 20;
a = 20; //-->error C2597: 비정적 멤버 'test::a'에 대한 참조가 잘못되었습니다.
}
};
위에 예제처럼 일반 멤버함수에서는 static 멤버 변수를 사용할 수 있으나 static 멤버 함수에서는 static 멤버 변수만을 사용할 수 있음을 보여준다. 이는 전역함수에서 다른 함수의 지역변수를 가져다 쓰는 것과 같은 이치라 생각하면 된다.
이처럼 static 멤버 함수는 클래스 객체의 멤버 변수들과 독립적이고 변경이 불가하다. 그러므로 static 멤버 함수의 const 지정은 무의미하다. static 멤버 함수에 const 지정시 컴파일 오류가 난다.
2016년 1월 12일 화요일
[C++]const 변수와 함수, 클래스
const는 위치에 따라 의미가 달라져서 정리를 해본다.
1. const 변수
const int x = 5;
int const x = 5;
위 두개는 의미가 같으면 x자체의 값을 변경할 수 없다는 의미임. const는 자료형과의
위치는 상관이 없으며 포인터나 변수명과의 위치에 따라 의미가 달라진다.
int x = 5;
const int *y = &x;
int x = 5;
int const *y = &x;
x = 7; --> 에러아님
*y = 7; -->에러
위 두개는 의미가 같으며 *y에 대한 값을 변경할 수 없다는 의미임
*y는 x를 의미하나 x는 변경이 가능하고 *y는 변경이 불가능함
*y와 x가 같은 것을 의미하나 다른 값으로 혼동되어 변경되는것을 방지하기 위함이다.
int x= 5;
int* const y = &x;
x = 7;
*y = 7;
y = (int*)0x00000007; --> 에러
위의 의미는 y에 대한 값을 변경할 수 없다는 의미로 x의 값이 담겨있는 주소값을 의미하는 y가 변경되어 다른 주소를 가리키게 되는것을 방지하기 위함이다.
2. const 멤버 변수
위에서는 일반적인 const의 쓰임새를 얘기했다면 이제 집중적으로 클래스 내부에서 const가 어떻게 쓰이는지를 정리한다.
클래스 내에서 멤버 변수를 const로 선언을 하면 생성 시점에 초기화를 해야하며 그 이후에는 더 이상 값을 변경할 수 없게 된다.
class test
{
const int a;
public:
test(int a1):a(a1) {}
};
위처럼 변수 정의시 초기화시키는 클래스 initializer에서 const 멤버 변수에 대한 초기화가 이루어져야한다. 생성자의 바디안에서 초기화시 에러 발생한다.
3. const 함수
<const 매개변수>
void func(const int& param)
{
param = 7; --> 에러
}
참조에 의한 전달 방식일때 기존 caller에서 변수의 복사 오버헤드를 줄이기 위해 참조방식을 체택하였으나 변경을 원치 않을때에는 const를 붙여서 변경을 방지한다.
<const 멤버함수>
void func(int param) const
{
a=1; //-->error
}
const 멤버함수는 클래스의 멤버함수에만 사용가능하다.
const로 지정된 멤버함수에서는 클래스의 멤버변수를 read only 권한으로만 사용가능하다는 의미이다.
<const 리턴>
const 매개변수와 마찬가지로 참조를 리턴할 때 변경되는것을 방지하기 위해 사용된다.
매개변수일때와 마찬가지로 복사가 일어나기를 바라지 않아서 참조를 리턴하는데 참조를 리턴하였을 경우 아래와 같이 클래스의 멤버함수를 caller쪽에서 변경의 위험이 있다.
이를 방지하기 위해서 const로 보호를 한다.
4. const 클래스 객체
클래스 멤버 함수에 const를 걸면 그 함수 안에서는 멤버 변수의 변경이 불가하다고 하였다. 그렇다면 클래스 객체 자체에 const를 걸면 어떻게 될까? const 클래스 객체는 const 멤버 함수만 사용이 가능하게 된다. 일반 멤버 함수 사용시 컴파일 에러가 발생한다.
class test
{
int c;
public:
test () {}
void func1() const
{
/*c = 1;*/ //-> 사용불가
}
void func2()
{
c = 2;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
const test t1;
t1.func1();
t1.func2(); //--> error C2662: 'test::func2' : 'this' 포인터를 'const test'에서 'test &'(으)로 변환할 수 없습니다.
return 0;
}
1. const 변수
const int x = 5;
int const x = 5;
위 두개는 의미가 같으면 x자체의 값을 변경할 수 없다는 의미임. const는 자료형과의
위치는 상관이 없으며 포인터나 변수명과의 위치에 따라 의미가 달라진다.
int x = 5;
const int *y = &x;
int x = 5;
int const *y = &x;
x = 7; --> 에러아님
*y = 7; -->에러
위 두개는 의미가 같으며 *y에 대한 값을 변경할 수 없다는 의미임
*y는 x를 의미하나 x는 변경이 가능하고 *y는 변경이 불가능함
*y와 x가 같은 것을 의미하나 다른 값으로 혼동되어 변경되는것을 방지하기 위함이다.
int x= 5;
int* const y = &x;
x = 7;
*y = 7;
y = (int*)0x00000007; --> 에러
위의 의미는 y에 대한 값을 변경할 수 없다는 의미로 x의 값이 담겨있는 주소값을 의미하는 y가 변경되어 다른 주소를 가리키게 되는것을 방지하기 위함이다.
2. const 멤버 변수
위에서는 일반적인 const의 쓰임새를 얘기했다면 이제 집중적으로 클래스 내부에서 const가 어떻게 쓰이는지를 정리한다.
클래스 내에서 멤버 변수를 const로 선언을 하면 생성 시점에 초기화를 해야하며 그 이후에는 더 이상 값을 변경할 수 없게 된다.
class test
{
const int a;
public:
test(int a1):a(a1) {}
};
위처럼 변수 정의시 초기화시키는 클래스 initializer에서 const 멤버 변수에 대한 초기화가 이루어져야한다. 생성자의 바디안에서 초기화시 에러 발생한다.
3. const 함수
<const 매개변수>
void func(const int& param)
{
param = 7; --> 에러
}
참조에 의한 전달 방식일때 기존 caller에서 변수의 복사 오버헤드를 줄이기 위해 참조방식을 체택하였으나 변경을 원치 않을때에는 const를 붙여서 변경을 방지한다.
<const 멤버함수>
void func(int param) const
{
a=1; //-->error
}
const 멤버함수는 클래스의 멤버함수에만 사용가능하다.
const로 지정된 멤버함수에서는 클래스의 멤버변수를 read only 권한으로만 사용가능하다는 의미이다.
위에서보면 const 객체인 t2는 const 멤버함수가 아닌 change 호출시 에러가 나는 것을 볼 수 있다. 이처럼 멤버 변수의 변경점이 없는 객체를 사용하고 싶을때 const 멤버함수를 구분하여 사용하였다면 구분해내기가 편할 것이다. 반면 const 멤버함수는 const 객체이든 비const 객체이든 호출에는 문제가 없다.
만약 멤버함수가 static으로 선언이 되었다면 클래스 객체와 무관하여 멤버의 변경이 불가능하므로 const선언이 의미가 없다. 하여 static 멤버 함수의 const 지정은 무의미하다.
하지만 const 멤버 함수도 예외적으로 멤버 변수의 변경을 허용할 수 있는 방법이 존재한다. 이것은 mutable 데이터 멤버이다. 아래와 같이 mutable로 선언시 const 멤버 함수에서 변경이 가능하게된다.
class test
{
mutable int a;
public:
test () {}
void func1() const
{
a = 1;
}
};
만약 멤버함수가 static으로 선언이 되었다면 클래스 객체와 무관하여 멤버의 변경이 불가능하므로 const선언이 의미가 없다. 하여 static 멤버 함수의 const 지정은 무의미하다.
하지만 const 멤버 함수도 예외적으로 멤버 변수의 변경을 허용할 수 있는 방법이 존재한다. 이것은 mutable 데이터 멤버이다. 아래와 같이 mutable로 선언시 const 멤버 함수에서 변경이 가능하게된다.
class test
{
mutable int a;
public:
test () {}
void func1() const
{
a = 1;
}
};
<const 리턴>
const 매개변수와 마찬가지로 참조를 리턴할 때 변경되는것을 방지하기 위해 사용된다.
매개변수일때와 마찬가지로 복사가 일어나기를 바라지 않아서 참조를 리턴하는데 참조를 리턴하였을 경우 아래와 같이 클래스의 멤버함수를 caller쪽에서 변경의 위험이 있다.
이를 방지하기 위해서 const로 보호를 한다.
4. const 클래스 객체
클래스 멤버 함수에 const를 걸면 그 함수 안에서는 멤버 변수의 변경이 불가하다고 하였다. 그렇다면 클래스 객체 자체에 const를 걸면 어떻게 될까? const 클래스 객체는 const 멤버 함수만 사용이 가능하게 된다. 일반 멤버 함수 사용시 컴파일 에러가 발생한다.
class test
{
int c;
public:
test () {}
void func1() const
{
/*c = 1;*/ //-> 사용불가
}
void func2()
{
c = 2;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
const test t1;
t1.func1();
t1.func2(); //--> error C2662: 'test::func2' : 'this' 포인터를 'const test'에서 'test &'(으)로 변환할 수 없습니다.
return 0;
}
2015년 9월 3일 목요일
[C++]캐스팅 연산자
C++의 4가지 캐스팅 연산자에 대해 정리하고 넘어가자.
(static_cast, dynamic_cast, const_cast, reinterpret_cast)
1. static_cast와 dynamic_cast
컴파일 시 static_cast 검사를 통해 두 형식 간에 상속 관계가 있는지 여부를 확이한다. 상속관계가 없을 시 컴파일 오류가 난다. 하지만 런타임 검사가 시행되지 않아 다운캐스팅 하였을 경우 액세스 위반이 발생 할 수 있다. 이 상황에서도 컴파일 오류는 나타나지 않는다. 그러나 dynamic_cast는 다운캐스팅시에 사용하는 캐스팅으로 변환이 가능한지에 대한 확인작업을 먼저 하여 위반시 컴파일 오류를 나타냅으로써 안전한 다운캐스트가 가능하게 한다. 아래의 코드에서 dynamic_cast와의 차이점을 볼 수 있다.
pd1인 경우는 B 클래스 안에 없는 함수들이 있기때문에 캐스팅이 불가능하다는 컴파일 오류는 나게 한다. 하지만 pd2인 경우는 컴파일 오류는 나지 않으나 런타임시 pd2에서 sub나 mul 사용에 대한 액세스 위반이 발생할 수 있다.
2. const_cast
const_cast는 const 속성을 제거할 때 사용한다.
msdn의 예제를 보니 const 변수는 많이 다뤄보았으나 const 함수는 많이 활용해보지 않았다.
printNumber함수가 const로 지정이 되어있습니다. 여기서 함수 선언시 사용되는 const는 멤버 함수에만 사용이 가능하며 목적은 멤버 변수의 변경을 방지 하기 위함입니다. 그렇다면 아래와 같이 printNumber 함수에서는 number 변수의 변경이 원래는 불가합니다. 그러나 const_cast를 통하여 const 속성을 없애고 변경을 하는것을 볼 수 있습니다.
3. reinterpret_cast
무조건적인 형변환으로 실제 데이터가 어떤 객체이던 강제로 형변환을 진행하여 상당히 위험한 캐스팅이다. 편하긴 한데 위험한....일단 나는 함수 포인터를 pvoid로 변환한다던가 IUnknown 같은 인터페이스를 추출할때 사용한다.
다중상속시는 기존클래스와의 인스턴스 포인트가 달라서 주의를 해야한다.
(static_cast, dynamic_cast, const_cast, reinterpret_cast)
1. static_cast와 dynamic_cast
컴파일 시 static_cast 검사를 통해 두 형식 간에 상속 관계가 있는지 여부를 확이한다. 상속관계가 없을 시 컴파일 오류가 난다. 하지만 런타임 검사가 시행되지 않아 다운캐스팅 하였을 경우 액세스 위반이 발생 할 수 있다. 이 상황에서도 컴파일 오류는 나타나지 않는다. 그러나 dynamic_cast는 다운캐스팅시에 사용하는 캐스팅으로 변환이 가능한지에 대한 확인작업을 먼저 하여 위반시 컴파일 오류를 나타냅으로써 안전한 다운캐스트가 가능하게 한다. 아래의 코드에서 dynamic_cast와의 차이점을 볼 수 있다.
pd1인 경우는 B 클래스 안에 없는 함수들이 있기때문에 캐스팅이 불가능하다는 컴파일 오류는 나게 한다. 하지만 pd2인 경우는 컴파일 오류는 나지 않으나 런타임시 pd2에서 sub나 mul 사용에 대한 액세스 위반이 발생할 수 있다.
2. const_cast
const_cast는 const 속성을 제거할 때 사용한다.
msdn의 예제를 보니 const 변수는 많이 다뤄보았으나 const 함수는 많이 활용해보지 않았다.
printNumber함수가 const로 지정이 되어있습니다. 여기서 함수 선언시 사용되는 const는 멤버 함수에만 사용이 가능하며 목적은 멤버 변수의 변경을 방지 하기 위함입니다. 그렇다면 아래와 같이 printNumber 함수에서는 number 변수의 변경이 원래는 불가합니다. 그러나 const_cast를 통하여 const 속성을 없애고 변경을 하는것을 볼 수 있습니다.
3. reinterpret_cast
무조건적인 형변환으로 실제 데이터가 어떤 객체이던 강제로 형변환을 진행하여 상당히 위험한 캐스팅이다. 편하긴 한데 위험한....일단 나는 함수 포인터를 pvoid로 변환한다던가 IUnknown 같은 인터페이스를 추출할때 사용한다.
다중상속시는 기존클래스와의 인스턴스 포인트가 달라서 주의를 해야한다.
피드 구독하기:
글 (Atom)