[TIL] 24-01-19

Lev·2024년 1월 19일
0

📓TIL Archive

목록 보기
26/33

ERROR

zero division, null pointer exception

class Test
{
public:
	int Value;

	void Function()
	{
		this->Value = 20;
	}
};

Test* CreateTest()
{
	return nullptr;
}

int main()
{
	{
		// [zero division]
		int Value = 0;
		int Test = 100 / Value;
	}
	{
		// [null reference exception]
		Test* NewTest = CreateTest();
		NewTest->Function();
	}
}

조사식

~ to be written ~

자료구조

String

/*
문자를 표현하는 방식 = 인코딩
숫자 1개와 문자 1개를 매칭시키는 방식

[아스키] (초기형 방식)
- 7비트
- 현재는 ansi, 멀티바이트 방식이라고 불리는 것에 통합되었다

[ansi]
- 1바이트 (char => 0 ~ 255, 'A' = 67)
- 아스키의 확장형
- 초창기에는 한글을 고려할 필요가 없었기 때문에, 255개의 숫자에
  코딩에 필요한 모든 문자가 잘 매칭되어 있었다 (+, -, *, /, [, ], ...도 포함)

[멀티바이트]
- 상황에 따라 1바이트 or 2바이트
- 65536개까지 가능...하지만 전세계의 글자를 다 넣을순 없었다
- 그래서 국가코드가 탄생했다
- 운영체제에 설정된 국가를 따르기 때문에 운영체제의 영향을 받는다
- 몇바이트를 사용할지 판단해야 하므로 좀 느리다...

[유니코드] = 와이드바이트
- 2바이트
- 여전히 국가코드간 변환을 해야한다는 불편함이 있다
- 전용 자료형인 wchar_t를 C++에서 지원한다
- 문자열 앞에 L을 붙이는 표현식을 사용한다

[UTF-8]
- 1 ~ 4바이트 안에 전세계 모든 글자를 넣었다
- 국가간 변환이 필요하지 않다(?)
- 예를 들어 U+0000부터 U+007F 범위에 있는 아스키 문자들은 1바이트만으로 표시된다
- 이모지(...)도 포함될 정도로 다양한 문자의 형태가 포함되어 있다
- UTF-16은 잘 사용하지 않는다...


일반적으로 코딩할 때에는, 내가 사용하는 문자열의 인코딩방식이 무엇인지 인지해야 한다.
함수를 사용하다보면 문자열을 인자로 받는 경우가 있는데, 이 때 만약 제대로 동작하지 않는다면
내가 넣어준 문자열의 값이 잘못된 것이 아니라, 인코딩이 잘못되었을 수 있음을 인지하고 있어야 한다.
*/

int main()
{
	char Arr0[3] = "가";		// 멀티바이트 (2 + 1바이트)
	wchar_t Arr1[2] = L"가";	// 유니코드 (1 + 1바이트)
	char8_t Arr2[4]= u8"가";	// UTF-8 (3 + 1바이트)

	// 기본적으로 멀티바이트를 사용하고, 필요할 때 와이드바이트나 UTF-8 방식으로 변환하자!
}
/*
[string]
- 어댑터 컨테이너 자료구조?
- char형 벡터라고 생각하면 편하다
*/

#include <string>
#include <iostream>

class MyString
{
public:
	int Size = 0;
	char* Arr = nullptr;

	MyString(const char* _Ptr)
	{
		// 깊은 복사
		Size = strlen(_Ptr) + 1;
		Arr = new char[Size] {0, };

		for (size_t i = 0; i < Size; i++)
		{
			Arr[i] = _Ptr[i];
		}
	}

	MyString(const MyString& _Other)	// 복사 생성자
	{
		// 깊은 복사
		Size = _Other.Size;
		Arr = new char[Size] {0, };

		for (size_t i = 0; i < Size; i++)
		{
			Arr[i] = _Other.Arr[i];
		}
	}

	~MyString()
	{
		if (Arr != nullptr)
		{
			delete Arr;
			Arr = nullptr;
		}
	}

	void operator =(const MyString& _Other)
	{
		// 얕은 복사
		Arr = _Other.Arr;
	}
};

void TestFunction(std::string Text)
{
	
}

void TestMyFunction(const MyString& Text)
{
	/*
	인자가...
	
	MyString Text 인 경우
	- 복사 생성자로 들어가 깊은 복사가 발생한다

	const MyString& Text 인 경우
	- 레퍼런스이기 때문에 값을 새로 생성할 필요가 없어 생성자를 거치지 않고
	- 새로이 메모리를 소모하지 않는다
	*/
}

int main()
{
	// 멀티바이트 인코딩
	// std::vector<char>
	std::string Text0;
	Text0.reserve(3);
	Text0 = "가";

	std::string Text1;
	Text1.reserve(3);
	Text1 = "나";

	std::string Result = Text0 + Text1;	// "가나"

	// 와이드바이트 인코딩 (유니코드)
	// std::vector<wchar_t>
	std::wstring wText0 = L"가";
	std::wstring wText1 = L"나";

	std::wstring wResult = wText0 + wText1;	// "가나"

	// 문자열을 인자로 받을 경우
	MyString String0 = "aaaaaaa";
	TestMyFunction(String0);
	// 굳이 깊은 복사를 할 필요가 없다

	// 문자열을 복사할 경우
	MyString String1 = MyString("aaaaaaa");	// 생성자
	MyString String2 = String0;				// 복사 생성자
	String2 = String0;						// 대입 연산자 -> 소멸자 단계에서 터진다
	// 이 경우 깊은 복사가 필요하다
	// [TIL] 24-01-08 참고

	// 하지만 동적할당을 남발할 경우 자원을 크게 소모한다는 것을 인지하고 있어야 한다
	// 특히 게임은 프로젝트가 거대한만큼 더 심각하니 필요하지 않은 곳에서는 지양하자
}

💡 메모리 단편화
RAM의 메모리 중간에 틈새가 생기고 정리되어 있지 않은 상태

메모리를 할당한다 ⇒ 느려진다
할당된 메모리를 삭제한다 ⇒ 느려진다.
새로이 메모리를 할당하기 위해 빈 공간을 찾는다 ⇒ 느려진다

빌드

Debug / Release

/*
[Debug 모드]
- 코드를 최적화하는 과정을 거치지 않고 인간이 인식하는 그대로를 보여준다
- 코드 중에 불피요한 부분을 삭제 또는 치환하는 최적화를 하지 않는다
- 인간이 이해하기 쉬운 모드로 변환하고, 함수의 이름, 변수의 이름 등을 전부 기억해둔다
- int A = 0; 과 같은 변수가 있다면 그대로 A라는 이름으로 남겨두고 사용자에게 보여준다

[Release 모드]
- 최적화가 완료되어 컴퓨터가 가장 빠르다고 생각하는 모습으로 코드를 내부에서 변경시켜버린다
- 중단점이 걸리긴 하지만, 값을 제대로 확인할 수 없기 때문에 의미가 없다
- 마지막에 가서 release 모드로 빌드하면 그때까지의 코드 중 어디서 문제가 발생했는지 알 수 없다
- 따라서 release빌드를 사용할 때에는 자주 빌드해보고 커밋하는 것이 중요하다
- debug 모드에 비해 3~5배 이상 프레임이 오르기 때문에 실제 출시도 release빌드로 한다
*/

x86 / x64

/*
[x64] = 64비트

[x86] = 32비트
- 4GB까지의 RAM만을 인식할 수 있다
*/

int main()
{
	int Ptr = sizeof(int*);
	// x64 => 8바이트
	// x86 => 4바이트
}

WinAPI

/*
[인터페이스]
어떤 동작을 하는 코드를 사용하기 위한 모든 것
e.g.
소리를 재생하기 위한 함수를 제공한다.
	=> 소리를 재생하기 위한 인터페이스를 제공한다.
소리를 재생하려면 xxx클래스를 사용해야 한다.
	=> 소리를 재생하려면 xxx클래스를 통한 인터페이스를 제공받아야 한다.

[WinAPI]
- API = Application Programming Interface
- Windows를 사용하기 위한 모든 방법을 종합한 인터페이스
(변수, 함수, 클래스, 멤버변수, 멤버함수, ... 등등)
*/

솔루션 → 추가 → 새 프로젝트 → Windows 데스크톱 애플리케이션
⇒ 윈도우에서 앱을 만들기 위한 기본적인 방법을 담아둔 프로젝트
프로젝트 속성 → 구성 속성 → 링커 → 시스템 → 하위 시스템
윈도우 프로젝트 : 창(/SUBSYSTEM:WINDOWS)
콘솔 프로젝트 : 콘솔(/SUBSYSTEM:CONSOLE)

#include "framework.h"
#include "WindowsProject1.h"

#define MAX_LOADSTRING 100

// 전역 변수 : 
HINSTANCE hInst;                                // 현재 인스턴스입니다.
WCHAR szTitle[MAX_LOADSTRING];                  // 제목 표시줄 텍스트입니다.
WCHAR szWindowClass[MAX_LOADSTRING];            // 기본 창 클래스 이름입니다.

// 이 코드 모듈에 포함된 함수의 선언 :
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);


// 진입점 = main() :
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
/*
APIENTRY = __stdcall
- static 멤버함수 등 전역함수 전용 함수호츌규약
- 사실 굳이 적지 않아도 되지만 명시해주는 것
(__cdecl - C전역함수 전용)
(__thiscall - 멤버함수 전용)

_In_
- 아마 여러 환경을 대비한 것이지만,..
- 어짜피 윈도우에서 하기 때문에 삭제해도 된다.
*/
{
    // 사용하지 않은 인자 사용 :
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);
    /*
    프로젝트 속성 -> 구성 속성 -> C/C++ -> 일반 -> 경고 수준 -> 모든경고사용
    - ...일 경우 인자를 사용하지 않기만 해도 에러가 나기 때문에 이를 회피하기 위함
    - 하지만 경고 수준을 굳이 높일 이유가...?
    (경고를 오류로 처리하는 설정은 켤 수도 있다)
    */

    // TODO: 여기에 코드를 입력합니다.

    // 전역 문자열 초기화 :
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_WINDOWSPROJECT1, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 애플리케이션 초기화 :
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT1));

    MSG msg;

    // 기본 메시지 루프 :
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}

/*
함수: MyRegisterClass()
용도: 창 클래스를 등록합니다.
*/
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT1));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT1);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    // 윈도우 창 클래스 만들기 :
    return RegisterClassExW(&wcex);
    /*
    프로그램이 윈도우에게 창을 띄워달라고 요청할 때, 
    WNDCLASSEW wcex에 입력한 모양으로 띄워달라고 요청하는 것
    그 모양의 정보를 담은 클래스 이름이 swWindowClass
    */
}

/*
함수: InitInstance(HINSTANCE, int)
용도: 인스턴스 핸들을 저장하고 주 창을 만듭니다.

이 함수를 통해 인스턴스 핸들을 전역 변수에 저장하고
주 프로그램 창을 만든 다음 표시합니다.
*/
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   // 인스턴스 핸들 :
   hInst = hInstance;
   /*
   내 프로그램의 제어 권한이자 표식
   인스턴스 핸들을 전역 변수에 저장한다.
   */

   // 윈도우 창 생성 :
   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
   /*
   윈도우를 제어할 수 있는 권한이자 표식
   마치 포인터처럼, 이 인스턴스? 창?에 일련번호를 붙혀주고 그것을 return한다.
   윈도우가 할당되어 있는 진짜 메모리 주소를 주면 위험하므로 대신 이 번호를 받는다.
   */ 

   if (!hWnd)
   {
      return FALSE;
   }

   // 윈도우 창 띄우기 : 
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

/*
함수: WndProc(HWND, UINT, WPARAM, LPARAM)
용도: 주 창의 메시지를 처리합니다.

WM_COMMAND  - 애플리케이션 메뉴를 처리합니다.
WM_PAINT    - 주 창을 그립니다.
WM_DESTROY  - 종료 메시지를 게시하고 반환합니다.
*/
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 메뉴 선택을 구문 분석 :
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 여기에 hdc를 사용하는 그리기 코드를 추가합니다...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// 정보 대화 상자 메시지 처리기 :
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == 2)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}
profile
⋆꙳⊹⋰ 𓇼⋆ 𝑻𝑰𝑳 𝑨𝑹𝑪𝑯𝑰𝑽𝑬 ⸝·⸝⋆꙳⊹⋰

0개의 댓글