(게임은 아님)

숫자 패드의 번호를 입력하면 0번 숫자가 눌렀던 방향으로 움직인다.

 

4(오른쪽), 2(아래) 숫자 키를 눌러서 숫자 0이 2행2열로 감.

 

소스 코드

github.com/gkagm2/cpppractise/blob/master/cpptraining/ch1/Program/MoveCountInMatrix_MBCS_WBCS/MoveCountInMatrix_MBCS_WBCS.cpp

 

실행파일

github.com/gkagm2/cpppractise/blob/master/cpptraining/ch1/Program/MoveCountInMatrix_MBCS_WBCS/MoveCountInMatrix_MBCS_WBCS.exe

 

빙고게임 연습.

 

 

5번 빙고되면 승리 출력 후 종료

 

소스 코드

github.com/gkagm2/cpppractise/blob/master/cpptraining/ch1/Game/Binggo_MBCS_WBCS/Binggo_MBCS_WBCS.cpp

 

실행 파일

github.com/gkagm2/cpppractise/blob/master/cpptraining/ch1/Game/Binggo_MBCS_WBCS/Binggo_MBCS_WBCS.exe

바꿀 수 있는 방법이 많음

 

임시 변수 이용

int a, b;
int t;
t = a;
a = b;
b = t;

수학적 사고 이용 ㅋㅋ

int a, b;
a = a + b;
b = a - b;
a = a - b;

xor 비트 연산자 이용

int a, b;
a = a ^ b;
b = a ^ b;
a = a ^ b;

 

속도를 비교해봤는데

 

Swap함수를 만들어서 두 변수를 파라미터로 참조하여 함수 안에서 바꾸게 하면 세개 다 속도가 비슷하게 나옴

Main(){
	for( roop ){
    	Sawp(a,b)
    }
}
Swap(int &a, int &b){
	...
}

 

 근데 함수 안쓰고 Main문 안에서 하면 속도차이가 발생함.

Main() {
	for( Roop ){
    	...
    }
}

대략 3000000000회 반복으로 돌렸을 때

임시 변수 : 6~7초

+,- 연산 : 14초

xor 연산 : 14초

정도 나옴.

 

임시변수쓸때는 레지스터와 메모리에 주소값 변경하는걸 내부적으로 하는것보다 
메모리에 있는 변수값 레지스터에 올리고 ALU 연산하는게 속도가 더 느린건가??

그나마 swap함수 안에 넣어놓으면 int t 변수가 swap함수 메모리에 올라가는 동작때문에 비슷한건가????

왜 이런걸까.. 궁금하네

 

암튼 결론 : 임시변수 쓰자.

 

 

디폴트 파라미터의 내부 동작

 

 

컴파일러는 내부적으로 디폴트 파라미터의 모든 조합에 대해 적절한 함수를 생성한다.

함수를 생성할때는 이름 바꾸기(name mangling)를 한 함수들이 생성 될 것이다.

오버로딩과 이와 마찬가지로 동작한다.

 

 

 

함수의 호출 관례(function calling convention)란 함수의 파라미터를 스택에 푸시하는 순서, 푸시하는 쪽 및 이름 변화를 명시한 것이다.

 

변경자 푸시 순서 팝하는 쪽 이름 변화
__cdec 오른쪽에서 왼쪽으로 호출하는 쪽 prepended
__fastcall 왼쪽에서 오른쪽으로 호출당하는 쪽 @ prepended
__pascal 왼쪽에서 오른쪽으로 호출당하는 쪽 Uppercase
__stdcall 오른쪽에서 왼쪽으로 호출당하는 쪽 No change

푸시 순서

위쪽 표의 푸시 순서의 의미는 함수의 파라미터가 어느쪽에서부터 먼저 푸시를 하는지에 대한 말.

 

팝하느 쪽

팝하는 쪽의 의미는 함수 호출 후 종료 시점에서 함수의 스택을 정리할 때 스택의 팝을 어느쪽에서 하느냐에 대한 말이다.

 

이름 변화

- C방식의 경우 명칭 앞에 언더스코어 '_'가 붙는다.

- 레지스터 방식의 경우 @가 붙는다.

- Pascal 바익의 경우 모두 대문자로 바뀐다.

- win32 방식(__stdcall)의 경우 이름은 __stdcall이다.

 

windows의 api 함수는 모두 win32 방식을 이용한다.

모든 C++ 컴파일러에서 기본값은 cedecl이다. 

visual studio IDE에서 calling convention을 바꿀 수 있다.

 

visual c++에서 Windows 프로그래밍을 하더라도 기본 설정은 __cdecl이다.

windows 응용 프로그램의 시작 함수인 WinMain()은 __stdcall이지만 win32 콘솔 응용 프로그램의 시작 함수인 main()은 __cdecl이다.

 

#include <iostream>
using namespace std;

void __pascal f(int i, int j) {
	cout << i << " " << j << "\n";
}

void __cdecl g(int i, int j) {
	cout << i << " " << j << "\n";
}

int main() {
	int i, j;

	i = 1; j = 2;
	f(i == j, i = j);
	i = 1; j = 2;
	g(i == j, i = j);

	return 0;
}

__pascal일 경우 왼쪽에서 오른쪽으로 스택에 푸쉬되므로 "0 1"로 될 것이고

__cdecl일 경우 오른쪽에서 왼쪽으로 스택에 푸쉬되므로 "1 2"로 될 것이다.

 

visual studio ide로 이용 할 경우 컴파일 에러가 발생한다.

windows에서 더 이상 pascal 방식은 존재하지 않는다. 이것은 __stdcall로 대처되었다.

 

 

__fastcall로 바꿨다. 

#include <iostream>
using namespace std;

void __fastcall f(int i, int j) {
	cout << i << " " << j << "\n";
}

void __cdecl g(int i, int j) {
	cout << i << " " << j << "\n";
}

int main() {
	int i, j;

	i = 1; j = 2;
	f(i == j, i = j);
	i = 1; j = 2;
	g(i == j, i = j);

	return 0;
}

이 상태에서 실행을 하면 

__fastcall은 "0 2"이 나와야되고 

__cdecl은 "1 2"이 나와야 된다.

 

근데 막상 실행해보면 둘 다 "1 2"나 "0 2"가 나올 수 있다. 왜그럴까..

 

 

다음과 같은 코드가 있다.

#include <iostream>
using namespace std;

class Something {
public:
	Something() { cout << "2"; }
	~Something() { cout << "~2"; }
};

class Parent {
public:
	Parent() { cout << "1"; }
	~Parent() { cout << "~1"; }
};

class Child : public Parent {
public:
	Child() { cout << "3"; }
	~Child() { cout << "~3"; }

private:
	Something mDataMember;
};

 

 

이렇게 할 경우 정상적으로 소멸된다.

int main() {
	Child c;

	return 0;
}

정상

 

 

아래처럼 동적으로 생성한 파생 클래스 객체를 부모클래스 포인터로 가리키고 있을 경우 소멸 순서가 엉망이 된다.

int main() {
	Parent* ptr = new Child();
	delete ptr;

	return 0;
}

~2, ~3이 호출안됨.

 

Parent타입의 포인터로 delete하면 child의 소멸자가 이닌 Parent의 소멸자가 불러져서 Child의 소멸자와 Child의 데이터 멤버도 삭제가 안됩니다.

 

해결방법은 Parent의 소멸자를 virtual로 만들면 된다.

 

 

부모 클래스에서 virtual을 붙이면 파생 클래스에 자동으로 virtual이 적용되지만..

소멸자를 만들때마다 virtual 키워드를 가져다 붙이는 습관을 만들자.

#include <iostream>
using namespace std;

class Something {
public:
	Something() { cout << "2"; }
	virtual ~Something() { cout << "~2"; }
};

class Parent {
public:
	Parent() { cout << "1"; }
	virtual ~Parent() { cout << "~1"; }
};

class Child : public Parent {
public:
	Child() { cout << "3"; }
	virtual ~Child() { cout << "~3"; }

private:
	Something mDataMember;
};

int main() {
	Parent* ptr = new Child();
	delete ptr;

	return 0;
}

virtual 붙이니까 정상

 

MBCS (Muiti Byte Character Set)
한글은 2byte, 영문은 1byte

WBCS (Wide Byte Character Set)
문자 표현시 2byte (UNICODE)

 

MBCS와 WBCS를 동시 지원하게 코드를 만들고 싶으면 

아래의 매크로를 만들어서 사용합시다.

 

#ifdef UNICODE
typedef WCHAR TCHAR;
typedef LPWSTR LPTSTR;
typedef LPCWSTR LPCTSTR;
#else
typedef CHAR TCHAR:
typedef LPSTR LPTSTR;
typedef LPCSTR LPCTSTR;
#endif

#ifdef _UNICODE
#define __T(x) L ## x
#else
#define __T(x) x
#endif

#define _T(x) __T(x)
#define _TEXT(x) __T(x)

동시지원용으로 기본 함수들을 사용할 때는 이미 만들어져있으므로 _t로 되어있는 함수를 사용합시다.

사용할때는 Windows.h 헤더파일을 선언해야되고 wbcs를 사용하려면 #define _UNICODE를 해줍시다.

동시지원용 wbcs mbcs
_tmain wmain main
_tcslen wcslen strlen
_tprintf wprintf printf
_tscanf wscanf scanf
... ... ...

 

+ Recent posts