2025-04-29 TIL

알고리즘 문제

오랜만에 풀어보는 DFS 문제입니다.
이번 문제는 특별한 알고리즘이 있을까 싶었는데, 문제의 배열 길이가 크지 않아 완전탐색으로 풀어보았습니다.
DFS의 탈출 조건이 딱 와닿지 않아서 로직을 구현하는게 가장 오래 걸렸고, 다른 부분은 빠르게 해낼 수 있었습니다.
프로그래머스의 2레밸 문제들이 처음에는 어렵게 느껴졌는데 지금은 어느정도 풀어낼 수 있게 된 것 같습니다.
[프로그래머스][C++] 이모티콘 할인행사

MakeFromX 함수 분석

언리얼 엔진의 MakeFromX 함수를 분석해보는 시간을 가져보았습니다.
MakeFromX는 입력된 벡터를 앞방향으로 가정하고, 이것을 기준으로 직교 좌표계를 구성하여 회전 행렬을 만들고, 반환하는 함수입니다.

template<typename T>
TMatrix<T> TRotationMatrix<T>::MakeFromX(TVector<T> const& XAxis)
{
    TVector<T> const NewX = XAxis.GetSafeNormal();

    // try to use up if possible
    TVector<T> const UpVector = (FMath::Abs(NewX.Z) < (1.f - UE_KINDA_SMALL_NUMBER)) ? TVector<T>(0, 0, 1.f) : TVector<T>(1.f, 0, 0);

    const TVector<T> NewY = (UpVector ^ NewX).GetSafeNormal();
    const TVector<T> NewZ = NewX ^ NewY;

    return TMatrix<T>(NewX, NewY, NewZ, TVector<T>::ZeroVector);
}
  1. 입력 벡터를 앞 방향이라고 가정하고, 정규화하여 NewX를 생성합니다.
    • 이 벡터가 최종 회전 행렬의 X축(앞 방향)이 됩니다.
  2. X축이 Z축과 거의 평행한 경우 크로스 곱 계산에서 Y축을 구할 수 없기 때문에 외적이 실패하지 않도록 보완합니다.
  3. UpVectorNewX의 외적(^)으로 Y축 벡터(NewY)를 계산합니다.
    • 언리얼 엔진은 오른손 좌표계를 사용하기 때문에 해당 좌표계 기준의 오른쪽 방향입니다.
  4. NewXNewY의 외적으로 Z축(NewZ)을 계산합니다.
  5. 3개의 축 벡터를 사용해 회전 행렬(TMatrix)을 생성해 반환합니다.
    • 네 번째 인자는 위치 벡터인데, 여기서는 원점입니다.

코드 실행 순서 심화

#include<iostream>

using namespace std;

int main()
{
    int a=1,b=2,c=3;
    int x=++a+b+++ ++b+c+++a+++ ++c+b+c;
    cout<<"a="<<a<<",b="<<b<<",c="<<c<<",x="<<x<<endl;
}

위 코드에 대해 분석해보는 시간을 가져보았습니다.
일종의 처음 봤을 때 굉장히 당황스러운 코드였는데, 차근차근 살펴보니 아래와 같이 코드를 좀 더 보기좋게 정리할 수 있었습니다.

#include<iostream>

using namespace std;

int main()
{
    int a = 1, b = 2, c = 3;
    int x = ++a + b++ + ++b + c++ + a++ + ++c + b + c;
    cout << "a=" << a << ",b=" << b << ",c=" << c << ",x=" << x << endl;
}

이 상태에서 코드의 실행 순서를 하나씩 파악해보았습니다.

우선 코드의 실행 순서는 왼쪽에서 오른쪽이므로, int x가 선언되고, 오른쪽의 대입 연산자를 발견하게 됩니다.
대입 연산자는 현재 실행해야할 코드 중에 가장 마지막에 실행되는 순서를 가집니다.
그렇기 때문에 오른쪽에 있는 전위와 후위 및 더하기 연산이 다 끝나고 대입됩니다.

더하기 연산자를 기준으로 전위가 먼저 실행되고, 더하기가 실행된 다음 후위가 실행됩니다.
하지만 컴파일러마다 순서가 달라질 수 있는데, 모든 전위와 더하기가 실행된 다음 후위가 실행될 수있습니다.

비주얼 스튜디오 기준으로는 x의 값이 25가 나왔는데, 다른 컴파일러에서는 27이 나오기도 했습니다.

a=3,b=4,c=5,x=25

조건문 심화

조건문에서 조건식에 문자열이 들어가면 어떻게 될지 살펴보았습니다.
평소에 생각해보지 못한 주제인데, 이런 주제를 듣고 굉장히 궁금해져서 직접 코드를 작성해보면서 살펴보았습니다.

#include <iostream>

int main()
{
    if ("false")
    {
    	std::cout << "1" << std::endl;
    }
}

위 코드는 문자열 리터럴 "false"를 조건식으로 사용하고 있습니다.
결과적으로 조건식은 참이므로, 1이 출력됩니다.

그 이유는 다음과 같습니다.

문자열 리터럴은 내부적으로 배열로 저장하기 때문에 포인터로 관리됩니다.
이때 이 포인터는 메모리에 있는 문자열의 시작 주소를 가리키며, 포인터의 값이 NULL이 아니기 때문에 조건식은 참입니다.

만약, 문자열 리터럴에 "false"가 아닌 아무 글자가 없는 ""기본 값이라면 결과가 다를지 궁금해집니다.
이 경우 배열의 크기가 0이 아닌 1이고, 포인터의 값이 NULL이 아니기 때문에 위에서 설명한 것과 같은 이유로 조건식은 참입니다.

정리하면, C++에서 포인터를 조건식에 사용하면 포인터가 nullptr인지 아닌지에 따라 조건식의 결과가 결정됩니다.
이런 이유는 C언어 때부터 0은 거짓 0이 아닌 값은 참으로 간주되며, nullptr일 경우 0이 반환되도록 구현돼있기 때문입니다.

이 개념은 컴퓨터가 이진 논리를 사용하는 것에 기반하고, 값이 0이면 없음, 0이 아니면 값이 있음으로 판단하는 기본적인 원칙에서 비롯했습니다.

"false"가 0이 아닌 값이 존재하는 무언가이므로, 실행될거라고 생각은 했었는데 구체적으로 알아보니 간단한 코드더라도 굉장히 많은 개념이 사용된다는 것을 알 수 있었습니다.

Date:     Updated:

카테고리:

태그:

TIL 카테고리 내 다른 글 보러가기

댓글남기기