프로젝트공부아이디어
    • 2월
    • 1월
  • 30

    26. 1. 30.

  • 29

    26. 1. 29.

  • Day9

    26. 1. 27.

  • Day8

    26. 1. 26.

  • Day5

    26. 1. 23.

  • Day1

    26. 1. 22.

  • Day2

    26. 1. 22.

  • Day3

    26. 1. 22.

  • Day4

    26. 1. 22.

로딩 중...

29

2026. 1. 29.

char 타입에 한글이 들어가면

한글이 char 타입 변수에 저장된다는건 char 타입이 2바이트로 늘어나는게 아니라 한글의 바이트 단위 조각이 각각 저장된다는 뜻 한글 처리는 "문자 인코딩" 문제

인코딩한글 1글자 크기
ASCII❌ (한글 없음)
EUC-KR2바이트
CP949 (Windows 기본)2바이트
UTF-83바이트
UTF-162바이트
UTF-324바이트

Windows(CP949)

C++
char str[] = "가";// "가" -> 0xB0 0xA1str[0] = 0xB0str[1] = 0xA1str[2] = '\0'

UTF-8

C++
char str[] = "가";// "가" -> 0xEA 0xB0 0x80str[0] = 0xEAstr[1] = 0xB0str[2] = 0x80str[3] = '\0'

mblen: 멀티바이트의 길이

strlen: 문자열 전체의 길이 mblen: 주어진 문자열 시작에 있는 한 멀티바이트 문자의 길이

파일입출력

  • 메타데이터를 파일로 직렬화시킬 때, 곧이곧대로 저장하면 악용될 여지가 있으므로 별도의 암호화 단계가 필요하다

Value Category

  • RValue 모음
C++
int main(){	0;	Player();	Function();}
  • Value Reference 에러 모음
C++
int main(){	int& lvalue = 10;	int&& rvalue = count;}
  • Move 생성자/대입 연산자
C++
class Player { public: 	// 기본 생성자. 	Player() { } 	// 복사 생성자. 	Player(const Player& other) { } 	// 대입 연산자. 	Player& operator=(const Player& other) { } 	// Move 생성자. 	Player(Player&& other) { } // 오히려 const 붙이면 안된다	// Move 대입 연산자. 	Player& operator=(Player&& other) { }};
  • 복사 생성자는 무겁다
C++
class Player { public: 	Player(const Player& other)	 : playerID(other.playerID); 	{ 		//playerID = other.playerID; 		size_t length = strlen(other.name) + 1; // 메모리 할당은 기본적으로 운영체제 커널 함수를 호출해야하므로 무겁다		name = new char[length]; 		strcpy_s(name, length, other.name); // 문자열 자체도 무거움	} 	int playerID; 	char* name; };
  • 반면 Move 생성자는 주소만을 핸들링하므로 가볍다
C++
class Player{public:	Player(Player&& other)	{		playerID = other.playerID; // 주소값 복사		name = other.name; // 주소값 복사. 이동이라 표현하지만 실제론 복사 행위다		other.name = nullptr; // other란 임시 r-value의 소유권 해제	}	int playerID;	char* name;};
C++
#include <iostream>#include <string>class NPC{public:	NPC() { std::cout << "기본 생성자\n"; }	NPC(int npcCode, const std::string& name) { std::cout << "인자 있는 생성자\n"; }		NPC(const NPC& other) { std::cout << "복사 생성자\n"; }	NPC& operator=(const NPC& other) { std::cout << "대입 연산자\n"; return *this; }		NPC(NPC&& other) { std::cout << "Move 생성자\n"; }	NPC& operator=(NPC&& other) { std::cout << "Move 대입 연산자\n"; return *this; }	int npcCode;	std::string name;};int main(){	std::cout << "1\n";	NPC npc1(NPC(10, "Orge1"));	//NPC npc1(10, "Orge1");    // 컴파일러가 이런식으로 최적화함.	std::cout << "\n2\n";	NPC npc2(11, "Orge2");	NPC npc3 = npc2;	std::cout << "\n3\n";	NPC npc4;	NPC npc5;	npc5 = npc4;	std::cout << "\n4\n";	NPC npc6 = NPC(12, "Orge3");	std::cout << "\n5\n";	NPC npc7;	NPC npc8;	npc8 = std::move(npc7); //std::move는 인자를 R-Value로 바꿈		// 여기서부터는 npc7을 사용하면 안됨(읽히긴 하나 언젠간 해제되어 보장이 안되기 때문).	std::cin.get();}
C++
#include <iostream> int main() { 	int lvalue1; 	int&& rvalueRef1 = lvalue1; // Error 	int&& rvalueRef2 = std::move(lvalue1); // OK. 	int&& rvalueRef3 = rvalueRef2; // Error. RValue 참조를 RValue 참조로 초기화할 수 x	std::cin.get(); }
  • 템플릿의 인자추론이 망가질 때가 종종 있다. 이럴 땐 std::forward()를 사용할 것
C++
#include <iostream>struct X{};void func(X&& x) { std::cout << "RValue\n"; }void func(X& x) { std::cout << "LValue\n"; }template<typename T>void foo(T&& t) { func(t); } // LValue LValuevoid foo(T&& t) { func(std::move<T>(t)); } // RValue RValuevoid foo(T&& t) { func(std::forward<T>(t)); } // LValue RValue, Perfect Forwarding!int main(){	X x;	foo(x);	foo(X());	std::cin.get();}
  • 깊은 복사보다 얕은 복사가 더 빠르다(힙 영역에 메모리 할당 vs 주소 소유권만 이전)
  • 다만 LValue를 move로 처리한다면 기존 LValue가 쓰였던 곳의 fallback 처리를 잘 해야한다.

언리얼: 이동 시맨틱

TArray, TMap, TSet, FString과 같은 모든 주요 컨테이너 타입에는 move 생성자와 move 대입 연산자가 있습니다. 이러한 타입을 값으로 전달 또는 반환할 때 종종 자동으로 사용되지만, std::move 의 UE 해당 버전인 MoveTemp 를 통해 명시적으로 호출할 수도 있습니다.

값으로 컨테이너나 스트링을 반환하는 것은 보통 임시로 복사하는 비용이 없어 표현성에 유용하게 작용할 수 있습니다. 값 전달 관련 규칙 및 MoveTemp 사용법은 아직도 확립 중이지만, 최적화된 코드베이스 영역 일부에서는 이미 찾아볼 수 있습니다.

함수 포인터

C++
int Function(){	return 100;}int main(){	// FunctionPointer 함수 포인터 변수에 Function 함수 저장.	// 함수를 함수 포인터에 저장할 때는 함수의 이름을 그대로 대입.	int (*FunctionPointer)() = Function;		// FunctionPointer 변수를 통해 함수 호출.	// 포인터 타입이기 때문에 null 비교가 가능.	if (FunctionPointer != nullptr)	{		FunctionPointer();	}}
  • callback
C++
// 두 값을 서로 교환하는 함수.template<typename T>void Swap(T& a, T& b){	T temp = a;	a = b;	b = temp;}// 두 값을 비교하는 함수.bool CompareValues(int a, int b){	return a > b;}// 버블 정렬 함수.typedef bool (*Comparer)(int, int); using Comparer = bool (*)(int, int); // typedef나 using을 활용하면 Comparer comparer로 간소화가 가능하다.void BubbleSort(int array[], int length, bool (*Comparer)(int, int)){	// 버블 정렬.	for (int ix = 0; ix < length - 1; ++ix)	{		for (int jx = 0; jx < length - 1; ++jx)		{			// 두 개씩 비교.			//if (array[jx] > array[jx + 1])			if (Comparer(array[jx], array[jx + 1]))			{				// 현재 값과 다음 값 교환.				Swap<int>(array[jx], array[jx + 1]);			}		}	}}int main(){	int array[] = { 5, 2, 8, 4, 1 };	int length = sizeof(array) / sizeof(array[0]);		BubbleSort(array, length, CompareValues);}
  • std::function
    • <functional> 헤더를 통해 함수 포인터를 정의할 수 있다.
    • 성능이 좋지 않다(템플릿 추론 때문에)
  • 람다
    • []() -> void { return ;}

frontBuffer, backBuffer

https://blog.naver.com/znfgkro1/80174839762 콘솔 게임은 이미지 버퍼 플립이 안돼서 콘솔 핸들 2개를 플립하는 구조로 구현

콘솔게임 유의사항

  1. 좌측상단이 (0, 0)
  2. 화면 우측 끝을 넘어가면 좌측 화면으로 넘어옴(값은 0이 아닌 양수)
  3. 화면 하단 끝을 넘어가면 스크롤 + 상단으로 넘어오므로 주의
  4. 스크롤 구현하려면 내부 로직을 직접 만들어주는게 편함
왼쪽 화살표2730오른쪽 화살표