[TIL] 23-12-19

Lev·2023년 12월 19일
0

📓TIL Archive

목록 보기
7/33

📢 기억해두면 좋은 VS단축키

  • Shift + Up or Down ⇒ 드래그와 같은 효과
  • 범위 선택 + Alt + Up or Down ⇒ 범위 통째로 윗줄 또는 아랫줄로 이동
  • Ctrl + K+ C ⇒ 주석
  • 파일명 위에 커서 + Ctrl + Shift + G ⇒ 파일 열기?(iostream과 같은 파일 내용 확인 가능)
  • Ctrl + A ⇒ 문서 코드 전체 선택
  • Ctrl + K + F ⇒ 자동 줄맞춤

printf (이어서)

// < %d를 포함한 MyPrintf 구현 >

#include <iostream>

void NumberToString(int _Number, char* _Ptr)
{
	int Cnt = 0;

	for (int num = _Number; num > 0; num /= 10)
	{
		Cnt += 1;
	}

	for (int i = Cnt; i > 0; i--)
	{
		_Ptr[i - 1] = (_Number % 10) + '0';
		_Number /= 10;
	}
}


int MyPrintf(const char* const _Format, ...)
{
	__int64 Address = reinterpret_cast<__int64>(&_Format);	// 주소값
	const int* Ptr = reinterpret_cast<int*>(Address);	// 해당 주소값을 가리키는 포인터
	// const char* const* Ptr = &_Format; => 위 두줄과 같은 의미

	Ptr = Ptr + 2;	// 8byte 뒤로 => 두번째 인자

	int Count = 0;
	// 문자열의 길이를 알아낸 다음 for문을 돌려도 좋지만,
	// 0을 만날때까지 반복문이 동작하면 되므로 while문이 좋다
	while (_Format[Count])
	{
		char Ch = _Format[Count];

		// 1. 포매팅 문자인 %를 만나지 않을 경우
		if (Ch != '%')
		{
			putchar(_Format[Count]);
			++Count;
			continue;
		}
		// Ch == '%' 라고 적지 않는 이유
		// => 가장 깔끔하게 떨어지는 경우를 상단에 두는 것이 좋다

		// 2. 포매팅 문자인 %를 만날 경우
		char NextCh = _Format[Count + 1];

		switch (NextCh)
		{
		case 'd':
		{
			char Arr[100] = {};	// 배열 초기화 잊지 말자!!!
			NumberToString(*Ptr, Arr);

			int Index = 0;
			while (Arr[Index])
			{
				putchar(Arr[Index]);
				++Index;
				++Count;
			}

			break;
		}
		default:
			break;
		}

		Ptr = Ptr + 2;	// 8byte 뒤로 => 다음 인자
	}
	return Count;
}

int main()
{
	int Return = MyPrintf("aaaa bbb");
	Return = MyPrintf("aaaa bbb %d %d %d", 123, 345, 6789);	// "aaaa bbb 1233456789"
}

포인터 (이어서)

포인터의 숙련도 == C++ 실력

#include <iostream>

void Function(int****** ptr, int _Value)
{
	// 두번째 인자에 접근하는 방법 두 가지
	{
		int* IPtr = reinterpret_cast<int*>(&ptr);
		char* CPtr = reinterpret_cast<char*>(&ptr);

		IPtr = IPtr + 2;	// 8byte 뒤로
		CPtr = CPtr + 8;	// 8byte 뒤로
	}
	{
		__int64 Address = reinterpret_cast<__int64>(&ptr);
		Address = Address + 8;	// 주소값 8 뒤로

		int* ValuePtr = reinterpret_cast<int*>(Address);
	}
}

int main()
{
	int Value = 1;	// 100번지에 있는 1이라는 값
	char* Ptr1 = reinterpret_cast<char*>(&Value);	// ??에 있는 100번지라는 값
	__int64 Address = reinterpret_cast<__int64>(Ptr1);	// 100번지

	char** Ptr2 = reinterpret_cast<char**>(&Value);	// ??에 있는 100번지라는 값(?)
	int* Ptr3 = reinterpret_cast<int*>(Ptr1);	// ??에 있는 100번지라는 값(?)
	*Ptr3 = 20;	// 100번지에 있는 Value 값 : 1 -> 20

	Function(nullptr, 20);

	return 0;
}
  • 결국 중요한 것은, ‘*가 몇 개 붙는지, 자료형이 어떤 것인지’가 아니라 ‘무엇이 메모리 어디에 위치하고, 크기가 얼마이며, 무엇을 가리키고 있는지’이다.

문자열과 배열

#include <iostream>

// 정적배열을 인자로 받을 수 있을 것 같지만, 아니다
// 문법적으로 허용되지만 컴파일러가 아래와 같이 변경해버리기 때문
// void Funcion(int* _Ptr)
void Function(int _Ptr[10])
{
	int Size = sizeof(_Ptr);	// 40byte(X), 8byte(O)
}

int main()
{
	{
		int ArrayEx[99];
		Function(ArrayEx);
		// 인자를 넣어줄 때 암시적 형변환이 발생한다
		// int[] -> int*
	}
	{
		"aaaaa";	// const char [6]
		// 문자열의 마지막에는 항상 0이 들어간다는 것을 잊지 말자
	}
	{
		strlen("fdfadfdsdaf");	// => 문자열 길이
		// strcpy_s(); => 문자열 복사
		// strcmp(); => 문자열 비교
		// 문자열은 전용 함수들이 따로 있고, 종류도 다양하다
		// 직접 구현해보면 도움이 된다
	}
	{
		char Ptr[10] = "ABC";

		const char* LeftPtr = Ptr;	// 스택영역 속 주소
		const char* RightPtr = "ABC";	// 코드영역 속 주소

		if (Ptr == "ABC")
		{
			// if (LeftPtr == RightPtr) 과 동일한 의미이므로 false이다
			// 문자열을 비교할 때에는 strcmp() 함수를 사용
		}
	}
}

문자열 파싱

  • JSON, xml과 같은 데이터를 파싱하여 사용하는 일을 하게 될 것
  • 문자열을 다루는 법을 잘 알아두는 것이 너무너무 중요하다.

과제

// Q) CharConvert()를 완성시키자

#include <iostream>

int CharConvert(char* _String, char _PrevCh, char _NextCh)  // 바뀐 글자수를 리턴하는 함수
{
    int Cnt = 0;
    int Res = 0;

    while (_String[Cnt])
    {
        if (_String[Cnt] == _PrevCh)
        {
            _String[Cnt] = _NextCh;
            ++Res;
        }

        ++Cnt;
    }

    return Res;
}

int main()
{
    char Arr[10] = "aaabbbccc";

    int Result = CharConvert(Arr, 'b', 'd'); // 3
}
// Q) LeftMoveString()과 DeleteChar()를 완성시키자

#include <iostream>

int StringCount(char* _Ptr)
{
	int Count = 0;

	while (_Ptr[Count])
	{
		++Count;
	}

	return Count;
}

void LeftMoveString(char* _Ptr, int _Start) // start부터 왼쪽으로 한칸 미는 함수
{
    int Cnt = StringCount(_Ptr);

    if (_Start >= 2)
    {
        for (int i = _Start - 1; i < Cnt - 1; i++)
        {
            _Ptr[i] = _Ptr[i + 1];
        }
    }
    else
    {
        for (int i = 0; i < Cnt - 1; i++)
        {
            _Ptr[i] = _Ptr[i + 1];
        }
    }
    _Ptr[Cnt - 1] = 0;

    /*
    // 방어 코드
    if (_Ptr == nullptr)
    {
        return;
    }

    int Count = StringCount(_Ptr);

    // 방어 코드
    if (_Start <= 0)
    {
        _Start = 1;
    }

    for (int i = _Start - 1; i < Count; i++)
    {
        _Ptr[i] = _Ptr[i + 1];
    }
    */
}

void DeleteChar(char* _Ptr, char _Delete)   // 특정 char를 삭제하는 함수
{
    int Cnt = StringCount(_Ptr);

    for (int i = 0; i < Cnt; i++)
    {
        if (_Ptr[i] == _Delete)
        {
            LeftMoveString(_Ptr, i + 1);
        }
        char ch = _Ptr[i];
    }
}

int main()
{
    {
        char Arr[100] = "abcde";
        // LeftMoveString(Arr, 0); // "bcde"
        // LeftMoveString(Arr, 1); // "bcde"
        LeftMoveString(Arr, 2); // "acde"
    }
    {
        char Arr[100] = "a b c d e";
        // DeleteChar(Arr, ' ');   // "abcde"
        DeleteChar(Arr, 'b');   // "a  c d e"
    }
}

방어코드(Defense Code)

  • 알지 못하는 메모리를 침범하려고 하는 코드를 막기 위해 사용한다.
int main()
{
	int Value = 517;
	
	char* Ptr = reinterpret_cast<char*>(&Value);
	
	// Value	0b 00000000 00000000 00000010 00000101
	
	//			   Ptr[0]   Ptr[1]   Ptr[2]   Ptr[3]
	// char*	0b 00000101 00000010 00000000 00000000

	// 바이트 순서가 거꾸로 들어가는 현상의 이름은?
}
profile
⋆꙳⊹⋰ 𓇼⋆ 𝑻𝑰𝑳 𝑨𝑹𝑪𝑯𝑰𝑽𝑬 ⸝·⸝⋆꙳⊹⋰

0개의 댓글