<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Dummers.log</title>
        <link>https://velog.io/</link>
        <description>tik tok</description>
        <lastBuildDate>Sun, 06 Jul 2025 08:57:32 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Dummers.log</title>
            <url>https://images.velog.io/images/dong_kyu-lee/profile/f2d2059d-4f49-4476-bfa9-5c3b221778a0/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Dummers.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dong_kyu-lee" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[C++] 함수 포인트 vs 함수 객체]]></title>
            <link>https://velog.io/@dong_kyu-lee/C-%ED%95%A8%EC%88%98-%ED%8F%AC%EC%9D%B8%ED%8A%B8-vs-%ED%95%A8%EC%88%98-%EA%B0%9D%EC%B2%B4</link>
            <guid>https://velog.io/@dong_kyu-lee/C-%ED%95%A8%EC%88%98-%ED%8F%AC%EC%9D%B8%ED%8A%B8-vs-%ED%95%A8%EC%88%98-%EA%B0%9D%EC%B2%B4</guid>
            <pubDate>Sun, 06 Jul 2025 08:57:32 GMT</pubDate>
            <description><![CDATA[<p>함수 포인터와 함수 객체 비교</p>
<h2 id="함수-포인터">함수 포인터</h2>
<p>함수 포인터는 함수에 대한 주소값을 저장하는 포인터이다.
표준 C에서는 함수의 인자로 다른 함수의 포인터(함수 포인터)를 받아와 간접 호출할 수 있다.</p>
<pre><code class="language-cpp">// ex)
int eval(int a, int b, int (*f)(int,int)) {
    return f(a,b);
}

// type: int (*)(int,int)
int add(int a, int b) { return a+b; }
int sup(int a, int b) { return a-b; }

int main() {
    cout &lt;&lt; eval(4,3,add); // print 7
    cout &lt;&lt; eval(4,3,sup); // print 1
}</code></pre>
<hr>
<h2 id="함수-객체">함수 객체</h2>
<p>함수 객체는 다른 함수의 인자로 넣을 수 있는 호출 가능한 객체이다.
함수 객체 안에 함수 호출 연산자<code>operator()</code>를 재정의해 함수 객체를 구현할 수 있다.</p>
<pre><code class="language-cpp">// ex)
#include &lt;algorithm&gt;

struct Descending{
    bool operator()(int a, int b){ return a &gt; b; }
};

int main() {
    int array[] = {7,2,5,1};
    std::sort(array, array+4, Descending{});
    // 내림차순 정렬{7, 5, 2, 1}
}</code></pre>
<pre><code class="language-cpp">// ex) 제네릭을 사용한 예시
struct A {
    int x;
    A(int x) : x(x) {}
    A() : x(0) {};
};

template&lt;typename T, typename F&gt;
T add(T&amp; a, T b, F f) {
    return f(a, b);
}

struct Adding {
    A&amp; operator()(A&amp; a, const A&amp; b) { 
        a.x += b.x;
        return a;
    }
};

int main()
{
    A a(10);
    A b(20);
    A c = add(a, b, Adding{});
    cout &lt;&lt; c.x &lt;&lt; endl;
}</code></pre>
<hr>
<h2 id="함수-포인터-vs-함수-객체">함수 포인터 vs 함수 객체</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>함수 포인터</th>
<th>함수 객체</th>
</tr>
</thead>
<tbody><tr>
<td>안정성</td>
<td>제네릭의 경우 인자의 타입을 확인할 수 없음.</td>
<td><code>template</code>을 포함한 모든 상황에서 타입 검사가 이루어져 안전함.</td>
</tr>
<tr>
<td>성능</td>
<td>함수 인라인화가 불가능해 성능에 좋지 않음.</td>
<td>함수 객체를 호출한 함수에 operator() 를 삽입하고 컴파일이 진행된다. 그리고 연산자는 인라인화가 가능하기 때문에 함수 포인터보다 좋은 성능을 보여준다.</td>
</tr>
<tr>
<td>상태 유지</td>
<td>상태를 가지지 못함.</td>
<td>클래스 내의 변수 등을 통해 상태를 가질 수 있음.</td>
</tr>
<tr>
<td>.</td>
<td></td>
<td></td>
</tr>
<tr>
<td>.</td>
<td></td>
<td></td>
</tr>
<tr>
<td>.</td>
<td></td>
<td></td>
</tr>
<tr>
<td>C++ 공부를 위해 작성된 글입니다. 오류가 있다면 지적해 주시면 감사하겠습니다.</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<p>참고자료
<a href="https://github.com/federico-busato/Modern-CPP-Programming">https://github.com/federico-busato/Modern-CPP-Programming</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C언어 TCP/IP 소켓 통신(1)]]></title>
            <link>https://velog.io/@dong_kyu-lee/C%EC%96%B8%EC%96%B4-TCPIP-%EC%86%8C%EC%BC%93-%ED%86%B5%EC%8B%A01</link>
            <guid>https://velog.io/@dong_kyu-lee/C%EC%96%B8%EC%96%B4-TCPIP-%EC%86%8C%EC%BC%93-%ED%86%B5%EC%8B%A01</guid>
            <pubDate>Tue, 01 Jul 2025 10:51:48 GMT</pubDate>
            <description><![CDATA[<h2 id="소켓-분류">소켓 분류</h2>
<h3 id="주소-체계로-구분">주소 체계로 구분</h3>
<ul>
<li><p><strong>유닉스 도메인 소켓</strong> : 유닉스 상에서 같은 호스트의 다른 프로세스들 간의 통신에 사용되는 소켓. (*패밀리: AF_UNIX)</p>
</li>
<li><p><strong>인터넷 소켓</strong> : 인터넷을 통해 다른 호스트와 통신할 때 사용. (패밀리: AF_INET)</p>
</li>
<li><p>*패밀리(Family) : 소켓의 주소 체계를 지정하는 개념</p>
<ul>
<li>소켓의 통신 상대방과 어떤 주소 체계로 통신할지 알려주는 역할을 한다.</li>
<li>이 값에 따라 소켓이 해석하는 주소의 종류와 구조가 달라진다.</li>
<li><table>
<thead>
<tr>
<th>패밀리 상수</th>
<th>설명</th>
<th>주소 표현 방식</th>
</tr>
</thead>
<tbody><tr>
<td>AF_INET</td>
<td>인터넷 소켓(IPv4주소체계)</td>
<td>32bit IP주소+16bit포트번호</td>
</tr>
<tr>
<td>AF_UNIX</td>
<td>유닉스 도메인 소켓</td>
<td>파일 시스템의 경로명</td>
</tr>
<tr>
<td>AF_INET6</td>
<td>IPv6 주소체계</td>
<td>IPv6주소(128bit)+포트번호</td>
</tr>
<tr>
<td>기타</td>
<td>블루투스(AF_BLUETOOTH) 등</td>
<td></td>
</tr>
</tbody></table>
</li>
<li>소켓을 생성할 때 패밀리 상수를 명확히 지정해야 운영체제가 해당 소켓에 맞는 주소 구조체와 통신 방식을 적용할 수 있다.</li>
</ul>
</li>
</ul>
<h3 id="프로토콜에-따라-구분">프로토콜에 따라 구분</h3>
<ul>
<li><p><strong>스트림 소켓(SOCK_STREAM)</strong></p>
<ul>
<li>TCP를 사용하는 소켓</li>
<li>상수 <code>SOCK_STREAM</code>를 사용</li>
<li>TCP를 사용하기 때문에, 상대방 소켓과의 연결(Connect)과정이 필요함.</li>
</ul>
</li>
<li><p><strong>데이터그램 소켓(SOCK_DGRAM)</strong></p>
<ul>
<li>UDP를 사용하는 소켓</li>
<li>상수 <code>SOCK_DGRAM</code>을 사용</li>
<li>신뢰성이나 순서 보장이 없지만, 이로 인해 빠르게 데이터를 전송해야 할 때 사용. (ex. 동영상 스트리밍)</li>
</ul>
</li>
</ul>
<h3 id="주소체계프로토콜-형태로-소켓-유형을-나눔">주소체계+프로토콜 형태로 소켓 유형을 나눔</h3>
<p>위 분류의 상수 값들을 사용해 주로 사용되는 4종류의 소켓 유형을 갖게 된다.</p>
<ul>
<li>AF_INET + SOCK_STREAM : IPv4 주소체계의 TCP 통신</li>
<li>AF_INET + SOCK_DGRAM : IPv4 주소체계의 UDP 통신</li>
<li>AF_UNIX + SOCK_STREAM : 프로세스 간 TCP 통신</li>
<li>AF_UNIX + SOCK_DGRAM : 프로세스 간 UDP 통신</li>
</ul>
<hr>
<h2 id="소켓-주소-구조체">소켓 주소 구조체</h2>
<h3 id="유닉스-도메인-소켓-주소-구조체">유닉스 도메인 소켓 주소 구조체</h3>
<pre><code class="language-c">struct sockaddr_un {
      __kernel_sa_family_t    sun_family;                   /* AF_UNIX */
      char s                  sun_path[UNIX_PATH_MAX];      /* 경로명 */
 };</code></pre>
<ul>
<li>sun_family : 소켓의 주소 체계, 패밀리 상수를 입력받는 변수</li>
<li>sun_path : 소켓 파일의 경로명, 이 경로는 파일 시스템 내에 위치하고, 프로세스 간 서로의 식별이 이루어진다.</li>
</ul>
<h3 id="인터넷-소켓-주소-구조체">인터넷 소켓 주소 구조체</h3>
<pre><code class="language-c">struct sockaddr_in {
    __kernel_sa_family_t    sin_family;                /* 주소 패밀리명 */
    __be16                  sin_port;                  /* 포트 번호 */
     struct in_addr          sin_addr;                  /* 인터넷 주소 */
 };

 struct in_addr {
     __be32 s_addr; /* 32비트 주소 */
 };</code></pre>
<ul>
<li>sin_family : 주소 체계 지정</li>
<li>sin_port : 포트 번호 지정. 네트워크 바이트 오더(빅엔디안)로 지정해야 하기 때문에 <code>htons()</code> 를 사용해 변환해주어야 한다.</li>
<li>sin_addr : IP 주소 지정(IP주소 표현을 <code>in_addr</code> 구조체로 함)</li>
</ul>
<p>ex)</p>
<pre><code class="language-c">struct sockaddr_in addr; // IPv4 소켓 주소 구조체
memset(&amp;addr, 0, sizeof(addr)); // 구조체를 전부 0으로 초기화
addr.sin_family = AF_INET; // 주소체계 지정
addr.sin_addr.s_addr = inet_addr(&quot;192.168.147.129&quot;); // IP주소 지정.
// inet_addr : 문자열 IP주소를 이진값으로 바꿔서 리턴
addr.sin_port = htons(9000); // 9000번 포트</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[C언어 TCP/IP 소켓 통신]]></title>
            <link>https://velog.io/@dong_kyu-lee/C%EC%96%B8%EC%96%B4-TCPIP-%EC%86%8C%EC%BC%93-%ED%86%B5%EC%8B%A0</link>
            <guid>https://velog.io/@dong_kyu-lee/C%EC%96%B8%EC%96%B4-TCPIP-%EC%86%8C%EC%BC%93-%ED%86%B5%EC%8B%A0</guid>
            <pubDate>Thu, 26 Jun 2025 10:34:30 GMT</pubDate>
            <description><![CDATA[<p><em>이번 학기 네트워크 프로그래밍에서는 유닉스, C언어, TCP/IP 환경에서 소켓 통신을 공부했다.
공부한 내용을 복기해보며 정리한 내용이다.</em></p>
<h2 id="tcpip">TCP/IP</h2>
<h3 id="tcpip-프로토콜">TCP/IP 프로토콜</h3>
<ul>
<li>패킷 통신 방식의 인터넷 프로토콜인 IP와 전송 조절 프로토콜인 TCP를 기반으로 사용하는 인터넷 통신 프로토콜들을 의미한다.</li>
<li>운영체제의 일부로 구현되며, 응용 프로그램은 운영체제가 제공하는 TCP/IP 프로토콜을 통해 통신을 수행한다.</li>
</ul>
<h4 id="계층-구조">계층 구조</h4>
<p><img src="https://velog.velcdn.com/images/dong_kyu-lee/post/b788daf0-bcd0-4c01-b052-ea624c49cc83/image.jpg" alt=""></p>
<h4 id="각-계층에서의-역할">각 계층에서의 역할</h4>
<ul>
<li>네트워크 엑세스 계층<ul>
<li>물리적 네트워크를 통한 데이터 송수신</li>
<li>이더넷 48bit 물리주소 사용</li>
</ul>
</li>
<li>인터넷 계층<ul>
<li>네트워크 엑세스 계층의 도움을 받아 목적지 호스트까지 데이터를 전송</li>
<li>IP주소 사용 (소프트웨어적으로 정의된 논리주소)</li>
<li>라우팅(데이터를 목적지까지 전달하는 일련 작업, 라우팅 정보를 기초로 데이터 전달)</li>
</ul>
</li>
<li>전송 계층<ul>
<li>최종 통신 목적지(응용 프로그램)을 지정하고 데이터 손실,손상 등의 전송 오류 없이 데이터를 전송하는 역할</li>
<li>포트 번호 사용</li>
<li>TCP(연결형, 신뢰할 수 있는 통신, 일대일 통신, 데이터 경계 구분 안 함(바이트 스트림))</li>
<li>UDP(비연결형, 신뢰할 수 없음, 일대일/일대다 통신, 데이터 경계 구분 함(데이터그램))</li>
</ul>
</li>
</ul>
<hr>
<h2 id="인터넷에서-데이터-전송-원리">인터넷에서 데이터 전송 원리</h2>
<h3 id="패킷packet">패킷(Packet)</h3>
<ul>
<li>크게 각 프로토콜에서 정의한 제어 정보(IP 주소, 포트번호, 오류 검출 코드 등) + 데이터로 나뉜다.</li>
<li>제어 정보는 패킷의 앞에 붙는 패킷 헤더(header)와 뒤쪽에 붙는 트레일러(Trailer)로 구분된다.</li>
</ul>
<h3 id="패킷-송신수신">패킷 송신/수신</h3>
<ul>
<li>송신측은 TCP/IP 계층의 응용 계층부터 네트워크 엑세스 계층까지 내려가면서 각 계층의 프로토콜에 필요한 헤더나 트레일러를 덭붙인다.(캡슐화)</li>
<li>수신측은 반대로 낮은 계층부터 상위 계층까지 올라가면서 각 계층의 헤더/트레일러를 처리하고 상위 계층으로 전달한다.(역캡슐화)</li>
</ul>
<p>송신측:<img src="https://velog.velcdn.com/images/dong_kyu-lee/post/80fef96e-291d-490d-be3b-5488f7337b83/image.png" alt=""></p>
<p>수신측:<img src="https://velog.velcdn.com/images/dong_kyu-lee/post/70c1099c-e635-4382-bcc6-e15f4e72d849/image.png" alt=""></p>
<hr>
<h2 id="소켓socket">소켓(Socket)</h2>
<ul>
<li>소켓은 인터넷 상에서 두 프로그램(클라이언트, 서버)이 데이터를 송수신하기 위한 통신 종단점이다.</li>
</ul>
<h3 id="데이터-타입-관점">데이터 타입 관점</h3>
<ul>
<li>소켓은 데이터 타입 관점에서 파일 디스크립터나 핸들과 유사한 개념이라 할 수 있다.</li>
<li>생성과 설정을 하고 난 후에 운영체제의 통신 관련 정보를 참조해 다양한 통신 기능을 편리하게 사용할 수 있는 데이터 타입이다.</li>
</ul>
<pre><code class="language-c">// 리눅스 파일 입출력
int fd = open(&quot;myfile&quot;, ...); // 파일 생성
read(fd, ...); // 데이터 읽기
write(fd, ...); // 데이터 쓰기</code></pre>
<pre><code class="language-c">// 윈도우 소켓
SOCKET sock = socket(...); // 소켓 생성
recv(sock, ...); // 데이터 읽기
send(sock, ..._); // 데이터 전송</code></pre>
<h3 id="네트워크-프로그래밍-인터페이스-관점">네트워크 프로그래밍 인터페이스 관점</h3>
<ul>
<li>소켓은 응용 계층과 네트워크 계층(전송 계층, 인터넷 계층, 네트워크 엑세스 계층) 사이에 위치하는 것으로 간주된다.</li>
<li>응용 프로그램은 네트워크 프르토콜의 세부사항을 몰라도 소켓이 제공하는 기능(send(), recv() 등)들을 사용하여 간단하게 네트워크 프로토콜들을 거쳐 상대방과 통신할 수 있다.
<img src="https://velog.velcdn.com/images/dong_kyu-lee/post/a78efbad-4ada-45c2-9acf-604e630defc1/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] Type Traits]]></title>
            <link>https://velog.io/@dong_kyu-lee/C-Type-Traits</link>
            <guid>https://velog.io/@dong_kyu-lee/C-Type-Traits</guid>
            <pubDate>Wed, 16 Oct 2024 07:20:00 GMT</pubDate>
            <description><![CDATA[<h1 id="type-traits">Type Traits</h1>
<p>C++에서는 Type Traits라는 라이브러리를 사용할 수 있다.</p>
<p>Type Traits은 타입의 속성을 컴파일 타임에 검사하거나 수정하는 기능을 가진 라이브러리를 말한다.</p>
<p>템플릿을 사용해 컴파일 타임에 타입을 검사 혹은 수정하기 때문에</p>
<ol>
<li>타입에 따라 다른 동작을 수행하도록 할 수 있다.</li>
<li>잘못된 타입 사용으로 발생하는 오류를 런타임 이전에 잡을 수 있다.</li>
</ol>
<h3 id="🟡예시">🟡예시</h3>
<p>정수 타입만을 받아 나눗셈을 수행하는 함수를 구현한다고 했을 때, 다음과 같이 템플릿 특수화를 이용할 수도 있지만...</p>
<pre><code class="language-cpp">template&lt;typename T&gt;
T integral_div(T a, T b) {
    return a / b;
}

// int 특수화
template&lt;&gt;
int integral_div&lt;int&gt;(int a, int b) {
    return a / b;
}
// char 특수화
template&lt;&gt;
char integral_div&lt;char&gt;(char a, char b) {
    return a / b;
}

// 다른 정수형 타입에 대해 전부 특수화 진행</code></pre>
<p>다음과 같이 type traits 라이브러리를 사용해 정수 타입인지를 컴파일 타임에 검사할 수도 있다.</p>
<pre><code class="language-cpp"># include &lt;type_traits&gt; // &lt;-- std type traits library

template&lt;typename T&gt;
T integral_div(T a, T b) {
    static_assert(std::is_integral&lt;T&gt;::value,
        &quot;integral_div accepts only integral types&quot;);
    return a / b;
}</code></pre>
<h3 id="🟡-is_integralt-와-is_integralt_v">🟡 <code>is_integral&lt;T&gt;</code> 와 <code>is_integral&lt;T&gt;_v</code></h3>
<p>위 예시에서 <code>is_integral&lt;T&gt;</code>는 <code>static constexpr</code> 형의 <code>value</code>값을 가진 구조체이다.</p>
<p>정수형 타입이라면 <code>value</code>는 true를, 그렇지 않다면 false 값을 가진다.</p>
<p><code>is_integral&lt;T&gt;</code>외에도 이와 비슷해보이는 <code>is_integral&lt;T&gt;_v</code>가 있다.</p>
<p><code>is_integral&lt;T&gt;_v</code>는 <code>is_integral&lt;T&gt;::value</code>를 편하게 쓰기 위한 헬퍼 변수이다.
C++17부터 사용 가능하며 다음과 같이 나타낼 수 있다.</p>
<pre><code class="language-cpp">template&lt; class T &gt;
constexpr bool is_integral_v = is_integral&lt;T&gt;::value;</code></pre>
<h2 id="타입-검사에-관한-type-traits-기능들">타입 검사에 관한 Type Traits 기능들</h2>
<p>🟡다음과 같은 타입 검사 기능들이 있다.</p>
<pre><code class="language-cpp">is_floating_point // 부동소수점형(float, double)
is_arithmetic     // 산술타입(정수형 + 부동소수점형)
is_signed         // signed 타입
is_unsigned       // unsigned 타입 
is_enum           // enum 타입(enum, enum class)
is_void           // void 타입
is_pointer        // 포인터 타입(T*)
is_null_pointer   // nullptr (C++)</code></pre>
<p>🟡참조, 함수, 배열에 대한 타입 검사</p>
<pre><code class="language-cpp">is_reference // 참조(T&amp;)
is_array     // 배열인지 검사
is_function  // 함수 타입</code></pre>
<pre><code class="language-cpp">void foo(int a, int b) {}

int a = 10;
int&amp; b = a;
cout &lt;&lt; is_reference&lt;decltype(b)&gt;::value; // true
cout &lt;&lt; is_array&lt;int[]&gt;::value; // true
cout &lt;&lt; is_function&lt;decltype(foo)&gt;::value; // true</code></pre>
<p>🟡클래스에 대한 검사</p>
<pre><code class="language-cpp">is_class // 클래스인지 검사(struct, class)
is_abstract // 추상 클래스인지 검사(적어도 하나의 순수가상함수가 포함되는지)
is_polymorphic // 다형성 클래스인지 검사(적어도 하나의 가상함수가 포함되는지)</code></pre>
<pre><code class="language-cpp">struct A{};
class B {
    virtual void foo() {}
};
class C {
    virtual void bar() = 0;
};

cout &lt;&lt; is_class&lt;A&gt;::value; // true
cout &lt;&lt; is_polymorphic&lt;B&gt;::value; // true
cout &lt;&lt; is_abstract&lt;C&gt;::value; // true</code></pre>
<p>🟡타입의 속성 검사</p>
<pre><code class="language-cpp">is_const // 타입이 `const` 타입인지 검사</code></pre>
<p>주의</p>
<pre><code class="language-cpp">template&lt;typename T&gt;
void f(T x) { cout &lt;&lt; std::is_const_v&lt;T&gt;; }

template&lt;typename T&gt;
void g(T&amp; x) { cout &lt;&lt; std::is_const_v&lt;T&gt;; }

template&lt;typename T&gt;
void h(T&amp; x) {
    cout &lt;&lt; std::is_const_v&lt;T&gt;;
    x = nullptr;
}

int main() {
    const int a = 3;
    f(a); // false. const가 값전달 되면서 사라짐!
    g(a); // true
    const int* b = new int;
    h(b); // false. T: (const int)*
}</code></pre>
<p>🟡타입 간의 관계 검사</p>
<pre><code class="language-cpp">is_same&lt;T, R&gt; // T와 R이 같은 타입인지 검사
is_base_of&lt;T, R&gt; // T가 R의 베이스 클래스인지 검사
is_convertible&lt;T, R&gt; // T가 R로 형변환 될 수 있는지 검사</code></pre>
<pre><code class="language-cpp">class B {};
class C : B {};

cout &lt;&lt; is_same&lt;int, unsigned int&gt;::value; // false
cout &lt;&lt; is_base_of&lt;B, C&gt;::value; // true
cout &lt;&lt; is_convertible&lt;int, float&gt;::value; // true</code></pre>
<h2 id="타입-조작에-관한-type-traits-기능들">타입 조작에 관한 Type Traits 기능들</h2>
<p>타입 검사에서는 <code>value</code> 값을 통해 검사를 확인할 수 있었다.</p>
<p>타입 조작(Type Manipulation)에서는 <code>type</code>필드를 통해 어떤 타입을 다른 타입으로 조작할 수 있다.</p>
<p><code>is_integral&lt;T&gt;::value == is_integral_v&lt;T&gt;</code>와 비슷하게, 타입 조작에서도 <code>make_unsigned&lt;T&gt;::type == make_unsigned_t&lt;T&gt;</code>로 사용할 수 있다.</p>
<p>🟡기능 종류들</p>
<pre><code class="language-cpp">// signed, unsigned 조작
make_signed // signed 타입으로 조작
make_unsigned // unsigned 타입으로 조작

// 포인터와 레퍼런스 조작
remove_pointer // 포인터를 제거(T* -&gt; T)
remove_reference // 참조를 제거(T&amp; -&gt; T)
add_pointer // 포인터로 변경(T -&gt; T*)
add_reference // 참조로 변경(T -&gt; T&amp;)

// const 조작
remove_const // const 제거(const T -&gt; T)
add_const // const로 변경

// 이외의 타입 조작
common_type&lt;T, R&gt; // T와 R의 공통인 타입을 반환. ex. &lt;int, short&gt; --&gt; int
conditional&lt;pred, T, R&gt; // pred 식이 true이면 T, false이면 R을 반환
decay&lt;T&gt; // 함수 인자에 값으로 전달될 때와 같은 타입을 리턴한다.</code></pre>
<p>🟡예시</p>
<pre><code class="language-cpp">// 배열의 포인터를 지우고 해당 타입의 값으로 배열 첫번째 인덱스를 가져옴.
template&lt;typename T&gt;
void f(T ptr) {
    using R = remove_pointer_t&lt;T&gt;;
    R x = ptr[0];
    cout &lt;&lt; x;
}

// 전달된 인수의 타입에 해당하는 const 타입 변수 생성
template&lt;typename T&gt;
void g(T x) {
    using R = add_const_t&lt;T&gt;;
    R y = 3;
    // y = 4; // 변경할 수 없음
}

// 두 값을 더한 후 T와 R 중 공통인 타입으로 반환
template&lt;typename T, typename R&gt;
common_type_t&lt;T, R&gt; add(T x, R y) {
    return x + y;
}

// T와 R 중 타입 크기가 더 큰 쪽으로 형변환하여 더한 값을 반환
template&lt;typename T, typename R&gt;
auto h(T a, R b) {
    constexpr bool pred = sizeof(T) &gt; sizeof(R);
    using S = std::conditional_t&lt;pred, T, R&gt;;
    return static_cast&lt;S&gt;(a) + static_cast&lt;S&gt;(b);
}

int main()
{
    auto x = h(2, &#39;a&#39;);
    cout &lt;&lt; x &lt;&lt; &quot;: &quot; &lt;&lt; typeid(x).name() &lt;&lt; &quot;\n&quot;; // print &quot;99: int&quot;
    auto y = h(2, 2ull);
    cout &lt;&lt; y &lt;&lt; &quot;: &quot; &lt;&lt; typeid(y).name() &lt;&lt; &quot;\n&quot;; // print &quot;4 : unsigned __int64&quot; 즉 unsigned long long
    auto z = h(2.0f, 2ull); 
    cout &lt;&lt; z &lt;&lt; &quot;: &quot; &lt;&lt; typeid(z).name() &lt;&lt; &quot;\n&quot;; // print &quot;4 : unsigned __int64&quot; 즉 unsigned long long

    char a[] = &quot;abc&quot;;
    f(a); // print &#39;a&#39;
    g(2);

    using res_t = decltype(add(2, 3.0f));
    res_t b = add(2, 3.0f);
    cout &lt;&lt; b &lt;&lt; &quot;: &quot; &lt;&lt; typeid(b).name(); // print &quot;5: float&quot;
}</code></pre>
<p>.
.
.
C++ 공부를 위해 작성된 글입니다. 오류가 있다면 지적해 주시면 감사하겠습니다.</p>
<p>참고자료
<a href="https://en.cppreference.com/w/cpp/types/is_integral">https://en.cppreference.com/w/cpp/types/is_integral</a>
<a href="https://github.com/federico-busato/Modern-CPP-Programming">https://github.com/federico-busato/Modern-CPP-Programming</a> (10.Templates_I)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] 함수 템플릿]]></title>
            <link>https://velog.io/@dong_kyu-lee/C-%ED%95%A8%EC%88%98-%ED%85%9C%ED%94%8C%EB%A6%BF</link>
            <guid>https://velog.io/@dong_kyu-lee/C-%ED%95%A8%EC%88%98-%ED%85%9C%ED%94%8C%EB%A6%BF</guid>
            <pubDate>Tue, 15 Oct 2024 06:12:03 GMT</pubDate>
            <description><![CDATA[<h1 id="함수-템플릿function-template">함수 템플릿(Function Template)</h1>
<p>함수 템플릿은 템플릿을 사용한 함수로, 서로 다른 타입의 변수를 인자로 받더라도 동일한 함수의 기능을 수행하게 할 수 있다.</p>
<pre><code class="language-cpp">template&lt;typename T&gt; // or template&lt;class T&gt;
T add(T a, T b) {
    return a + b;
}

int c1 = add(3, 4); // c1 = 7
float c2 = add(3.0f, 4.0f); // c2 = 7.0f</code></pre>
<p><code>template&lt;typename T&gt;</code> 와 같은 형식으로 함수 위에 작성하여 템플릿 함수를 나타낼 수 있다.</p>
<p>위 예시처럼 int나 float 파라미터를 받는 함수를 각각 오버로딩하지 않아도 템플릿 파라미터 <code>T</code>에 다른 타입을 넣음으로써 같은 기능을 수행하는 것을 볼 수 있다.</p>
<h2 id="템플릿-인스턴스화template-instantiation">템플릿 인스턴스화(Template Instantiation)</h2>
<p>함수 템플릿의 파라미터에 다른 타입들을 넣어도 함수가 같은 기능을 수행할 수 있는 이유는 무엇일까?</p>
<p>그 이유는 컴파일러가 템플릿 인스턴스화를 통해 각 타입에 맞는 함수를 생성하기 때문이다.
즉, 함수 템플릿은 일반화된 함수의 틀이고, 실제로 호출되는 함수는 컴파일 타임에 생성되는 인스턴스화된 함수이다.</p>
<pre><code class="language-cpp">template&lt;typename T&gt;
T add(T a, T b) {
    return a + b;
}

add(3, 4); // int형 함수 구현 생성. int add(int, int)
add(3.0f, 4.0f); // float형 함수 구현 생성. float add(float, float)
add(2, 6); // 이미 생성됨.</code></pre>
<p>템플릿 인스턴스화는 암시적 인스턴스화와 명시적 인스턴스화로 나뉜다.</p>
<h3 id="🟡암시적-인스턴스화">🟡암시적 인스턴스화</h3>
<p>암시적 인스턴스화는 컴파일러가 추론한 타입이나 명시적 템플릿 인자에 따른 코드를 생성하는 형태의 인스턴스화이다.</p>
<ul>
<li>오직 함수 정의를 사용할 때에만 생성된다!</li>
</ul>
<pre><code class="language-cpp">template&lt;typename T&gt;
void f(T a) {}

void g() {
    f(3); // void f(int) 생성 : 암시적 템플릿 인스턴스화
    f&lt;short&gt;(3.0); // void f(short) 생성 : 암시적 템플릿 인스턴스화
}

// void f(float) 같이 사용하지 않은 타입의 인스턴스화는 생성되지 않는다.</code></pre>
<h3 id="🟡명시적-인스턴스화">🟡명시적 인스턴스화</h3>
<p>명시적 인스턴스화는 사용자가 직접 인스턴스화할 함수를 선언해주는 것이다.</p>
<ul>
<li>특정 타입에 대한 인스턴스를 미리 생성하여 컴파일 시간을 줄이고 코드 크기를 최적화 할 수 있다.</li>
</ul>
<pre><code class="language-cpp">template &lt;typename T&gt;
void f(T a) { cout &lt;&lt; a &lt;&lt; &quot;\n&quot;; }

template void f&lt;int&gt;(int); // void f(int) 생성 : 명시적 템플릿 인스턴스화</code></pre>
<h2 id="함수-템플릿-오버로딩">함수 템플릿 오버로딩</h2>
<p>일반적인 함수와 마찬가지로 함수 템플릿 또한 오버로딩을 할 수 있다.
또한, 템플릿 파라미터도 함수 시그니처의 하나이기 때문에 오버로딩이 가능하다.</p>
<pre><code class="language-cpp">template&lt;typename T&gt;
T add(T a, T b) {
    return a + b;
}

template&lt;typename T&gt; // 함수 파라미터를 오버로딩
T add(T a, T b, T c) {
    return a + b + c;
}

template&lt;int C, typename T&gt; // 템플릿 파라미터를 오버로딩
T add(T a, T b) {
    return a + b + C;
}</code></pre>
<h2 id="함수-템플릿-특수화template-specialization">함수 템플릿 특수화(Template Specialization)</h2>
<p>템플릿 특수화는 특정한 조합의 템플릿(혹은 특정 타입)에 대한 구현을 따로 만들어 놓는 기능이다.</p>
<ul>
<li>부동소수점 반올림 오류 같은 특정 타입을 사용했을 때 발생할 수 있는 문제를 구현을 따로 만들어 놓아 방지할 수 있다.</li>
</ul>
<pre><code class="language-cpp">template&lt;typename T&gt;
T comp(T a, T b) {
    return a == b;
}

template&lt;&gt;
float comp(float a, float b) {
    return std::abs(a - b) &lt; 1e-9;
}

cout &lt;&lt; comp&lt;int&gt;(1, 1); // &quot;true&quot;
cout &lt;&lt; comp&lt;float&gt;(10.0000001f, 10.0f); // &quot;true&quot;</code></pre>
<h2 id="템플릿-파라미터template-parameter">템플릿 파라미터(Template Parameter)</h2>
<p>✅ 템플릿 파라미터는 기본값을 가질 수 있다.</p>
<pre><code class="language-cpp">template&lt;typename T = int&gt;
void f() { cout &lt;&lt; typeid(T).name(); }

template&lt;int A = 3, int B = 4&gt;
void print1() { cout &lt;&lt; A &lt;&lt; &quot;, &quot; &lt;&lt; B; }

template&lt;int A = 3, int B&gt; // 가능은 하지만 A에 기본값을 주는게 의미가 없음.
void print2() { cout &lt;&lt; A &lt;&lt; &quot;, &quot; &lt;&lt; B; }

f(); // &#39;int&#39;
f&lt;float&gt;(); // &#39;float&#39;

print1(); // print `3, 4`
print1&lt;2&gt;(); // print &#39;2, 4&#39;
print1&lt;1, 2&gt;(); // print &#39;1, 2&#39;

print2&lt;2, 5&gt;(); // print &#39;2, 5&#39;
// print2&lt;2&gt;(); // error
// print2(); // error</code></pre>
<p>✅ 템플릿 파라미터의 이름이 없을 수도 있다.</p>
<ul>
<li>이런 경우 템플릿 함수는 함수가 사용되는 경우에만 코드를 생성한다.</li>
</ul>
<pre><code class="language-cpp">void f() {} // 일반 함수. 코드가 있음.

template&lt;typename = void&gt;
void g() {} // g()를 호출하지 않는 이상 코드가 생성되지 않음.

int main(){
    g(); // generated
}</code></pre>
<p>✅ 템플릿 파라미터는 앞서 선언된 템플릿 파라미터를 사용해 초기화 할 수 있다.</p>
<pre><code class="language-cpp">template&lt;int A, int B = A + 3&gt;
void f() {
    cout &lt;&lt; B;
}
template&lt;typename T, int S = sizeof(T)&gt;
void g(T) {
    cout &lt;&lt; S;
}

f&lt;3&gt;(); // B = 6
g(3); // S = 4</code></pre>
<h2 id="함수-템플릿의-장단점">함수 템플릿의 장단점</h2>
<h3 id="⭕장점">⭕장점</h3>
<ol>
<li>일반화 프로그래밍의 장점과 같다. 코드 재사용성과 유연성을 높여준다.</li>
<li>타입 계산이 컴파일 타임에 진행되어서 런타임 속도가 빠르다.</li>
</ol>
<h3 id="⭕단점">⭕단점</h3>
<ol>
<li>코드를 읽기가 어려워진다.</li>
<li>템플릿은 모든 구분된 파라미터를 암시적으로 인스턴스화하기 때문에 컴파일 시간이 더 걸리고, 바이너리 크기를 키운다.</li>
</ol>
<p>.
.
.
C++ 공부를 위해 작성된 글입니다. 오류가 있다면 지적해 주시면 감사하겠습니다.</p>
<p>참고자료
<a href="https://github.com/federico-busato/Modern-CPP-Programming">https://github.com/federico-busato/Modern-CPP-Programming</a> (10. Templates_I)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] 람다 표현식]]></title>
            <link>https://velog.io/@dong_kyu-lee/C-%EB%9E%8C%EB%8B%A4-%ED%91%9C%ED%98%84%EC%8B%9D</link>
            <guid>https://velog.io/@dong_kyu-lee/C-%EB%9E%8C%EB%8B%A4-%ED%91%9C%ED%98%84%EC%8B%9D</guid>
            <pubDate>Wed, 11 Sep 2024 06:25:10 GMT</pubDate>
            <description><![CDATA[<hr>
<h3 id="람다-표현식lambda-expression">람다 표현식(Lambda expression)</h3>
<hr>
<p>C++의 기능 중에는 <strong>람다 표현식</strong>이라는 것이 있다.</p>
<p>C++ 레퍼런스의 설명에는 다음과 같이 적혀있다.</p>
<blockquote>
<p><em>&quot;클로져(closure)를 생성하는 범위 내의 변수를 캡쳐할 수 있는 이름 없는 함수객체&quot;</em></p>
</blockquote>
<p>클로져는 뭐고, 캡쳐한다는 것은 또 무엇일까?
이에 대해 공부해 보았다.</p>
<hr>
<h4 id="람다의-형태">람다의 형태</h4>
<p>람다는 다음과 같은 표현식 형태를 가진다.</p>
<pre><code class="language-cpp">1. auto x = [capture clause] (parameter) {body}
2. auto x = [capture clause] (parameter)-&gt;return type {body}</code></pre>
<blockquote>
</blockquote>
<ul>
<li>capture clause : 람다 표현식임을 정의하고, 람다가 속한 지역 범위 내의 변수를 어떻게 사용할 지 정의한다.</li>
<li>parameter : 함수의 parameter와 같은 기능이다.</li>
<li>return type : 함수의 반환 값과 같은 기능이다.</li>
<li>body : 함수의 구현부와 같은 기능이다.</li>
</ul>
<p>아래와 같이 람다 표현식을 사용할 수 있다.</p>
<pre><code class="language-cpp">auto lambda = []() {std::cout &lt;&lt; &quot;hello&quot; &lt;&lt; &quot;\n&quot;; };
lambda();
// print &quot;hello&quot;</code></pre>
<hr>
<h4 id="캡쳐capture">캡쳐(Capture)</h4>
<p>람다 표현식에서 캡쳐란, 람다 표현식이 속한 지역 범위의 변수들을 값으로 가져오거나 참조하여 람다 표현식 내에서 접근 가능하게 하는 방법을 의미한다.</p>
<p>캡쳐하지 않은 외부 변수들은 일부를 제외하고는 람다 표현식에서 사용할 수 없다.</p>
<p>람다의 캡쳐에는 이러한 방법들이 있다.
<code>[]</code>안에 적은 캡쳐할 내용들을 캡쳐 리스트(Capture List)라고 한다.</p>
<blockquote>
</blockquote>
<p><code>[]</code> : 캡쳐하지 않음. 즉, 외부 변수를 사용하지 않음.
<code>[=]</code> : 모든 변수들을 값으로 캡쳐함. (<code>const</code>로 가져오게 된다.)
<code>[&amp;]</code> : 모든 변수들을 참조로 캡쳐함.
<code>[val]</code> : 범위 내의 변수 <code>val</code>을 값으로 캡쳐함.(<code>val</code>만 캡쳐)
<code>[&amp;val]</code> : 범위 내의 변수 <code>val</code>을 참조로 캡쳐함.(<code>val</code>만 캡쳐)
<code>[=, &amp;val]</code> : 모든 변수를 값으로 캡쳐함, <code>val</code>변수만 참조로 캡쳐
<code>[val1, &amp;val2]</code> : 각 변수를 각 방식대로 캡쳐함.
<strong><em>클래스에서의 캡쳐 리스트는 아래에서 설명한다.</em></strong></p>
<p>추가로, 캡쳐를 하지 않고도 람다 표현식에서 쓰거나 읽을 수 있는 변수들이 있다.</p>
<blockquote>
</blockquote>
<p>쓰기 가능</p>
<ul>
<li>전역 변수,  <code>static</code>변수, thread local 저장기간인 변수</li>
<li>상수 표현식(constant expression)으로 선언된 참조 변수<blockquote>
</blockquote>
읽기 가능</li>
<li><code>const</code>인 정수형 혹은 열거형 변수</li>
<li>상수 표현식으로 선언된 변수</li>
<li><code>mutable</code> 멤버가 없는 <code>constexpr</code> 변수</li>
</ul>
<p>캡쳐한 예시를 살펴보자.</p>
<pre><code class="language-cpp">size_t st = 1;
std::vector&lt;int&gt; v{ 1,2,3 };

int main() {
    int val1 = 10;
    const int cVal = 2;
    constexpr char val2 = &#39;m&#39;;
    std::string s = &quot;hello&quot;;

    // auto lambda1 = [=](int a) { val1 = a; }; // 컴파일 에러 : 값 수정 불가능
    auto lambda1 = [&amp;s]() {s[0] = val2; std::cout &lt;&lt; s &lt;&lt; &quot;\n&quot;; };  // 캡쳐 없이 val2 사용
    auto lambda2 = []() {st = v.size(); std::cout &lt;&lt; st &lt;&lt; &quot;\n&quot;; }; // 캡쳐 없이 st, v 사용
    lambda1();    // print &quot;mello&quot;
    lambda2();    // print &quot;3&quot;
}</code></pre>
<hr>
<h4 id="클로져closure">클로져(Closure)</h4>
<p>클로져는 람다 표현식의 런타임 결과로써 나오는 임시객체이다.</p>
<p>우리가 작성한 람다 표현식은 표현식일 뿐, 런타임에는 존재하지 않는다.
대신 컴파일 시간에 람다 표현식은 고유한 클래스를 만들어내고,
런타임에 그 클래스 타입의 객체(클로져)가 생성되는 것이다.</p>
<pre><code class="language-cpp">auto f = [](int a, int b) { return a &lt; b; };</code></pre>
<p>위와 같은 람다 표현식이 있을 때, 컴파일 타임에 해당 표현식이 평가되어 클로져가 생성된다.
여기서 <code>f</code>는 클로져가 아닌 클로져의 복사된 값을 갖는 변수가 된다.</p>
<hr>
<h4 id="람다-표현식는-어디에-쓸까">람다 표현식는 어디에 쓸까?</h4>
<blockquote>
</blockquote>
<ul>
<li>람다 식은 함수에 인자로 전달될 때 쓰일 수 있다.</li>
<li>특정 지역 범위에서만 쓰일 함수를 사용할 때 전통적인 함수 대신 사용하기 좋은 기능이다.</li>
</ul>
<p>아래 코드는 분기에 따라 오름차순 혹은 내림차순으로 배열을 정렬하는 코드이다.</p>
<pre><code class="language-cpp">int main() 
{
    bool flag = false;
    int arr[5]{ 10, -2, 1, 7, -12 };

    if (flag)
        std::sort(arr, arr + 5, [](int a, int b) {return a &lt; b; });
    else 
        std::sort(arr, arr + 5, [](int a, int b) {return a &gt; b; });

    for (int i = 0; i &lt; 5; ++i) {
        std::cout &lt;&lt; arr[i] &lt;&lt; &quot;, &quot;;
    }
}</code></pre>
<hr>
<h4 id="람다-표현식과-클래스">람다 표현식과 클래스</h4>
<p>클래스에서 람다를 어떻게 사용하는지 살펴보자.</p>
<p>먼저 클래스 내에서 람다를 선언해 멤버변수에 접근하려면 위에서 설명한 <code>[멤버변수]</code> 이런 식으로는 불가능하다.
왜냐하면 멤버변수는 객체의 <code>this</code>포인터를 통해 접근해야 하기 때문이다.</p>
<p>클래스에서 사용하는 캡쳐 리스트들은 이런 것들이 있다.</p>
<blockquote>
</blockquote>
<p><code>[this]</code> : 현재 객체의 포인터(<code>*this</code>)를 참조로 가져와 멤버에 접근할 수 있게 해준다.
<code>[x=x]</code> : 현재 객체의 멤버변수 <code>x</code>를 값으로 가져온다.
<code>[&amp;x=x]</code> : 현재 객체의 멤버변수 <code>x</code>를 참조로 가져온다.
<code>[=]</code>를 통해 <code>this</code>포인터에 접근하는 행위는 C++20부터 권장되지 않는다.(컴파일 경고 발생)</p>
<p>예시)</p>
<pre><code class="language-cpp">struct A {
    int x;

    void foo() {
        auto lambda = [=]() { this-&gt;x = 1; }; // 컴파일러 경고(C++ 20부터)
        auto lambda1 = [x = x]() { return x; };
        auto lambda2 = [&amp;x = x]() {x = 2; };
        // auto lambda3 = [x]() {return x; }; // this가 캡쳐 목록에 없기에 x를 쓸 수 없음.
        auto lambda4 = [this]() {return x; }; 
        auto lambda5 = [*this]() {return x; };
    }
};</code></pre>
<p>.
.
.
C++ 공부를 위해 작성된 글입니다. 오류가 있다면 지적해 주시면 감사하겠습니다.</p>
<p>참고자료
<a href="https://github.com/federico-busato/Modern-CPP-Programming">https://github.com/federico-busato/Modern-CPP-Programming</a> (07.Basic_Concepts_V)
<a href="https://en.cppreference.com/w/cpp/language/lambda">https://en.cppreference.com/w/cpp/language/lambda</a>
<a href="https://lunchballer.com/archives/284">https://lunchballer.com/archives/284</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++]C 프로그램의 메모리 레이아웃]]></title>
            <link>https://velog.io/@dong_kyu-lee/CC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8%EC%9D%98-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83</link>
            <guid>https://velog.io/@dong_kyu-lee/CC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8%EC%9D%98-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83</guid>
            <pubDate>Thu, 05 Sep 2024 07:18:32 GMT</pubDate>
            <description><![CDATA[<p>C/C++에서 메모리에 올라가는 프로그램의 구조는 크게 스택, 힙, 데이터, 텍스트 영역으로 나뉜다. 이에 대해 정리한 내용이다.</p>
<h3 id="텍스트-영역">텍스트 영역</h3>
<p>프로그램의 실제 코드가 저장되는 영역이다.</p>
<ul>
<li>텍스트 영역은 주로 읽기 전용이고 공유 가능하다는 특징이 있다.</li>
<li>읽기 전용의 장점은 프로그램이 급작스럽게 수정되는 일이 발생하지 않도록 막는 것이다.</li>
<li>공유 가능하다는 것은 텍스트 편집기나 C 컴파일러, 셸 등이 프로그램 코드를 사용할 때 코드 사본을 따로 만들지 않고 텍스트 영역을 공유해 사용할 수 있다.</li>
<li>텍스트 영역은 메모리 영역에서 힙이나 스택 아래에 배치되어 힙/스택 오버플로우로 인한 영향을 피하도록 설계되기도 한다.</li>
</ul>
<h3 id="데이터-영역">데이터 영역</h3>
<p>전역 변수나 정적 변수가 저장되는 메모리 영역이다. 크게 데이터 영역과 BSS 영역으로 나뉜다.</p>
<ul>
<li><p>Data 영역은 초기화된 데이터 영역(Initialized Data Segment)이라고도 불리며, &quot;프로그래머가&quot; 초기화한 전역 변수, 정적 변수가 저장된다.</p>
</li>
<li><p>Data 영역은 &quot;초기화된 읽기 영역&quot;과 &quot;초기화된 읽기-쓰기 영역&quot;으로 나뉜다.</p>
<blockquote>
<p>예를 들어, 전역 변수로 선언한 <code>const char* str = &quot;hello world&quot;</code>라는 코드에서,
<code>&quot;hello world&quot;</code>라는 문자열 리터럴은 <code>const</code>로 선언되어 초기화된 읽기 영역에 저장된다.
포인터 변수 <code>str</code>은 읽기-쓰기 영역에 저장된다.</p>
</blockquote>
</li>
<li><p>BSS 영역은 Block Started by Symbol의 약자로, 초기화되지 않은 전역 변수나 정적 변수가 저장된다. 프로그램 실행 전에 커널에 의해 0으로 초기화된다.</p>
</li>
</ul>
<pre><code class="language-cpp">int x = 10; // Data segment memory
int y;      // BSS segment memory

int main(){
    int a = 10; // stack memory
}</code></pre>
<h3 id="스택-영역">스택 영역</h3>
<p>스택(Stack) 영역은 함수의 호출과 관계되어 있는 데이터들이 저장되는 영역이다.</p>
<ul>
<li>한 함수 호출에 대해 스택에 push된 값 집합을 &quot;스택 프레임(stack frame)&quot;이라고 한다.</li>
<li>스택 프레임에는 반환할 주소나 함수의 매개변수와 지역변수 등이 저장된다.</li>
<li>주로 높은 주소에서 낮은 주소로 할당이 이루어진다.</li>
</ul>
<h3 id="힙-영역">힙 영역</h3>
<p>힙(heap) 영역은 동적 할당이 이루어지는 영역이다.</p>
<ul>
<li>C++에서는 <code>new/new[]</code>키워드를 통해 동적 할당을 하고, <code>delete/delete[]</code> 키워드를 통해 해제한다.</li>
<li>일반적으로 낮은 주소에서 높은 주소로 할당이 이루어진다.</li>
</ul>
<h3 id="스택-힙-비교">스택, 힙 비교</h3>
<table>
<thead>
<tr>
<th></th>
<th>Stack</th>
<th>Heap</th>
</tr>
</thead>
<tbody><tr>
<td>메모리 구성</td>
<td>연속적임(LIFO)</td>
<td>할당 내에 연속적임. 할당 간의 단편화 발생(가상머신에 따라 차이)</td>
</tr>
<tr>
<td>최대 크기</td>
<td>리눅스(8MB), 윈도우(1MB)</td>
<td>메모리 시스템 전체</td>
</tr>
<tr>
<td>메모리 초과시</td>
<td>함수 호출 시 프로그램 크래시</td>
<td>예외 or nullptr</td>
</tr>
<tr>
<td>할당</td>
<td>컴파일타임</td>
<td>런타임</td>
</tr>
<tr>
<td>지역성</td>
<td>높음</td>
<td>낮음</td>
</tr>
<tr>
<td>스레드 관점</td>
<td>각 스레드가 스텍 공간을 각각 차지함</td>
<td>스레드끼리 공유함</td>
</tr>
<tr>
<td>.</td>
<td></td>
<td></td>
</tr>
<tr>
<td>.</td>
<td></td>
<td></td>
</tr>
<tr>
<td>.</td>
<td></td>
<td></td>
</tr>
<tr>
<td>C++ 공부를 위해 작성된 글입니다. 오류가 있다면 지적해 주시면 감사하겠습니다.</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<p>참고자료
<a href="https://github.com/federico-busato/Modern-CPP-Programming">https://github.com/federico-busato/Modern-CPP-Programming</a> (05.Basic_Concepts_IV)</p>
<p><a href="https://www.geeksforgeeks.org/memory-layout-of-c-program">https://www.geeksforgeeks.org/memory-layout-of-c-program</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++]Bit-Field와 union]]></title>
            <link>https://velog.io/@dong_kyu-lee/CBit-Field%EC%99%80-union</link>
            <guid>https://velog.io/@dong_kyu-lee/CBit-Field%EC%99%80-union</guid>
            <pubDate>Tue, 03 Sep 2024 06:59:17 GMT</pubDate>
            <description><![CDATA[<hr>
<h3 id="bit-field">Bit-Field</h3>
<hr>
<p>Bit-Field는 변수들이 사용할 메모리 공간을 비트 단위로 지정해 구조체(클레스)의 멤버변수로 저장하는 방식이다. 다음과 같이 ( : )를 사용해 나타낼 수 있다.</p>
<pre><code class="language-cpp">struct S {
int b1 : 10; // 10비트를 사용
int b2 : 10; // b2의 범위는 [0, 1023]
int b3 : 8;  // [0, 255]
};</code></pre>
<ul>
<li>bit-field에 이름 없는 변수를 사용해 새로운 바이트에서 값을 시작하도록 할 수 있다.<pre><code class="language-cpp">struct S {
  // 3 bits: b1에 할당
  // 5 bits: 사용되지 않음(다음 byte로 넘어감)
  // 2 bits: b2에 할당(새로운 byte)
  // 6 bits: 사용되지 않음
  unsigned char b1 : 3;
  unsigned char :0; // start a new byte
  unsigned char b2 : 2;
}; // sizeof(S) == 2</code></pre>
</li>
</ul>
<blockquote>
<p>특징</p>
</blockquote>
<ol>
<li><p>bit-field 구조체의 크기는 사용한 정수 타입의 크기를 기준으로 잡힌다. (<code>int</code>로 구성된 bit-field 구조체는 4byte 단위로 크기가 잡힘.)</p>
</li>
<li><p>bit-field에는 정수형 자료형만을 사용해야 한다.</p>
</li>
<li><p>멤버 변수에 할당하는 비트의 수는 자료형의 크기를 넘지 못한다. (<code>char</code>형 변수에 8bit를 초과하는 크기를 할당하면 컴파일 에러 발생.)</p>
</li>
</ol>
<hr>
<h3 id="union">Union</h3>
<hr>
<p><strong>공용체(Union)</strong>는 서로 다른 자료형들이 하나의 메모리 공간을 공유하도록 하는 키워드이다.</p>
<pre><code class="language-cpp">union A
{
    int x;
    char y;
}; // sizeof(A) : 4</code></pre>
<p>자료형들이 하나의 메모리 공간을 공유하기 때문에 멤버 변수들을 잘못 사용하면 예상치 못한 오류가 발생할 수 있다.</p>
<p>다음 예시를 보자.</p>
<pre><code class="language-cpp">union A
{
    int x;
    char y;
}; // sizeof(A) : 4

A a;
a.x = 1023;  // 32bit에서 00...00111111111
a.y = 0;     // 1byte(8bit) 값을 0으로 바꿈
             // =&gt; 00...00110000000
cout &lt;&lt; a.x; // 2^9 + 2^8 = 768 출력하는 오류가 발생.</code></pre>
<ol>
<li>위 예시처럼 <code>A</code>의 멤버 변수 <code>x</code>에 1023을 할당했다면, union의 크기 4byte(32bit) 중에서 4byte를 사용하여 1023을 할당한다.</li>
<li>1023은 이진법으로 111111111이다.</li>
<li>여기서 멤버변수 <code>y</code>를 0(1byte)으로 할당하게 되면 <code>x</code>에 값을 할당해 놓은 공간이 <code>y</code>를 위한 값 할당으로 덮이게 되는 것이다.</li>
<li><code>x</code>는 110000000이 된다.</li>
<li>결과적으로 <code>x</code>값은 768로 바뀌게 되는 상황이 발생한다.</li>
</ol>
<blockquote>
<p>특징</p>
</blockquote>
<ol>
<li><p><code>union</code>의 크기는 멤버 변수의 타입 중 가장 큰 타입의 크기로 정해진다.</p>
</li>
<li><p>아래 예시처럼 <code>union</code>은 이름 없이 정의하는 것이 가능하다.
 &quot;익명 공용체&quot;(Anonymous union)라고 하며, 전역or네임스페이스 범위에서 사용하려면 <code>static</code>을 사용해야 한다.</p>
<pre><code class="language-cpp">static A{
 char a;
 char b;
};
</code></pre>
</li>
</ol>
<p>struct S {
    union {
        int x;
        int y;
    };
};</p>
<p>S s;
s.x = 100;</p>
<pre><code>.
.
.
_C++을 공부를 위해 작성한 글입니다. 오류가 있다면 지적해주시면 감사겠습니다._

참고자료
https://github.com/federico-busato/Modern-CPP-Programming (04.Basic_Concepts_III)

https://en.cppreference.com/w/cpp/language/bit_field</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘]분할정복 (백준 10211번)]]></title>
            <link>https://velog.io/@dong_kyu-lee/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EB%B6%84%ED%95%A0%EC%A0%95%EB%B3%B5-%EB%B0%B1%EC%A4%80-10211%EB%B2%88</link>
            <guid>https://velog.io/@dong_kyu-lee/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EB%B6%84%ED%95%A0%EC%A0%95%EB%B3%B5-%EB%B0%B1%EC%A4%80-10211%EB%B2%88</guid>
            <pubDate>Tue, 30 Apr 2024 06:15:11 GMT</pubDate>
            <description><![CDATA[<h3 id="분할정복">분할정복</h3>
<hr>
<p><strong>분할정복(divide-and-conquer)</strong> 이란 문제를 더 작은 문제로 쪼개고 작은 문제에 대한 해를 찾고 결합하여 원 문제를 해결하는 알고리즘이다.</p>
<blockquote>
<p><strong>특징</strong></p>
</blockquote>
<ul>
<li>어떤 문제를 같은 유형의 더 작은 문제로 해결 할 수 있을 때 사용할 수 있는 알고리즘이다.</li>
<li>큰 문제에서 시작해 작은 문제로 내려가는 하향식 접근 방법(top-down)이다. (동적 프로그래밍은 상향식 접근 방법)</li>
<li>주로 재귀적으로 구현하고, 함수 호출로 인한 오버헤드가 발생한다는 단점이 있다.</li>
</ul>
<p>분할정복의 기본 알고리즘은 간단하게 아래처럼 나타낼 수 있다.</p>
<blockquote>
<p><strong>기본 알고리즘</strong></p>
</blockquote>
<ol>
<li>문제를 더 작은 문제로 분할한다.</li>
<li>작은 문제를 재귀적으로 해결한다.</li>
<li>작은 문제의 해결을 결합하여 큰 문제를 해결한다.</li>
</ol>
<h3 id="백준-10211번">백준 10211번</h3>
<hr>
<p>(<a href="https://www.acmicpc.net/problem/10211">백준 10211번 : Maximum Subarray</a>)</p>
<p>문제</p>
<ul>
<li>크기 N인 정수형 배열 X가 있을 때, X의 부분 배열(X의 연속한 일부분) 중 각 원소의 합이 가장 큰 부분 배열을 찾고 원소의 합을 출력해라.</li>
</ul>
<p>이 문제는 분할정복을 사용해 <code>O(NlogN)</code> 에 해결할 수 있다.</p>
<blockquote>
<p>아이디어</p>
</blockquote>
<ol>
<li>배열 X를 반으로 나누어 최대 부분 배열이 왼쪽에 있을 때, 오른쪽에 있을 때, 두 부분에 걸쳐있을 때. 총 3가지의 경우로 구분한다.</li>
<li>배열을 더 이상 나눌 수 없을 때까지 재귀적으로 쪼갠다.</li>
<li>위 3가지 경우를 계산하고 가장 큰 값을 리턴하면서 배열 X의 최대 부분 배열을 찾는다.</li>
</ol>
<p>전체 코드</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
using namespace std;

int split(vector&lt;int&gt;&amp; vec, int L, int R, int mid)
{
    int sum = 0;
    int lMax = -99999;
    int rMax = -99999;
    for (int i = mid; i &gt;= L; --i)
    {
        sum += vec[i];
        lMax = (sum &gt; lMax) ? sum : lMax;
    }
    sum = 0;
    for (int i = mid + 1; i &lt;= R; ++i)
    {
        sum += vec[i];
        rMax = (sum &gt; rMax) ? sum : rMax;
    }

    return lMax + rMax;
}

int solve(vector&lt;int&gt;&amp; vec, int s, int e)
{
    if (s == e) return vec[s];

    int mid = (s + e) / 2;
    int L = solve(vec, s, mid);
    int R = solve(vec, mid + 1, e);
    int M = split(vec, s, e, mid);

    return L &gt; R ? (L &gt; M ? L : M) : (R &gt; M ? R : M);
}

int main()
{
    int T = 0;
    cin &gt;&gt; T;
    for (int t = 0; t &lt; T; ++t)
    {
        int N = 0;
        cin &gt;&gt; N;
        vector&lt;int&gt; vec(N, 0);
        for (int i = 0; i &lt; N; ++i)
        {
            cin &gt;&gt; vec[i];
        }

        cout &lt;&lt; solve(vec, 0, N - 1) &lt;&lt; &quot;\n&quot;;
    }
}</code></pre>
<hr>
<h3 id="해설">해설</h3>
<pre><code class="language-cpp">int solve(vector&lt;int&gt;&amp; vec, int s, int e)
{
    if (s == e) return vec[s];

    int mid = (s + e) / 2;
    int L = solve(vec, s, mid);
    int R = solve(vec, mid + 1, e);
    int M = split(vec, s, e, mid);

    return L &gt; R ? (L &gt; M ? L : M) : (R &gt; M ? R : M);
}</code></pre>
<ol>
<li>위 함수는 전체 배열(<code>vec</code>)과 부분 배열의 시작(<code>s</code>)과 끝(<code>e</code>)을 인자로 받는다.</li>
<li>부분 배열을 왼쪽(<code>L</code>)과 오른쪽(<code>R</code>)을 재귀호출로 나눌 수 없을 때까지 쪼갠다.</li>
<li>더 이상 나눌 수 없다면(크기가 1인 부분배열) 현재 가리키는 원소를 리턴한다.</li>
<li>리턴받은 <code>L</code>과 <code>R</code>과 <code>M</code>을 비교하여 가장 큰 변수를 리턴한다.</li>
</ol>
<pre><code class="language-cpp">int split(vector&lt;int&gt;&amp; vec, int L, int R, int mid)
{
    int sum = 0;
    int lMax = -99999;
    int rMax = -99999;
    for (int i = mid; i &gt;= L; --i)
    {
        sum += vec[i];
        lMax = (sum &gt; lMax) ? sum : lMax;
    }
    sum = 0;
    for (int i = mid + 1; i &lt;= R; ++i)
    {
        sum += vec[i];
        rMax = (sum &gt; rMax) ? sum : rMax;
    }

    return lMax + rMax;
}</code></pre>
<ol start="5">
<li>여기서 <code>M</code>은 위  <code>split</code>함수를 통해 얻어지는데, 둘로 나눈 부분 배열의 <code>mid</code>를 기준으로 왼쪽으로/오른쪽으로 점진적으로 이동하며 최대값을 찾아낸다.</li>
<li><code>mid</code>기준 왼쪽 최댓값(<code>lMax</code>)과 오른쪽 최댓값(<code>rMax</code>)를 더해 최종적으로 겹친 부분의 최댓값을 리턴한다.</li>
<li>위의 과정으로 배열을 결합하고 마지막으로 배열<code>vec</code>의 <code>L</code>과 <code>R</code>, <code>M</code>을 비교하여 최대 부분 배열의 원소의 합을 도출해낼 수 있다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Unity-유니티에서 직렬화란?]]></title>
            <link>https://velog.io/@dong_kyu-lee/Unity-%EC%9C%A0%EB%8B%88%ED%8B%B0%EC%97%90%EC%84%9C-%EC%A7%81%EB%A0%AC%ED%99%94%EB%9E%80</link>
            <guid>https://velog.io/@dong_kyu-lee/Unity-%EC%9C%A0%EB%8B%88%ED%8B%B0%EC%97%90%EC%84%9C-%EC%A7%81%EB%A0%AC%ED%99%94%EB%9E%80</guid>
            <pubDate>Thu, 07 Sep 2023 07:29:21 GMT</pubDate>
            <description><![CDATA[<p>유니티로 개발하면서 게임에서 플레이어에게 필요한 데이터들을 앱을 종료하고 실행시켰을 때, 그대로 불러오는 기능을 구현해야 하는 경우가 자주 생깁니다.</p>
<p>이럴 때는 종종 저장해야 할 값이나 객체를 JSON 포멧으로 바꾸어 .json 파일 형식으로 저장하고 불러오는 기능을 사용합니다.</p>
<p>또한, 클래스의 private 멤버변수를 개발 할 때의 편의를 위해 인스펙터 창에서 볼 수 있도록 [SerializeField] 키워드를 이용하기도 합니다.</p>
<p>이런 기능들은 직렬화(Serialization)라는 과정을 통해 가능한 일들입니다.</p>
<hr>
<p><strong>직렬화(Serialization)</strong>란, 데이터 구조나 오브젝트를 컴퓨터 환경에 저장하고 나중에 재구성 할 수 있는 포멧으로 변환하는 과정을 의미합니다.</p>
<p>그렇다고 모든 데이터들을 다 직렬화 할 수 있는 것은 아니고, 직렬화 규칙이 있습니다.</p>
<p>다음 규칙을 지키는 데이터들을 직렬화 할 수 있습니다.</p>
<blockquote>
</blockquote>
<ol>
<li>public이거나 SerializeField 속성이 있어야 함</li>
<li>정적이 아님</li>
<li>상수가 아님</li>
<li>읽기 전용이 아님</li>
<li>다음과 같이 직렬화 가능한 필드 타입이 있어야 함</li>
</ol>
<ul>
<li>기본 데이터 형식(int, float, double, bool, string 등)</li>
<li>열거형 타입(32 바이트 이하)</li>
<li>고정 크기 버퍼</li>
<li>Unity 빌트인 타입(예: Vector2, Vector3, Rect, Matrix4x4, Color, AnimationCurve)</li>
<li>Serializable 속성이 있는 커스텀 구조체</li>
<li>UnityEngine.Object에서 파생된 오브젝트에 대한 레퍼런스</li>
<li>Serializable 속성이 있는 커스텀 클래스(커스텀 클래스 직렬화 참조)</li>
<li>위에서 언급한 필드 타입의 배열</li>
<li>위에서 언급한 필드 타입의 List&lt; T &gt;</li>
</ul>
<hr>
<p>예시로, 아래 클래스의 요소들처럼 직렬화 규칙을 만족하는 멤버들은 인스펙터 창에 직렬화되어 나타나는 것을 볼 수 있습니다.</p>
<pre><code class="language-cs">public class SerializeScript : MonoBehaviour
{
    public enum MyEnum { AAA, BBB, CCC }

    [System.Serializable]
    public class MyClass
    {
        public int intValue;
        public List&lt;int&gt; intListValue;
        public MyEnum myEnum;
    }

    public int publicValue;
    [SerializeField]
    private int serializedPrivateValue;
    static int staticValue;
    public const int constValue = 10;
    public readonly int readonlyValue = 10;
    public MyClass myClass = new MyClass();
}</code></pre>
<blockquote>
<p><img src="https://velog.velcdn.com/images/dong_kyu-lee/post/7506dcd2-da1f-4a28-9cb3-28e51aba7f7d/image.png" alt=""></p>
</blockquote>
<p>인스펙터는 오브젝트의 직렬화 조건을 갖춘 필드들을 보여주며, 인스펙터 창에서 값을 수정하면, 직렬화된 데이터를 업데이트하고 역직렬화를 수행하여 오브젝트에 적용됩니다.</p>
<hr>
<h2 id="json-직렬화">Json 직렬화</h2>
<p>Json을 유니티에서 직렬화 할 때는 JsonUtility 클래스를 사용할 수 있습니다.</p>
<p>JsonUtility는 유니티에서 지원하는 Json 데이터 처리 클래스입니다.</p>
<p>JsonUtility를 사용하여 직렬화 및 역직렬화 할 때는 다음의 조건이 필요합니다.</p>
<blockquote>
</blockquote>
<ol>
<li>직렬화하려는 데이터가 &quot;구조화&quot;된 데이터여야 한다. 즉, 저장하려는 변수를 클래스나 구조체로 표현해야 합니다.</li>
<li>Monobehavior 혹은 ScriptableObject를 상속받은 클래스이거나, [Serializable] 속성을 가진 클래스여야 합니다.</li>
<li>단순 변수나 배열은 직렬화하지 못하며, 클래스 혹은 구조체를 사용하여 나타내야 합니다.</li>
</ol>
<p>이제 직렬화 및 역직렬화를 사용하는 방법을 코드를 통해 알아보겠습니다.</p>
<blockquote>
<p>직렬화 방법</p>
</blockquote>
<ol>
<li>직렬화 할 데이터의 구조를 클래스 혹은 구조체로 선언합니다.</li>
<li>해당 클래스(구조체)에 [Serializable] 속성을 붙여 직렬화 가능하게 만듭니다.<pre><code class="language-cs">[Serializable]
public class MyClass
{
 public int level;
 public float timeElapsed;
 public string playerName;
}</code></pre>
</li>
<li>직렬화 할 클래스를 선언하고 값을 초기화합니다.<pre><code class="language-cs">MyClass myObject = new MyClass();
myObject.level = 1;
myObject.timeElapsed = 47.5f;
myObject.playerName = &quot;Dr Charles Francis&quot;;</code></pre>
</li>
<li>JsonUtility.ToJson() 메서드를 사용해 클래스를 json 포맷의 문자열로 생성합니다.<pre><code class="language-cs">string json = JsonUtility.ToJson(myObject);
// json now contains: &#39;{&quot;level&quot;:1,&quot;timeElapsed&quot;:47.5,&quot;playerName&quot;:&quot;Dr Charles Francis&quot;}&#39;</code></pre>
<a href="https://docs.unity3d.com/kr/2021.3/Manual/JSONSerialization.html">코드 출처</a></li>
</ol>
<blockquote>
</blockquote>
<p>역직렬화 방법 (위 코드 참고)</p>
<ol>
<li>Json 포멧의 문자열 데이터와 동일한 구조의 클래스의 인스턴스를 선언합니다.</li>
<li>JsonUtility.FromJson&lt; T &gt;() 메서드를 통해 문자열 데이터를 해당 클래스의 구조로 매핑합니다.<pre><code class="language-cs">myObject = JsonUtility.FromJson&lt;MyClass&gt;(json);</code></pre>
! 여기서 변환할 클래스의 구조에 없는 데이터는 무시됩니다.</li>
</ol>
<p>참고자료
<a href="https://docs.unity3d.com/kr/2021.3/Manual/script-Serialization.html">https://docs.unity3d.com/kr/2021.3/Manual/script-Serialization.html</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Unity-Json으로 상점 데이터 구현하기]]></title>
            <link>https://velog.io/@dong_kyu-lee/Unity-Json%EC%9C%BC%EB%A1%9C-%EC%83%81%EC%A0%90-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dong_kyu-lee/Unity-Json%EC%9C%BC%EB%A1%9C-%EC%83%81%EC%A0%90-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 23 Aug 2023 13:48:40 GMT</pubDate>
            <description><![CDATA[<h3 id="json을-사용해-상점-데이터를-save---load-해보자">Json을 사용해 상점 데이터를 Save  &amp; Load 해보자.</h3>
<p>현재 진행 중인 프로젝트에서 필요한 상점이 가지는 정보는 다음과 같다.</p>
<blockquote>
</blockquote>
<ol>
<li>해머 아이템 (해머는 소모품으로 한 번 구매 후 사용하면 다시 구매할 수 있다.)</li>
<li>방어선 증가 아이템 (1번만 구매 가능. 9칸 → 10칸)</li>
<li>캐릭터 선택 슬롯 증가 아이템 (LV 4까지 존재. 6칸→7칸, 7→8, 8→9, 9→10)</li>
<li>캐릭터 레벨 업그레이드</li>
</ol>
<p>위 요소들을 저장할 데이터 형식은 다음과 Json으로 변환할 클래스로 나타내었다.</p>
<pre><code class="language-cs">[System.Serializable]
public class ShopData
{
    public bool             hasHammer;        // 구매 여부
    public bool             hasSeatExpansion; // 구매 여부
    public int              slotLevel;
    public CharacterLvls[]  characterLvls;    // 캐릭터 별 레벨
    public ShopData()
    {
        hasHammer = false;
        hasSeatExpansion = false;
        slotLevel = 0;
        characterLvls = null;
    }
}

[System.Serializable]
public struct CharacterLvls
{
    public string characterName;
    public int level;
}</code></pre>
<p>이제 위와 동일 데이터 구조를 가진 json 스크립트를 불러오는 기능을 구현해보자.</p>
<p>먼저 위 클래스를 json으로 변환하려면 스크립트 직렬화가 가능한 클래스여야 한다.</p>
<p>json 스크립트 직렬화는 System 네임스페이스에서 제공하는 &quot;[Serializable]&quot; 명령어로 사용자 정의한 클래스를 <a href="https://velog.io/@dong_kyu-lee/Unity-%EC%9C%A0%EB%8B%88%ED%8B%B0%EC%97%90%EC%84%9C-%EC%A7%81%EB%A0%AC%ED%99%94%EB%9E%80">직렬화</a> 할 수 있다.</p>
<pre><code class="language-cs">using System.IO;
...

public class DataManager : MonoBehaviour
{
    static string shopDataPath = Application.dataPath +
                                &quot;/Resources/Data/shopData.json&quot;;

    ...

    public static void SaveShopData(ShopData shopData)
    {
        // json 형태로 된 문자열 생성
        string json = JsonUtility.ToJson(shopData);
        // 파일 생성 및 저장
        File.WriteAllText(shopDataPath, json);
    }

    ...
}</code></pre>
<p>위 코드에서 사용하는 JsonUtility는 무엇인가?</p>
<p>JsonUtility는 유니티 엔진에서 제공하는 클래스로, 이 클래스의 함수를 사용해 유니티 오브젝트를 json 포맷으로 상호 전환을 할 수 있다.</p>
<ul>
<li>JsonUtility.ToJson(object obj) : object 데이터를 json 포맷의 문자열로 변환한다.</li>
</ul>
<p>그리고 생성된 문자열을 지정한 위치에 저장한다.</p>
<ul>
<li>File.WriteAllText(string path, string contents) : 새 파일을 만들고 contents를 해당 파일에 작성한다. 만약 path가 존재한다면 그 파일을 덮어쓴다.</li>
</ul>
<pre><code class="language-json">{&quot;hasHammer&quot;:false,&quot;hasSeatExpansion&quot;:false,&quot;slotLevel&quot;:0,&quot;characterLvls&quot;:[]}</code></pre>
<p>위와 같이 json 파일이 생성되었다.</p>
<p>이제 Json 형식으로 저장했으니 그것을 불러오는 것도 구현해보자.</p>
<pre><code class="language-cs">using System.IO;
...

public class DataManager : MonoBehaviour
{
    ...

    public static ShopData GetShopData()
    {
        ShopData shopData = new ShopData();

        if (File.Exists(shopDataPath))
        {
            string jsonString = File.ReadAllText(shopDataPath);
            shopData = JsonUtility.FromJson&lt;ShopData&gt;(jsonString);
        }
        else
        {
            SaveShopData(shopData);
        }

        return shopData;
    }

    ...
}</code></pre>
<p>먼저 File.Exist(path)를 통해 지정된 위치에 데이터가 있는 지 확인하고,
파일이 있다면, Json 파일을 읽어 문자열로 가져온다.</p>
<pre><code class="language-cs">string File.ReadAllText(string path); // 해당 위치에 있는 파일을 읽어 문자열로 반환한다.</code></pre>
<p>그리고 가져온 문자열을 JsonUtility의 함수를 사용해 같은 데이터 구조의 객체에 대입해준다.</p>
<pre><code class="language-cs">T FromJson&lt;T&gt;(string json); // json 포맷의 데이터를 동일한 데이터 구조의 오브젝트로 생성한다.</code></pre>
<p>❗주의해야 할 것은, json 파일에 저장된 key 이름과 value의 타입이 생성하려는 클래스의 구조와 같아야 한다.</p>
<pre><code class="language-cs">public class ShopManager : ShopBase
{
    ShopData shopData;
    ...

    void Start()
    {
        shopData = DataManager.GetShopData();

        Debug.Log(&quot;hasHammer : &quot; + shopData.hasHammer);
        Debug.Log(&quot;hasSeatExpansion : &quot; + shopData.hasSeatExpansion);
        Debug.Log(&quot;slotLevel : &quot; + shopData.slotLevel);
        Debug.Log(&quot;CharacterLvls : &quot; + shopData.characterLvls);
    ...
    }
}</code></pre>
<p>이제 데이터를 사용할 ShopManager 클래스에서 Debug.Log()를 찍어보면 다음과 같이 결과가 나오는 것을 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/dong_kyu-lee/post/f219530b-16b9-45a9-b305-a05abad2c38f/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSV를 사용하여 몬스터 스폰 데이터 구현]]></title>
            <link>https://velog.io/@dong_kyu-lee/Unity-CSV-%ED%8C%8C%EC%8B%B1%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dong_kyu-lee/Unity-CSV-%ED%8C%8C%EC%8B%B1%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 24 Jul 2023 05:14:01 GMT</pubDate>
            <description><![CDATA[<p>게임을 개발하면서 게임에 필요한 크고 작은 데이터들을 관리해야 한다.
예를 들어, 다이얼로그 시스템이나 여러 게임 캐릭터들의 스텟 정보 등을 텍스트로 관리하여 쉽게 유지, 보수를 할 수 있다.</p>
<p>나의 경우, CSV 파일을 활용하여 게임에서 몬스터 자동 스폰(몬스터 웨이브) 데이터를 관리해 보았다.</p>
<p><strong>CSV란, CSV(comma-separated values)로, 표를 쉼표로 구분한 텍스트 데이터이다.</strong></p>
<p><img src="https://velog.velcdn.com/images/dong_kyu-lee/post/9a599cd3-3772-4b06-8c99-2a842bd09e4e/image.png" alt=""></p>
<p>구글 스프레드시트를 활용해 아래와 같이 CSV 파일을 쉽게 생성할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/dong_kyu-lee/post/3a4ae9ba-7018-43a9-9e47-7a67af2ae6d2/image.png" alt=""></p>
<p><strong>파일 -&gt; 다운로드 -&gt; 쉼표로 구분된 값(.csv)</strong></p>
<p><img src="https://velog.velcdn.com/images/dong_kyu-lee/post/8d9efaf2-4d08-4ae3-8e4b-87228b76e170/image.png" alt=""></p>
<p>CSV로 저장된 데이터를 보면 콤마(,)로 구분되어 저장되어 있는 것을 볼 수 있다.</p>
<p>이제 이것을 파싱하는 코드를 확인해보자.</p>
<pre><code class="language-cs">// 한 페이즈의 Wave 데이터를 파싱하여 stage 별로 나눈 리스트를 반환.
    List&lt;StageWave&gt; WaveParse(string _CSVFileName)
    {
        List&lt;StageWave&gt; res = new List&lt;StageWave&gt;();

        TextAsset csvData = Resources.Load&lt;TextAsset&gt;($&quot;Waves/{_CSVFileName}&quot;);

        string[] data = csvData.text.Split(new char[] { &#39;\n&#39; });

        int count = data.Length;
        for(int i = 1; i &lt; count;)
        {
            string[] elements = data[i].Split(new char[] { &#39;,&#39; });
            int currentStage = int.Parse(elements[0]);

            List&lt;MonsterWave&gt; waveList = new List&lt;MonsterWave&gt;();
            do
            {
                MonsterWave wave;

                wave.stage = int.Parse(elements[0]); // Stage
                wave.time = float.Parse(elements[1]); // time
                if (elements[2] == &quot;first&quot;)
                {
                    ++i;
                    monsterWaveTimer.FirstwaveTime = (int)wave.time;
                    elements = data[i].Split(new char[] { &#39;,&#39; });
                    continue;
                }
                else if (elements[2] == &quot;second&quot;)
                {
                    ++i;
                    monsterWaveTimer.SecondWaveTime = (int)wave.time;
                    elements = data[i].Split(new char[] { &#39;,&#39; });
                    continue;
                }
                int monsterNum = int.Parse(elements[2]);
                wave.monsterInfo = Resources.Load&lt;GameObject&gt;($&quot;Prefabs/Monsters/{(MonsterName)monsterNum}&quot;); // monster
                wave.line = int.Parse(elements[3]); // line

                if (++i &lt; count)
                {
                    waveList.Add(wave);
                    elements = data[i].Split(new char[] { &#39;,&#39; });
                }
                else
                {
                    waveList.Add(wave);
                    break;
                }
            }
            while (int.Parse(elements[0]) == currentStage);

            StageWave waveSet = new StageWave(waveList);
            res.Add(waveSet);
        }

        return res;
    }
}</code></pre>
<p>위 코드는 CSV 텍스트 데이터를 파싱하여, 스테이지 별로 몬스터 스폰 데이터를 저장하는 코드이다.</p>
<pre><code class="language-cs">TextAsset csvData = Resources.Load&lt;TextAsset&gt;($&quot;Waves/{_CSVFileName}&quot;);</code></pre>
<p>여기서 TextAsset은 Unity에서 지원하는 텍스트 파일 포멧으로,
.txt / .html / .htm / .xml / .bytes / .json / .csv / .yaml / .fnt</p>
<p>위와 같은 파일 형식을 지원한다.
TextAsset 으로 경로 상에 있는 csv 파일을 가져온다.</p>
<pre><code class="language-cs">string[] data = csvData.text.Split(new char[] { &#39;\n&#39; });</code></pre>
<p>그리고 가져온 데이터를 개행문자(\n) 단위로 자르고 </p>
<pre><code class="language-cs">string[] elements = data[i].Split(new char[] { &#39;,&#39; });</code></pre>
<p>한 줄에 포함된 요소(elements)를 콤마(,) 단위로 잘라 저장한다.</p>
<pre><code class="language-cs">int currentStage = int.Parse(elements[0]);</code></pre>
<p>이후 위와 같이 문자열로 되어있는 값을 원하는 데이터 타입으로 차례차례 변환하여 게임 내에 필요한 데이터를 도출할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준-1920]]></title>
            <link>https://velog.io/@dong_kyu-lee/%EB%B0%B1%EC%A4%80-1920</link>
            <guid>https://velog.io/@dong_kyu-lee/%EB%B0%B1%EC%A4%80-1920</guid>
            <pubDate>Sun, 17 Jul 2022 16:56:00 GMT</pubDate>
            <description><![CDATA[<p>이번 문제는 매우 간단해보이는 문제였다.
입력 값의 범위를 보지 않고 문제를 제출했을 때의 코드는 다음과 같다.</p>
<pre><code>int main()
{
    int N = 0;
    cin &gt;&gt; N;
    unordered_map&lt;int, int&gt; s;
    for (int i = 0; i &lt; N; ++i)
    {
        int num = 0; cin &gt;&gt; num;
        s.insert(make_pair(num, num));
    }

    int M = 0;
    cin &gt;&gt; M;
    vector&lt;int&gt; vec2(M);
    for (int i = 0; i &lt; M; ++i)
        cin &gt;&gt; vec2[i];

    for (int j = 0; j &lt; M; ++j)
    {
        if (s.find(vec2[j]) != s.end()) cout &lt;&lt; 1 &lt;&lt; endl;
        else cout &lt;&lt; 0 &lt;&lt; endl;
    }
}</code></pre><p>처음 unordered_map을 쓸 때 무작정 O(1)의 시간복잡도를 갖는다고 알고 사용했는데, 사실 최악의 경우 O(N)의 시간복잡도를 가질 수 있었다.
최악의 경우 O(M*N)의 시간복잡도를 가질 수 있는 것이다.</p>
<p>그래서 일반적인 for문을 가지고서는 문제를 해결할 수 없다고 생각해 N번 입력받은 수들을 오름차순으로 나열하여 절반씩 잘라서 확인하는 분할정복, 그 중 이진탐색(Binary Search) 방법을 사용해 보기로 했다.</p>
<p>그 결과는 다음과 같다.</p>
<pre><code>void solve(vector&lt;int&gt;&amp; v, int num, int left, int right)
{
    int mid = (left + right) / 2;

    if (left &gt; right)
    {
        cout &lt;&lt; &quot;0\n&quot;;
        return;
    }
    else {
        if (num &lt; v[mid]) {
            return solve(v, num, left, mid - 1);
        }
        else if (num &gt; v[mid]) {
            return solve(v, num, mid + 1, right);
        }
        else {
            cout &lt;&lt; &quot;1\n&quot;;
            return;
        }
    }
}</code></pre><blockquote>
<pre><code>if (left &gt; right)
    {
        cout &lt;&lt; &quot;0\n&quot;;
        return;
    }</code></pre></blockquote>
<p>  ```</p>
<p> 원래 위 부분에서 출력값과 리턴값을 합쳐서</p>
<blockquote>
<p> <code>return 0;</code></p>
</blockquote>
<p>이런 식으로 표현하려 했으나 시간초과가 발생하여 원래대로 고치게 되었다.</p>
<h4 id="이번-문제를-풀면서-아직-자료구조에-대한-지식이-많이-부족하고-알고리즘-구현에-기초가-되는-지식들을-쌓아야겠다는-것을-느꼈다😭">이번 문제를 풀면서 아직 자료구조에 대한 지식이 많이 부족하고, 알고리즘 구현에 기초가 되는 지식들을 쌓아야겠다는 것을 느꼈다..😭</h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준-설탕 배달]]></title>
            <link>https://velog.io/@dong_kyu-lee/%EB%B0%B1%EC%A4%80-%EC%84%A4%ED%83%95-%EB%B0%B0%EB%8B%AC</link>
            <guid>https://velog.io/@dong_kyu-lee/%EB%B0%B1%EC%A4%80-%EC%84%A4%ED%83%95-%EB%B0%B0%EB%8B%AC</guid>
            <pubDate>Mon, 20 Jun 2022 12:42:44 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dong_kyu-lee/post/f09f46c8-e8e5-48c0-8226-00feac93ef5a/image.png" alt=""></p>
<p><a href="https://www.acmicpc.net/problem/2839">백준-설탕배달(2839)</a></p>
<h4 id="메모이제이션memoization을-사용해-백준-2839번-문제를-풀어보았다">메모이제이션(memoization)을 사용해 백준 2839번 문제를 풀어보았다.</h4>
<blockquote>
<p>재귀호출을 사용한 전수조사</p>
</blockquote>
<pre><code>int solve(int m, int res)
{
    if (m &lt; 0) return -1;
    if (m == 0) return res;
    int a = solve(m - 5, res+1);
    int b = solve(m - 3, res+1);
    if (a != -1 &amp;&amp; b != -1) return a &lt; b ? a : b;
    else if (a == -1 &amp;&amp; b != -1) return b;
    else if (a != -1 &amp;&amp; b == -1) return a;
    else return -1;
}</code></pre><p>위의 방법처럼 했더니 당연하게도 시간초과가 났다.
N의 최대 크기가 5000이었는데, 재귀호출을 하면 최적의 해를 찾는 이진 탐색 트리의 최소 깊이만 해도 1000이다.</p>
<p>그래서 위 코드에 메모이제이션을 넣어 사용해보았다.</p>
<pre><code>int solve(int m, unordered_map&lt;int,int&gt;&amp; memo)
{
    if (m &lt; 0) return -1;
    if (m == 0) return 0;

    int a, b;
    if (memo.find(m - 5) != memo.end()) a = memo[m - 5];
    else {
        a = solve(m - 5,  memo);
        memo.insert(make_pair(m - 5, a));
    }
    if (memo.find(m - 3) != memo.end()) b = memo[m - 3];
    else {
        b = solve(m - 3, memo);
        memo.insert(make_pair(m - 3, b));
    }

    if (a != -1 &amp;&amp; b != -1) return a &lt; b ? a+1 : b+1;
    else if (a == -1 &amp;&amp; b != -1) return b+1;
    else if (a != -1 &amp;&amp; b == -1) return a+1;
    else return -1;
}</code></pre><p>Hash map을 사용해 계산한 값들을 저장해준다.
만약 hash map의 key가 존재하면, 해당 계산을 수행한 적이 있는 것이다.
key가 존재한다면 key에 해당하는 value를 가져오기만 하면 된다.
따라서 이미 했던 재귀호출들을 또 하는 비효율을 없앨 수 있다.</p>
<p>결과는 성공이다.</p>
<p><em>공부를 위해 기록해놓은 것입니다. 틀린 것이 있다면 지적해주시면 감사드리겠습니다!</em></p>
]]></description>
        </item>
    </channel>
</rss>