<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>소년코딩</title>
    <link>https://boycoding.tistory.com/</link>
    <description>소년코딩, 자바스크립트, C++, 물리, 게임 코딩 이야기</description>
    <language>ko</language>
    <pubDate>Thu, 30 Jul 2026 15:29:05 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>소년코딩</managingEditor>
    <image>
      <title>소년코딩</title>
      <url>https://t1.daumcdn.net/cfile/tistory/265C1A4F57DD23A219</url>
      <link>https://boycoding.tistory.com</link>
    </image>
    <item>
      <title>생성형 AI로 차별화하는 홍보영상 제작 강의 후기</title>
      <link>https://boycoding.tistory.com/267</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&amp;lt;생성형 AI로 차별화하는 홍보 영상 제작&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;강사님 : 우연이 / 루앙데이 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;최근에&amp;nbsp;&lt;/span&gt;&lt;b&gt;용인시에서 제공하는 &amp;lsquo;생성형 AI로 차별화하는 홍보영상 제작&amp;rsquo; 강의&lt;/b&gt;&lt;span&gt;를 들었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;-&amp;nbsp; 이미지 생성형 AI로 &lt;/span&gt;&lt;b&gt;어떻게 원하는 장면을 만들 수 있는지&lt;/b&gt;&lt;span&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 모델마다 결과가 어떻게 달라지는지&lt;/b&gt;&lt;span&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그리고 &lt;/span&gt;&lt;b&gt;프롬프트 한 줄이 만들어내는 차이&lt;/b&gt;&lt;span&gt;를 직접 체험했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇보다 놀라웠던 건&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출시된 지 일주일도 안 된 &lt;span&gt;&lt;b&gt;소라2(Sora 2)&lt;/b&gt;&lt;/span&gt; 를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강사님 덕분에 세상 누구보다 빠르게 써볼 수 있었다는 것!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 단순한 도구가 아니라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아이디어를 함께 만들어주는 창작 파트너&lt;/b&gt;&lt;span&gt;라는 걸 느꼈다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 내가 개발할 때 필요한 이미나 영상들을&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 지식들을 적극적으로 활용할 수 있을 것 같아&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 보람찬 시간이었다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>용인시교육 #생성형AI #AI영상 #소라2 #AI홍보영상 #창작의미래 #AI강의 #인공지능</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/267</guid>
      <comments>https://boycoding.tistory.com/267#entry267comment</comments>
      <pubDate>Sun, 19 Oct 2025 19:30:57 +0900</pubDate>
    </item>
    <item>
      <title>항해 플러스 AI 후기 | 8주간의 딥러닝과 LLM, RAG 학습 경험</title>
      <link>https://boycoding.tistory.com/266</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-23 오후 11.34.33.png&quot; data-origin-width=&quot;1380&quot; data-origin-height=&quot;548&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cykfcq/btsMushwiWC/n4PkWDnr35KEvdbGzqayzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cykfcq/btsMushwiWC/n4PkWDnr35KEvdbGzqayzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cykfcq/btsMushwiWC/n4PkWDnr35KEvdbGzqayzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcykfcq%2FbtsMushwiWC%2Fn4PkWDnr35KEvdbGzqayzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1380&quot; height=&quot;548&quot; data-filename=&quot;스크린샷 2025-02-23 오후 11.34.33.png&quot; data-origin-width=&quot;1380&quot; data-origin-height=&quot;548&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;안녕하세요! 저는 게임 개발자로서 인공지능(AI)과의 접목을 고민해 오던 중, AI 역량을 키우기 위해 &lt;/span&gt;&lt;span&gt;&lt;b&gt;항해 플러스 AI&lt;/b&gt;&lt;/span&gt;&lt;span&gt; 과정을 수료하였습니다. 기존에 게임 개발과 AI 추천 시스템에 관심이 많았으며, 이번 과정에서는 딥러닝, LLM, 파인튜닝, RAG 등을 학습하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;2. 항해 플러스에 들어오기 전, 현업에서 갖고 있던 고민&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;현재 AI 기술이 게임 개발, 추천 시스템, 챗봇 등 다양한 분야에서 활용되고 있는 것을 보며, AI에 대한 깊이 있는 이해가 필요하다고 느꼈습니다. 하지만 혼자 공부하기에는 범위가 너무 넓고, 최신 기술을 어떻게 적용해야 할지 막막했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;3. 항해 플러스를 선택하게 된 결정적 계기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;AI 관련 공부를 체계적으로 하고 싶었고, &lt;/span&gt;&lt;span&gt;&lt;b&gt;실제 프로젝트 기반 학습&lt;/b&gt;&lt;/span&gt;&lt;span&gt;과 &lt;/span&gt;&lt;span&gt;&lt;b&gt;최신 AI 트렌드 반영&lt;/b&gt;&lt;/span&gt;&lt;span&gt;이라는 항해 플러스 AI의 커리큘럼이 매력적으로 다가왔습니다. 특히, LLM과 RAG와 같은 최신 기술을 다룬다는 점이 결정적인 이유였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;4. 항해 플러스 AI 코스의 장점&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;1) 딥러닝 기초부터 LLM 활용까지 다루는 커리큘럼&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;단순히 머신러닝 기초에서 끝나는 것이 아니라, 실제 &lt;/span&gt;&lt;span&gt;&lt;b&gt;LLM 모델을 활용하고 파인튜닝하는 과정&lt;/b&gt;&lt;/span&gt;&lt;span&gt;까지 학습할 수 있었습니다. 특히, RAG를 통한 AI 시스템 개선 과정이 인상적이었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;2) 현업에서는 경험하기 어려운 AI 서비스 개발&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;딥러닝 모델을 실제로 배포하고, AI 서비스를 개발해보는 과정이 있어서 &lt;/span&gt;&lt;span&gt;&lt;b&gt;실제 서비스에서 AI를 어떻게 활용할지&lt;/b&gt;&lt;/span&gt;&lt;span&gt;를 고민할 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;3) 현직 AI 종사자의 멘토링&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;각 단계에서 멘토님들이 직접 코드 리뷰와 피드백을 주셔서, 실무 관점에서 어떻게 접근해야 하는지 배울 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;5. 항해 플러스 AI 과정에서 만든 프로젝트: 루메나AI&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;RAG1.png&quot; data-origin-width=&quot;3838&quot; data-origin-height=&quot;1902&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcK61i/btsMuJJ8Kst/7vDo2TxDcKUC16am7QSTJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcK61i/btsMuJJ8Kst/7vDo2TxDcKUC16am7QSTJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcK61i/btsMuJJ8Kst/7vDo2TxDcKUC16am7QSTJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcK61i%2FbtsMuJJ8Kst%2F7vDo2TxDcKUC16am7QSTJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;474&quot; height=&quot;235&quot; data-filename=&quot;RAG1.png&quot; data-origin-width=&quot;3838&quot; data-origin-height=&quot;1902&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignRight&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;RAG2.png&quot; data-origin-width=&quot;3836&quot; data-origin-height=&quot;1910&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqryDa/btsMtpy4ULh/VfTfyKwBC8nYn8PlqBaOl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqryDa/btsMtpy4ULh/VfTfyKwBC8nYn8PlqBaOl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqryDa/btsMtpy4ULh/VfTfyKwBC8nYn8PlqBaOl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqryDa%2FbtsMtpy4ULh%2FVfTfyKwBC8nYn8PlqBaOl0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;462&quot; height=&quot;230&quot; data-filename=&quot;RAG2.png&quot; data-origin-width=&quot;3836&quot; data-origin-height=&quot;1910&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;항해 플러스 AI 과정을 통해, 유튜브 영상의 내용을 분석하고 요약해주는 서비스인 &lt;/span&gt;&lt;span&gt;&lt;b&gt;루메나AI&lt;/b&gt;&lt;/span&gt;&lt;span&gt;를 개발하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;루메나AI는 &lt;/span&gt;&lt;span&gt;&lt;b&gt;자연어 처리 기술과 LLM을 활용하여&lt;/b&gt;&lt;/span&gt;&lt;span&gt; 긴 유튜브 영상을 빠르게 요약하고, 핵심 내용을 제공하는 AI 서비스입니다. 이를 통해 유저들은 긴 영상을 시청하지 않아도 &lt;/span&gt;&lt;span&gt;&lt;b&gt;중요한 내용을 빠르게 파악할 수 있도록&lt;/b&gt;&lt;/span&gt;&lt;span&gt; 도와줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;프로젝트 깃허브 링크: &lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://github.com/adunStudio/LumenaAI&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Lumena AI GitHub&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 프로젝트를 통해 LLM과 RAG 기술을 활용하여 실제 서비스를 개발하는 경험을 쌓을 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;6. 항해 플러스 이전과 이후 어떻게 달라졌나&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;1) 기술적 성장&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;딥러닝과 트랜스포머 모델을 이해&lt;/b&gt;&lt;/span&gt;&lt;span&gt;하고 활용할 수 있게 되었습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;LLM 파인튜닝 및 RAG 기반 검색 시스템&lt;/b&gt;&lt;/span&gt;&lt;span&gt;을 직접 구현해 볼 수 있었습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;2) AI를 바라보는 시각 변화&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;기존에는 AI가 막연하게 어려운 기술이라고 생각했지만, &lt;/span&gt;&lt;span&gt;&lt;b&gt;적절한 접근 방법과 실습을 통해 실제 활용할 수 있는 기술&lt;/b&gt;&lt;/span&gt;&lt;span&gt;이라는 것을 깨달았습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;3) 개발자 네트워크&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;같은 목표를 가진 동료들과 협업하며 &lt;/span&gt;&lt;span&gt;&lt;b&gt;AI 개발자 네트워크를 구축&lt;/b&gt;&lt;/span&gt;&lt;span&gt;할 수 있었습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;7. 항해 플러스에 투자한 시간, 돈이 아깝지 않은 이유&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;항해 플러스 AI 과정은 단순한 이론 수업이 아니라, &lt;/span&gt;&lt;span&gt;&lt;b&gt;실무에서 바로 적용 가능한 프로젝트 기반 학습&lt;/b&gt;&lt;/span&gt;&lt;span&gt;이었습니다. 이를 통해 &lt;/span&gt;&lt;span&gt;&lt;b&gt;AI 서비스 개발자로서 한 단계 성장&lt;/b&gt;&lt;/span&gt;&lt;span&gt;할 수 있었고, &lt;/span&gt;&lt;span&gt;&lt;b&gt;실제 프로젝트에서 AI를 적용할 수 있는 자신감&lt;/b&gt;&lt;/span&gt;&lt;span&gt;을 얻었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;8. 항해 플러스 코스를 고민하고 있는 개발자들에게 한마디&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;AI를 공부하고 싶지만, 혼자 학습하기 어렵거나 방향을 못 잡고 있다면 &lt;/span&gt;&lt;span&gt;&lt;b&gt;항해 플러스 AI 코스&lt;/b&gt;&lt;/span&gt;&lt;span&gt;를 추천합니다. 체계적인 커리큘럼과 실습을 통해, &lt;/span&gt;&lt;span&gt;&lt;b&gt;단순한 AI 개념 학습이 아니라, 실제 활용 가능한 기술을 익힐 수 있습니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;9. 수료생 추천 할인 혜택 안내&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;결제 페이지 &amp;gt; 할인 코드 입력 &amp;gt; 수료생 할인 코드 입력 시 20만 원 할인 적용&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;추천인은 1명당 인센티브 20만 원&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;피추천인은 등록금 20만 원 할인&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;할인 코드 관련 문의는 비밀댓글 또는 오픈 채팅을 통해 문의 주세요!&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;</description>
      <category>웹앱 이야기</category>
      <category>항해솔직후기</category>
      <category>항해플러스ai</category>
      <category>항해플러스후기</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/266</guid>
      <comments>https://boycoding.tistory.com/266#entry266comment</comments>
      <pubDate>Sun, 23 Feb 2025 23:35:35 +0900</pubDate>
    </item>
    <item>
      <title>경영정보시각화능력 시험 신설</title>
      <link>https://boycoding.tistory.com/265</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;IT 스펙트럼을 넓히기 위해 현재 직무와 겹치지 않아도 공부를 했고, 또 그것을 증명하기 위해 자격증을 많이 취득했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자는 실력주의라고 하지만 뜻밖의 상황에서 자격증의 덕을 보는 경우가 많이 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 대한상공회의소에서 &lt;b&gt;경영정보시각화능력&lt;/b&gt;이라는 시험이 새로 나와 공부 후 취득할 예정.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퇴근 후 유튜브 쇼츠를 보는 것보다 조금이라도 지식을 넓혀가는 게 더 좋다고 생각한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 취득한 빅데이터분석기사와 내용이 많이 겹치므로 상대적으로 짧은 학습 시간이 필요할 것으로 예상하지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1과목 경영정보 공부는 나에게 새로운 시야를 제공할 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img_2024_webposter_PowerBi.png&quot; data-origin-width=&quot;3558&quot; data-origin-height=&quot;7296&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VgLhp/btsFXPRybsK/Wxq202ikc5lA4f8JSvMsPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VgLhp/btsFXPRybsK/Wxq202ikc5lA4f8JSvMsPK/img.png&quot; data-alt=&quot;경영정보시각화능력 소개&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VgLhp/btsFXPRybsK/Wxq202ikc5lA4f8JSvMsPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVgLhp%2FbtsFXPRybsK%2FWxq202ikc5lA4f8JSvMsPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3558&quot; height=&quot;7296&quot; data-filename=&quot;img_2024_webposter_PowerBi.png&quot; data-origin-width=&quot;3558&quot; data-origin-height=&quot;7296&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;경영정보시각화능력 소개&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/265</guid>
      <comments>https://boycoding.tistory.com/265#entry265comment</comments>
      <pubDate>Thu, 21 Mar 2024 09:43:39 +0900</pubDate>
    </item>
    <item>
      <title>유니티 FSM: 유한 상태 머신</title>
      <link>https://boycoding.tistory.com/262</link>
      <description>&lt;h1 style=&quot;padding-bottom: 20px;&quot;&gt;유니티 FSM: 유한 상태 머신 (Finite State Machine)&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;유한 상태 머신(Finite State Machine, FSM)&lt;/b&gt;은 게임 에이전트에게 환상적인 지능을 부여하기 위한 선택 도구로 사용되어왔다.&lt;br /&gt;다시 말해, 유한 상태 머신은, 주어지는 모든 시간에서 처해 있을 수 있는 유한개의 상태를 가지고 주어지는 입력에 따라 어떤 상태에서 다른 상태로 전환하거나 출력이나 액션이 일어나게 하는 장치 또는 그런 장치를 나타낸 모델이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상태(State)&lt;/b&gt;: 게임에 정의된 여러 동작, 적 캐릭터뿐만 아니라 게이머에게도 적용될 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Idle, Run, Attack, ... , 공격할 수 없는 상태, 캐릭터의 마나가 없어 마법 공격할 수 없는 상태 등&lt;/li&gt;
&lt;li&gt;한 상태에서 다른 상태로 전화할 수 있고, 동시에 여러 상태를 실행할 수는 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전이(Transition)&lt;/b&gt;: 한 상태에서 다른 상태로 전화하는 것&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 상태 로직 또는 외부에서 전이 조건에 의해 전이될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1243&quot; data-origin-height=&quot;628&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/U4H5o/btq52ADpa2A/N7oTEakd81YzReDbLVPSm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/U4H5o/btq52ADpa2A/N7oTEakd81YzReDbLVPSm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/U4H5o/btq52ADpa2A/N7oTEakd81YzReDbLVPSm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FU4H5o%2Fbtq52ADpa2A%2FN7oTEakd81YzReDbLVPSm0%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1243&quot; data-origin-height=&quot;628&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI 개념을 프로그래머 외에 기획자 또는 제 3자가 쉽게 확인/설계 할 수있다.&lt;/li&gt;
&lt;li&gt;직관적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;확장이 힘들다. (FSM의 상태를 계속 추가하다 보면 다시 연결하기가 머리 아프다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr  /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;여러 가지 구현 방법&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 일반적인 구현:&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.raywenderlich.com/6034380-state-pattern-using-unity&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.raywenderlich.com/6034380-state-pattern-using-unity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/dubit/unity-fsm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/dubit/unity-fsm&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 구현하느냐에 따라 좋을 수도 안 좋을 수도, 확장성이 있을 수도 없을 수도 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 플러그인 방식:&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=cHUXh5biQMg&amp;amp;list=PLX2vGYjWbI0ROSj_B0_eir_VkHrEkd4pi&amp;amp;index=2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.youtube.com/watch?v=cHUXh5biQMg&amp;amp;list=PLX2vGYjWbI0ROSj_B0_eir_VkHrEkd4pi&amp;amp;index=2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/templates/systems/topdown-engine-89636?locale=ko-KR&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://assetstore.unity.com/packages/templates/systems/topdown-engine-89636?locale=ko-KR&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니티 에디터의 인스펙터에서 조합하는게 보통 노가다가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 상태 / 전이 클래스에서 에이전트에 대한 2중 컴포넌트 참조가 귀찮다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 애니메이터와 StageMachineBehaviour 이용:&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.unity3d.com/kr/530/ScriptReference/StateMachineBehaviour.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.unity3d.com/kr/530/ScriptReference/StateMachineBehaviour.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애니메이션 하나당 상태 1개 -&amp;gt; 상태가 많아지면..?&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. GUI 기반 에셋:&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/tools/visual-scripting/behavior-designer-behavior-trees-for-everyone-15277&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://assetstore.unity.com/packages/tools/visual-scripting/behavior-designer-behavior-trees-for-everyone-15277&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근본이 코드리스이므로 프로그래머의 코드와 합치려면 여간 귀찮다.&lt;/p&gt;
&lt;hr  /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;추천 라이브러리: MonsterLove FSM&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/thefuntastic/Unity3d-Finite-State-Machine&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/thefuntastic/Unity3d-Finite-State-Machine&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니티에서 상태 머신 개념을 사용하고 싶어 여러 가지 에셋/라이브러리를 사용해본 결과 현재는 MonsterLove FSM을 즐겨 쓰고 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;장점과 특징&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리플렉션 기반으로 한 코드 내에서 빠르게 코딩할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(외부 클래스에서 복잡한 컴포넌트 참조가 불필요하다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;구현 방법이 단순하고 직관적이며, 상태는 Enum 필드를 추가하기만 하면 된다.&lt;/li&gt;
&lt;li&gt;상태의 메서드는 밑줄 규칙 StateName_MethodName (상태명_메서드명)로 정의된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;using MonsterLove.StateMachine; //1. Remember the using statement

public class MyGameplayScript : MonoBehaviour
{
    public enum States
    {
        Init, 
        Play, 
        Win, 
        Lose
    }

    StateMachine&amp;lt;States&amp;gt; fsm;

    void Awake()
    {
        fsm = new StateMachine&amp;lt;States&amp;gt;(this); //2. The main bit of &quot;magic&quot;. 

        fsm.ChangeState(States.Init); //3. Easily trigger state transitions
    }

    void Init_Enter()
    {
        Debug.Log(&quot;Ready&quot;);
    }

    void Play_Enter()
    {      
        Debug.Log(&quot;Spawning Player&quot;);    
    }

    void Play_FixedUpdate()
    {
        Debug.Log(&quot;Doing Physics stuff&quot;);
    }

    void Play_Update()
    {
        if(player.health &amp;lt;= 0)
        {
            fsm.ChangeState(States.Lose); //3. Easily trigger state transitions
        }
    }

    void Play_Exit()
    {
        Debug.Log(&quot;Despawning Player&quot;);    
    }

    void Win_Enter()
    {
        Debug.Log(&quot;Game Over - you won!&quot;);
    }

    void Lose_Enter()
    {
        Debug.Log(&quot;Game Over - you lost!&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이브러리에 내장된 메서드는 &lt;code&gt;ChangeState(새로운 상태)&lt;/code&gt; 호출에 의해 자동으로 트리거 된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;내장 메서드 목록&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Enter&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Update&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FixedUpdate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LateUpdate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Exit&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Finally&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OntriggerEnter2D&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;리플렉션&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MonsterLove FSM의 가장 큰 특징은 한 코드 내에서 StateName_MethodName (Ex. Idle_Update) 규칙을 기반으로 자동으로 호출이 된다는 것이다. 이것은 리플렉션을 이용해서 구현했다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;MethodInfo[] methods = component.GetType().GetMethods(bindingFlags);

//Bind methods to states
for (int i = 0; i &amp;lt; methods.Length; i++)
{
    TState state;
    string evtName;
    if (!ParseName(methods[i], out state, out evtName))
    {
        continue; //Skip methods where State_Event name convention could not be parsed
    }

    StateMapping&amp;lt;TState, TDriver&amp;gt; mapping = stateLookup[state];

    if (eventFieldsLookup.ContainsKey(evtName))
    {
        //Bind methods defined in TDriver
        // driver.Foo.AddListener(StateOne_Foo);
        FieldInfo eventField = eventFieldsLookup[evtName];
        BindEvents(rootDriver, component, state, enumConverter(state), methods[i], eventField);
     }
     else
     {
         //Bind Enter, Exit and Finally Methods
         BindEventsInternal(mapping, component, methods[i], evtName);
      }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 클래스의 메서드 정보를 읽어와서 메서드의 이름이 라이브러리에서 정의한 이름 규칙에 맞는지 검사 후 FSM 구현을 위해 바인딩 하는 코드 일부다.&lt;/p&gt;
&lt;hr  /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;퍼포먼스&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;리플렉션&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리플렉션은 어셈블리, 모듈 및 형식을 설명하는 개체(&lt;a href=&quot;https://docs.microsoft.com/ko-kr/dotnet/api/system.type&quot;&gt;Type&lt;/a&gt; 형식)를 제공합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스의 메서드 정보를 가져오고 바인딩하기 위해 사용했으며, 일반적으로 리플렉션 사용은 컴파일 시점이 아닌 런타임 시점에 동적으로 실행되기 때문에 오버헤드를 일으킨다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 MonsterLove FSM 라이브러리의 StateMachine 인스턴스는 리플렉션 사용을 초기화 단계에서 단 한 번 사용으로 제한함으로써 오버헤드를 최대한 피했다. 이미 우리 게임은 최적화를 위해 오브젝트 풀링과 같은 여러 기법을 이미 사용하기 때문에 게임 유저들은 이 오버헤드를 알아차리지 못할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Invoke()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이브러리 내부에서는 정의된 이름 규칙에 맞는 메서드들을 검색 후 delegate와 같은 대리자에 바인딩한 후 &lt;code&gt;Invoke()&lt;/code&gt; 함수 호출을 통해 상태에 맞는 행동을 트리거한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Invoke()&lt;/code&gt; 함수는 일반적인 함수 호출보다 좀 더 느리다. 수만 개가 넘는 인스턴스 때문에 상당한 오버헤드가 발생할 수 있다. 그러나 일반적인 사용(매니저 클래스 또는 인스턴스 10 ~ 100개)에는 이 성능 차이를 반드시 고려할 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Update()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Monobehaviour.Update()&lt;/code&gt;와 같은 유니티 이벤트 함수는 기본적으로 프레임마다 자동으로 호출된다. 이것을 최적화하는 방법중 하나는 Update()를 쓰지 않는 것이다. 정확히 말하면 한 번만 쓰는것이다. 유니티는 Update()를 호출할 때 GameObject의 원시 코드와 중간 코드 연결에 많은 자원을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 추가 자원 사용을 최소화 하려면 Update()를 적게 사용해 불필요한 연결을 최소화해야 한다. 자체적인 업데이트 스타일을 함수가 담긴 구성요소와 이를 제어하는 만능 클래스라면 가능할 것이다. MonsterLove FSM은 이런 유니티 이벤트를 중앙에서 제어하는 설계를 가지고 있다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;void FixedUpdate() { /*** 생략 ***/}

void Update()
{
    for (int i = 0; i &amp;lt; stateMachineList.Count; i++)
    {
        var fsm = stateMachineList[i];
        if (!fsm.IsInTransition &amp;amp;&amp;amp; fsm.Component.enabled)
        {
            fsm.Driver.Update.Invoke();
        }
    }
}        

void LateUpdate() { /*** 생략 ***/}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 이러한 설계 덕분에 MonsterLove FSM의 퍼포먼스도 손쉽게 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;208&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnG06B/btq6b1STWM7/d4mvYSNSeE2fwP8vZX6uQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnG06B/btq6b1STWM7/d4mvYSNSeE2fwP8vZX6uQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnG06B/btq6b1STWM7/d4mvYSNSeE2fwP8vZX6uQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnG06B%2Fbtq6b1STWM7%2Fd4mvYSNSeE2fwP8vZX6uQ0%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;208&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는 이 포스트의 제일 상단에 있는 몬스터의 FSM을 일부 구현한 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1247&quot; data-origin-height=&quot;706&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKzVXQ/btq58YPPu0I/MDh92pF1cwqQhNwVV3jKpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKzVXQ/btq58YPPu0I/MDh92pF1cwqQhNwVV3jKpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKzVXQ/btq58YPPu0I/MDh92pF1cwqQhNwVV3jKpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKzVXQ%2Fbtq58YPPu0I%2FMDh92pF1cwqQhNwVV3jKpk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1247&quot; data-origin-height=&quot;706&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;public abstract Entity : GameBehaviour
{
    [Header(&quot;스탯&quot;)] public Stat Stat;
    public Vector3 Position =&amp;gt; transform.position;

    protected SpriteRenderer m_SpriteRenderer;

    protected Entity m_Target;

    protected bool m_Death;
    public bool Death =&amp;gt; m_Death;

    protected virtual void Awake()
    {
        m_SpriteRenderer = GetComponent&amp;lt;SpriteRenderer&amp;gt;();
    }

    public abstract void TakeHit(int damage);

    public abstract void Init(int dataIndex);
}

public class Monster : Entity
{

    // 몬스터의 상태 정의: 열거형
    public enum States
    {
        Spawn,
        Idle,
        Run,
        Attack,
        Death,
    }

    // FSM 
    private StateMachine&amp;lt;States&amp;gt; FSM;

    protected override void Awake()
    {
        base.Awake();

        FSM =  new StateMachine&amp;lt;States&amp;gt;(this);
        // FSM = StateMachine&amp;lt;States&amp;gt;.Initialize(this);
        // FSM.ChangeState(States.Idle);
    }

    public void TakeHit(int damage)
    {
        if (m_Death) return;

        Stat.Health -= damage;

        // OnTakeHit(damage);

        if (Stat.Health &amp;lt;= 0)
        {
            // OnDeath(damage);
            FSM.ChangeState(States.Death, StateTransition.Overwrite);
        }
    }

    public void Init(int monsterIndex)
    {
        // 외형 설정
        // 데이터 설정 (스탯...)

        m_Death = false;

        FSM.ChangeState(States.Spawn);
    }

    /**************************************** Spawn ************************************/
    protected virtual IEnumerator Spawn_Enter()
    {
        var duration = PlayAnimationAndGetDuration(&quot;Spawn&quot;);

        yield return new WaitForSeconds(duration);

        BattleManager.Instance.AddMonster(this);

        FSM.ChangeState(States.Idle);
    }

    /**************************************** Idle ************************************/
    protected virtual void Idle_Enter()
    {
        PlayAnimation(&quot;Idle&quot;);
    }

    protected virtual void Idle_Update()
    {
        var distance = GetDistance(Position, m_Target.Position);

        if (distance &amp;lt;= Stat.AttakRange &amp;amp;&amp;amp; Stat.AttackCoolTimer &amp;lt;= 0)
        {
            FSM.ChangeState(States.Attack);
        }

        if (distance &amp;gt; Stat.AttackRange)
        {
            FSM.ChangeState(States.Run);
        }
    }

    protected virtual void Idle_Exit()
    {
        Debug.Log(gameObject.name + &quot;: Idle_Exit&quot;);
    }

    /**************************************** Run ************************************/
    protected virtual void Run_Enter()
    {
        PlayAnimation(&quot;Run&quot;);
    }

    protected virtual void Run_Update()
    {
        if (GetDistance(Position, m_Target.Position) &amp;lt;= Stat.AttackRange)
        {
            FSM.ChangeState(States.Attack);
        }

        Vector2.MoveTowards(Position, m_Target.Position, Time.deltaTime * Stat.MoveSpeed);
    }

    protected virtual void Run_Exit()
    {
        Debug.Log(gameObject.name + &quot;: Run_Exit&quot;);
    }


    /**************************************** Attack ************************************/
    protected virtual IEnumerator Attack_Enter()
    {
        var duration = PlayAnimationAndGetDuration(&quot;Attack&quot;);

        var halfWait = new WaitForSeconds(duration / 2f);

        yield return halfWait;

        m_Target.TakeHit(Stat.Damage);

        yield return halfWait;

        FSM.ChangeState(States.Idle);
    }



    /**************************************** Death ************************************/
    protected virtual IEnumerator Death_Enter()
    {
        m_Death = true;

        var duration = PlayAnimationAndGetDuration(&quot;Death&quot;);

        yield return new WaitForSeconds(duration);

        BattleManager.Instance.RemoveMonster(this);

        SafeSetActive(false);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방치형 게임과 같이 몬스터의 상태 개수가 적고 단순할 경우 위처럼 한 클래스 내에서 빠르게 코드를 작성할 수 있는 장점이 있다.&lt;/p&gt;
&lt;hr  /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마무리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FSM을 주제로 R&amp;amp;D를 진행하면서 직접 구현해보기도 하고, 많은 라이브러리/에셋을 사용해 봤다. 결론은 좋고 나쁘고를 떠나 어떤 게임을 개발하느냐에 따라 자신에게 맞는 것을 사용하는 게 좋을 거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MonsterLove FSM은 여기서 설명한 것 외에도 더 많은 메커니즘이 있다. 사용하고 싶다면 꼭 공식 &lt;a href=&quot;https://github.com/thefuntastic/Unity3d-Finite-State-Machine&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git&lt;/a&gt;에서 API를 확인해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;64&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot; height=&quot;64&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WgUDI/btq52Tie7ae/iFKwlGoyabpw60GvlEzSPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WgUDI/btq52Tie7ae/iFKwlGoyabpw60GvlEzSPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WgUDI/btq52Tie7ae/iFKwlGoyabpw60GvlEzSPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWgUDI%2Fbtq52Tie7ae%2FiFKwlGoyabpw60GvlEzSPk%2Fimg.png&quot; width=&quot;64&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot; height=&quot;64&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>유니티 &amp;amp; C# 이야기</category>
      <category>C#</category>
      <category>FSM</category>
      <category>유니티</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/262</guid>
      <comments>https://boycoding.tistory.com/262#entry262comment</comments>
      <pubDate>Sun, 30 May 2021 17:18:28 +0900</pubDate>
    </item>
    <item>
      <title>9. 훑어보는 가상 메모리</title>
      <link>https://boycoding.tistory.com/261</link>
      <description>&lt;h1 id=&quot;&quot;&gt;가상 메모리&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/260?category=1036725&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;훑어보는 메모리 관리&lt;/a&gt;에서 언급한 바와 같이 사용자들이 시스템에 탑재된 물리 메모리보다 훨씬 크게 느껴지는 메모리를 말한다. 예를 들어, 현재 진행 중인 모든 프로그램의 크기의 총합이 10mb라면 이 시스템의 메모리 크기는 10mb 이상이라는 환상을 주는데 가상 메모리 덕분이다.&lt;/p&gt;&lt;p&gt;가상 메모리는 반드시 프로그램 전체가 적재되어야 한다는 전제하에서는 결코 얻어질 수 없다. 프로그램 전체가 적재가 아니라면 프로그램의 분리가 필연적으로 요구되고, 분리된 조각 중 필요한 부분만 적재되어야 한다.&lt;/p&gt;&lt;p&gt;가상 메모리 환경에서는 분리된 프로그램 일부분만 메모리에 적재되고 나머지는 디스크에 존재한다. 이때, 필요에 따라서 메모리에 적재된 내용을 디스크로 보내거나 디스크 내용을 메모리로 적재하는 &lt;strong&gt;스와핑(Swapping)&lt;/strong&gt;이 빈번하게 일어난다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;가상 메모리는 분리된 프로그램 조각 중, 필요한 일부 몇 개만 적재함으로써 얻어질 수 있다.&lt;/li&gt;

&lt;li&gt;가상 메모리 관리 기법은 페이징을 기반으로 한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;1demandpaging&quot;&gt;1. 요구 페이징 (Demand Paging)&lt;/h2&gt;

&lt;p&gt;가상 메모리를 제공하기 위해 확장한 페이징을 &lt;strong&gt;요구 페이징&lt;/strong&gt;이라 하는데, 명칭과 같이 실시간으로 요구가 있을 때 해당 페이지를 적재한다는 뜻이다.&lt;/p&gt;&lt;p&gt;요구 페이징은 페이지 테이블에 대한 약간의 수정과 하드웨어 인터럽트를 활용해서 구현할 수 있다. 페이지 테이블의 각 항목에 유효 비트(Valid/Invalid)를 추가하고, 지금 참고하고자 하는 페이지가 부재이면(유효하지 않으면) 인터럽트가 발생하여 대응되는 인터럽트 핸들러에 의해서 해당 페이지를 적재하도록 하면 이것이 &lt;strong&gt;요구 페이징&lt;/strong&gt;이 된다.&lt;/p&gt;&lt;p&gt;페이지를 디스크에서 읽어 들이는 과정을 스왑 인(Swap In), 적재된 페이지를 디스크로 저장하는 과정을 스왑 아웃(Swap out)이라 하고, 이들 두 과정을 스와핑(Swapping)이라 한다. 스왑 아웃은 메모리가 부족할 때 이루어지고, 이때 스왑 아웃시킬 페이지를 선택하는 일을 &lt;strong&gt;페이지 교체&lt;/strong&gt;라고 한다.&lt;/p&gt;

&lt;h2 id=&quot;2pagefaulttrap&quot;&gt;2. 페이지 부재 트랩(Page Fault Trap) 처리&lt;/h2&gt;

&lt;p&gt;요구 페이징의 가장 큰 장애 요인 중 하나는 페이지 부재로 인한 프로세스 처리 지연이다. 부재 페이지 때문에 메모리가 스왑 인 되는 과정은 엄연한 입/출력이므로 해당 프로세스는 입/출력이 완료될 떄까지 대기 상태로 전환되어야 하고, 프로세스 처리는 그만큼 지연될 수밖에 없다.&lt;/p&gt;&lt;p&gt;이러한 처리 지연을 최소화하기 위해서는 페이지 부재 트랩 처리 과정에 대한 명확한 이해가 필수적이다.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;CPU가 페이지 1에 있는 load 기계 명령어를 인출하여 피연산자 주소를 분리한다. (결과: 페이지 4(E))&lt;/li&gt;

&lt;li&gt;CPU가 다시 피연산자를 인출하기 위해 페이지 4(E)에 대한 접근으로 시도한다.&lt;/li&gt;

&lt;li&gt;MMU는 CPU가 보내온 주소(페이지 4, E)를 물리 주소로 변환하기 위해 페이지 테이블을 참조한다.&lt;/li&gt;

&lt;li&gt;MMU는 페이지 테이블을 참조해서 해당 페이지가 부재이므로 페이지 부재 트랩(인터럽트)이 발생하고, CPU는 해당 운영체제에 있는 페이지 부재 핸들러로 점프한다.&lt;/li&gt;

&lt;li&gt;운영체제는 페이지 부재 트랩 핸들러는 페이지 테이블에 기록된 디스크 정보를 참조하여 디스크의 12번 블록을 확인한다. (페이지 4(E) -&amp;gt; 디스크 12번 블록)&lt;/li&gt;

&lt;li&gt;물리 메모리에서 비어있는 2번 페이지 프레임을 발견하여 입/출력 채널에 디스크의 12번 블록을 읽어 들이도록 명령을 설정한 후, CPU를 다른 프로세스 할당한다. (문맥 교환(context switch))&lt;/li&gt;

&lt;li&gt;입/출력 채널로부터 입력 완료 인터럽트를 받으면 다시 페이지 부재 트랩 핸들러로 진입하여, 페이지 테이블의 4번 항목의 페이지 프레임 번호를 2로 변경하고,&lt;/li&gt;

&lt;li&gt;유효 비트를 무효(i)에서 유효(v)로 변경한 후,&lt;/li&gt;

&lt;li&gt;페이지 부재 트랩 핸들러를 마치면서, 프로세스의 페이지 부재를 일으켰던 기계 명령어부터 재실행하도록 한다.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;3pagereplacement&quot;&gt;3. 페이지 교체(Page Replacement)&lt;/h2&gt;

&lt;p&gt;실행 중인 프로세스들의 요구 페이징이 진행되고 적재 프로세스의 수가 늘어나면 결국에는 메모리 전체가 사용 상태에 이르게 된다. 이처럼 메모리에 비어있는 페이지 프레임이 없는 상황에서, 요구 페이징이나 새로운 프로세스의 적재를 위한 페이지 프레임이 필요할 때는 사용 중인 페이지 프레임 하나를 희생양(Victim)으로 선택해서 그 내용을 디스크에 보관 시켜 빈 페이지 프레임을 확보해야 하는데 이를 &lt;strong&gt;페이지 교체&lt;/strong&gt;라고 한다.&lt;/p&gt;&lt;p&gt;페이지 교체 작업 시에는 페이지 부재 처리 시간은 페이지 보관 시간이 추가되어 2배로 늘어나고, 교체될 페이지로 어떤 것을 선택하느냐의 문제는 시스템의 성능을 좌우하는 중요한 상황이다. (만약 바로 다음에 참조될 페이지가 교체된다면 최악의 선택이 되고, 프로세스 처리속도가 늦어진다.)&lt;/p&gt;

&lt;h4 id=&quot;optoptimal&quot;&gt;- 최적(OPT: Optimal) 페이지 교체&lt;/h4&gt;

&lt;p&gt;사용되지 않거나 가장 오랫동안 사용되지 않을 페이지를 교체하는 것이다. 미래에 가장 오랫동안 참조되지 않을 페이지를 교체하는 이상적인 기법이지만, 프로세스의 미래 진행을 예측할 수 없으므로 구현할 수 없다.&lt;/p&gt;

&lt;h4 id=&quot;fifofirstinfirstout&quot;&gt;- FIFO(First-In First-Out) 페이지 교체&lt;/h4&gt;

&lt;p&gt;가정 먼저 적재된 페이지 즉, 가장 오래된 페이지를 교체한다. 그러나 만약 그것이 현재 참조가 왕성하게 일어나고 있는 페이지라면 최악의 선택이 된다.&lt;/p&gt;

&lt;h4 id=&quot;lruleastrecentlyused&quot;&gt;- LRU(Least Recently Used) 페이지 교체&lt;/h4&gt;

&lt;p&gt;적재된 모든 페이지를 참조 순서에 따라 스택으로 관리하여 가장 오랫동안 사용되지 않은 페이지를 교체한다.&lt;/p&gt;&lt;p&gt;모든 페이지를 일렬로 순서화시켜야 하므로 최대 할당 페이지 프레임 수가 커지면 스택 관리 부담이 크다.&lt;/p&gt;

&lt;h4 id=&quot;nurnotusedrecently&quot;&gt;- NUR(Not Used Recently) 페이지 교체&lt;/h4&gt;

&lt;p&gt;비교적 최근에 사용되지 않은 페이지를 교체하는 방법으로, 모든 페이지 사이의 참조 순서를 일렬로 가르지 않고 참조된 등급과 참조되지 않은 등급으로 분류하는 방법을 사용해서 관리 부담을 줄였다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;운영체제는 주기적으로 모든 페이지의 참조 비트를 0으로 청소한다.&lt;/li&gt;

&lt;li&gt;하드웨어 참조가 있을 때마다 해당 페이지의 참조 비트를 1로 표시한다.&lt;/li&gt;

&lt;li&gt;페이지 교체가 필요하면 먼저 참조 비트가 0인 페이지 중에서 희생 페이지를 선택한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;참조 비트 외에 부가 비트를 사용하면 참조 그룹을 보다 세분화시킬 수 있다. 예를 들어, 여덟 개의 비트 중 최상위 비트를 참조 비트로 사용하고, 주기적으로 전체 비트를 오른쪽으로 한 비트씩 이동시키면 최근 8주기 동안 일어난 각 페이지에 대한 참조 패턴을 256(2^8)가지 등급으로 관리할 수 있다.&lt;/p&gt;&lt;p&gt;참조 비트는 하드웨어에 의해 쉽게 구현될 수 있으므로 현대 운영체제는 대부분 이 기법을 사용한다.&lt;/p&gt;

&lt;h2 id=&quot;4pagebuffering&quot;&gt;4. 페이지 버퍼링(Page Buffering)&lt;/h2&gt;

&lt;p&gt;몇 개의 가용 페이지 프레임을 미리 풀로 관리하고 있다가, 페이지 교체가 필요할 때 우선 풀에서 빈 페이지 프레임을 즉시 할당하고, 사후에 새로운 교체 페이지를 선택하여 디스크 기록 후 풀에 등록하는 것을 &lt;strong&gt;페이지 버퍼링&lt;/strong&gt;이라 한다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;페이지 버퍼링&lt;/strong&gt;은 풀에 등록된 몇 개의 페이지 프레임들에 대한 활용도는 낮아지지만 교체될 페이지의 디스크 저장과 새로운 페이지의 적재 사이에서 시간적 완충을 제공함으로써 페이지 교체에 따른 프로세스 지연 시간을 줄인다.&lt;/p&gt;

&lt;hr&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 64px; width: 64px; height: 64px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99B3514B600AA3C825&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99B3514B600AA3C825&quot; width=&quot;64&quot; height=&quot;64&quot; alt=&quot;virtualmemory&quot; filename=&quot;ram.png&quot; filemime=&quot;image/png&quot; style=&quot;width: 64px; height: 64px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>컴구 이야기</category>
      <category>운영체제</category>
      <category>컴퓨터구조</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/261</guid>
      <comments>https://boycoding.tistory.com/261#entry261comment</comments>
      <pubDate>Fri, 22 Jan 2021 19:07:53 +0900</pubDate>
    </item>
    <item>
      <title>8. 훑어보는 메모리 관리</title>
      <link>https://boycoding.tistory.com/260</link>
      <description>&lt;h1 id=&quot;&quot;&gt;메모리 관리&lt;/h1&gt;

&lt;p&gt;최초의 단일 프로그래밍 일괄처리 환경에서는 오로지 하나의 프로세스만이 메모리에 적재될 수 있었으므로 특별한 메모리 관리 정책이 필요하지 않았다. 그러나 이후의 모든 운영체제 유형의 기본이 되는 다중 프로그래밍 환경 즉, 메모리에 여러 개의 프로세스가 동시에 적재되는 시스템 환경에서는 어느 프로세스를 어느 부분에 어떻게 적재할 것인가 하는 메모리 관리 전략이 필요하게 됐다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;프로그램 전체 적재


&lt;ul&gt;
&lt;li&gt;연속 할당&lt;/li&gt;

&lt;li&gt;고정분할 할당&lt;/li&gt;

&lt;li&gt;가변분할 할당&lt;/li&gt;

&lt;li&gt;비연속 할당&lt;/li&gt;

&lt;li&gt;페이징&lt;/li&gt;

&lt;li&gt;세그먼테이션&lt;/li&gt;

&lt;li&gt;페이지화된 세그멘테이션&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;

&lt;li&gt;프로그램 일부 적재(가상 메모리)


&lt;ul&gt;
&lt;li&gt;연속 할당&lt;/li&gt;

&lt;li&gt;오버레이&lt;/li&gt;

&lt;li&gt;비연속 할당&lt;/li&gt;

&lt;li&gt;페이징&lt;/li&gt;

&lt;li&gt;페이지화된 세그먼테이션&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;-1&quot;&gt;- 전체 적재와 일부 적재, 그리고 가상 메모리&lt;/h3&gt;

&lt;p&gt;사용자가 작성한 프로그램은 텍스트(기계 명령어)와 데이터(전역 변수)로 구성되고, 실행되기 위해 메모리에 적재될 때는 이들 외에 지역 변수 및 함수 호출제어를 위한 스택 영역이 추가로 확보된다.&lt;/p&gt;&lt;p&gt;이때 메모리 관리 기법에 따라 텍스트와 데이터 전체가 적재되어야 하는 경우도 있고, 우선 일부만 적재된 후 참조 요구에 의해 필요한 부분이 점차 적재되기도 한다.&lt;/p&gt;&lt;p&gt;프로그램 전체가 적재되어야 하는 환경에서는 동시에 적재되는 프로그램들의 크기가 메모리 용량보다 적어야 하므로 다중 프로그래밍 정도가 낮아질 수밖에 없고, 메모리 용량보다 큰 프로그램은 실행될 수 없다.&lt;/p&gt;&lt;p&gt;반면에 프로그램 일부만 적재되어도 실행이 가능한 환경에서는 적재되어 실행 중인 프로그램들의 크기 총합이 실제 메모리 용량보다 훨씬 클 수 있다. 즉, 사용자에게 실제보다 큰 용량의 메모리를 제공하는 효과를 주게 되는데 이를 &lt;strong&gt;가상 메모리(Virtual Memory)&lt;/strong&gt;라고 한다.&lt;/p&gt;

&lt;h3 id=&quot;contiguousnoncontiguous&quot;&gt;- 연속(Contiguous) 할당과 비연속(Non-contiguous) 할당&lt;/h3&gt;

&lt;p&gt;프로그램의 텍스트와 데이터, 그리고 스택 영역 전체를 메모리의 연속된 공간에 적재하는 방법을 &lt;strong&gt;연속 할당&lt;/strong&gt;이라 하고, 메모리의 이곳저곳에 조금씩 나누어 적재하는 방법을 &lt;strong&gt;비연속 할당&lt;/strong&gt;이라고 한다.&lt;/p&gt;&lt;p&gt;연속 할당에서는 각 프로세스에 대한 위치 정보가 시작 주소와 크기 등 두 가지로 표시될 수 있지만, 비연속 할당에서는 프로그램이 분리된 파트별로 시작 주소와 크기가 관리되어야 한다.&lt;/p&gt;&lt;p&gt;| 프로세스 | 시작주소 | 크기  |
| -------- | -------- | ----- |
| P0       | 10,000   | 5,000 |
| P1       | 15,000   | 300   |
| P2       | 18,000   | 4,000 |&lt;/p&gt;&lt;p&gt;(a) 연속 할당&lt;/p&gt;&lt;p&gt;| 프로세스 | 파트 | 시작주소 | 크기  |
| -------- | ---- | -------- | ----- |
| P0       | 0    | 10,000   | 2,000 |
| P0       | 1    | 16,000   | 1,000 |
| P1       | 0    | 22,000   | 2,000 |&lt;/p&gt;

&lt;h2 id=&quot;1logicaladdressphysicaladdress&quot;&gt;1. 논리 주소와 물리 주소 (Logical Address, Physical Address)&lt;/h2&gt;

&lt;p&gt;C 언어 등 고급 프로그래밍 언어로 작성된 프로그램은 컴파일러와 어셈블러, 그리고 링커에 의해 하나의 완성된 목적 프로그램(실행 파일)으로 완성되는데, 이때 목적프로그램에서 명시적 혹은 묵시적으로 사용되고 있는 주소를 &lt;strong&gt;논리 주소&lt;/strong&gt;라고 한다. 기호로만 표시되었던 함수나 변수들이 목적 프로그램에서는 주소로 변환되는데 이 과정을 주소의 바인딩(Binding)이라 부르며, 이 주소가 곧 &lt;strong&gt;논리 주소&lt;/strong&gt;가 된다.&lt;/p&gt;&lt;p&gt;논리 주소와 달리, 프로그램이 실행되기 위해 적재된 실제 메모리의 주소를 &lt;strong&gt;물리 주소&lt;/strong&gt;라고 한다. &lt;/p&gt;&lt;p&gt;CPU는 메모리 접근을 위해 주소 버스에 설정한 주소값을 가로채서 적절한 물리 주소로 변환하고, 변환하는 부분을 &lt;strong&gt;MMU(Memory Management Unit)&lt;/strong&gt;라고 한다.&lt;/p&gt;&lt;p&gt;MMU에 의한 논리 주소와 물리 주소의 분리는 사용자 프로그래밍 과정에 매우 편리한 환경을 제공한다. 프로그래머는 자신의 프로그램이 메모리의 어느 곳에 적재될지에 걱정 없이 임의의 번지를 가정하고 목적 프로그램을 생산하면 된다.&lt;/p&gt;

&lt;h2 id=&quot;2&quot;&gt;2. 단일 프로그래밍 메모리 관리&lt;/h2&gt;

&lt;p&gt;초창기 단일 프로그래밍 일괄 처리 시스템은 메모리에 오직 하나의 프로세스만이 존재하므로 논리 주소와 물리 주소를 구분할 필요와 메모리의 이곳저곳에 나누어 분산 적재할 필요가 없다.&lt;/p&gt;&lt;p&gt;즉, 사용자 프로세스 영역으로 지정된 부분부터 연속적으로 할당하면 된다. 다만, 운영체제 영역을 보호하고, 메모리 용량을 초과하는 프로그램을 실행시킬 수 있는 다른 방안이 필요하다.&lt;/p&gt;

&lt;h4 id=&quot;brboundaryregister&quot;&gt;- 경계 레지스터(BR: Boundary Register)에 의한 운영체제 영역 보호&lt;/h4&gt;

&lt;p&gt;경계 레지스터에는 사용자 프로세스 영역과 운영체제 영역이 접하는 주소를 저장한다. 만약 사용자 프로세스가 경계 레지스터에 기록된 주소값을 벗어난 지점에 대한 참조를 시도하면 하드웨어 인터럽트가 발생하여 제어 흐름이 운영체제로 이동하고, 해당 프로세스는 운영체제에 의해 강제 종료된다.&lt;/p&gt;

&lt;h4 id=&quot;overlay&quot;&gt;- 오버레이(Overlay, 중첩)에 의한 메모리 용량 초과 프로그램의 실행&lt;/h4&gt;

&lt;p&gt;단일 프로그래밍 환경에서 프로그램의 크기가 메모리 영역을 초과하면 프로그램을 적절하게 분리하여, 필요할 때마다 분리된 부분을 같은 메모리 영역에 번갈아 적재해가면 실행하는 방법을 &lt;strong&gt;오버레이&lt;/strong&gt;라고 한다.&lt;/p&gt;&lt;p&gt;오버레이는 운영체제에 의해 자동으로 이루어지는 것이 아니고, 프로그래머의 세심한 설계에 따라 실행 중 요구하는 시스템 콜에 의해 이루어지므로 일종의 동적 &lt;strong&gt;적재(Dynamic Loading)&lt;/strong&gt;라 할 수 있다. 이를테면, 어셈블러 경우 전처리(Pre-process) 패스 1(Pass 1), 패스 2(Pass 2)로 구성되는데, 이들 세 부분을 차례차례 독립적으로 처리되므로 어셈블러 전체를 한꺼번에 적재하지 않아도 무방하다.&lt;/p&gt;

&lt;h2 id=&quot;3fixedpartitionvariblepartition&quot;&gt;3. 고정 분할과 가변 분할(Fixed Partition, Varible Partition)&lt;/h2&gt;

&lt;p&gt;여러 개의 프로그램을 동시 적재하는 다중 프로그래밍을 위해서는 무엇보다 먼저 메모리의 분할이 필요하다.&lt;/p&gt;

&lt;h3 id=&quot;-2&quot;&gt;고정 분할&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;고정 분할&lt;/strong&gt;이란 메모리를 미리 고정적으로 분할시켜 놓고, 공간이 비워질 때마다 새로운 프로그램을 적재하는 방법이다.&lt;/p&gt;

&lt;h4 id=&quot;-3&quot;&gt;- 운영체제 및 다른 프로세스 보호&lt;/h4&gt;

&lt;p&gt;각 분할의 위치는 시작 주소와 끝 주소로 명시되므로, 어느 분할에서 실행 중인 프로세스는 해당 분할의 범위를 벗어난 영역을 참조할 수 없다. 단일 프로그래밍의 경계 레지스터와 동일한 개념으로 분할의 상/하한. 경계 레지스터를 사용하여 운영체제 및 다른 프로세스를 보호할 수 있다.&lt;/p&gt;

&lt;h4 id=&quot;-4&quot;&gt;- 분할의 크기&lt;/h4&gt;

&lt;p&gt;고정 분할의 가장 큰 단점은 사용자 프로그램의 크기에 꼭 맞는 메모리 공간을 제공할 수 없으므로 메모리 일부를 활용하지 못하는 데 있다. 이와 같은 현상을 &lt;strong&gt;내부 단편화(Internal Fragmentation)&lt;/strong&gt;라고 부른다. &lt;/p&gt;&lt;p&gt;고정 분할의 또 다른 문제는, 가장 큰 분할에조차 적재될 수 없는 큰 프로그램은 실행될 수 없다는 점이다. 이를 해결하기 위해 인접한 여러 개의 분할을 일시적으로 통학하여 적재하는 방안이 있는데, 이는 어디까지나 인접한 분할들이 모두 비어있는 경우에만 가능하므로 큰 프로그램은 기아 상태가 초래된다.&lt;/p&gt;&lt;p&gt;분할의 크기는 동시에 적재할 수 있는 프로세스의 수에도 영향을 미친다. 다중 프로그래밍의 정도를 높이기 위해 분할을 여러 개 만들면 분할의 크기가 작아지고, 반대로 분할의 크기를 높이면 다중 프로그래밍 정도가 낮아질 수밖에 없다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;고정 분할은 관리가 간단하지만, 내부 단편화에 의한 메모리 낭비를 피할 수 없고, 실행 가능한 프로그램의 최대 크기가 제한된다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;-5&quot;&gt;가변 분할&lt;/h3&gt;

&lt;p&gt;고정 분할의 내부 단편화 문제를 해결하기 위해 비어있는 공간을 정확히 프로그램의 크기만큼만을 할당하는 방법이 있는데 이를 &lt;strong&gt;가변 분할&lt;/strong&gt;이라고 한다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;가변 분할&lt;/strong&gt;에서는 분할의 개수 및 시작과 끝이 고정되어 있지 않고, 프로세스의 종료 및 적재가 일어날 때마다 변한다. &lt;/p&gt;&lt;p&gt;그러나 할당과 반납이 진행되면서 크기가 아주 작은 빈 분할들이 늘어나 이들이 비어 있는데도 불구하고 활용되지 못하는 상황이 발생한다. 이처럼 빈 공간이 있지만 크기가 너무 작아서 할당되지 못하는 현상을 &lt;strong&gt;외부 단편화(External Fragmentation)&lt;/strong&gt;이라 부르고, 이러한 작은 분할을 &lt;strong&gt;홀(Hole)&lt;/strong&gt;이라 한다.&lt;/p&gt;&lt;p&gt;외부 단편화 문제를 완화하기 위해서는 적절한 &lt;strong&gt;할당 정책&lt;/strong&gt;, 인접한 빈 공간의 &lt;strong&gt;병합(Coalescing)&lt;/strong&gt;, 빈 공간 전체의 &lt;strong&gt;통합(Compaction)&lt;/strong&gt; 등의 조치가 필요하다.&lt;/p&gt;

&lt;h4 id=&quot;-6&quot;&gt;- 가변 분할의 할당 정책&lt;/h4&gt;

&lt;p&gt;할당 정책은 다양한 크기의 비어있는 분할들이 있을 때 어느 분할에서 요구 메모리를 할당하는 것인가를 선택하는 문제다.&lt;/p&gt;

&lt;h5 id=&quot;bestfit&quot;&gt;최적 적합(Best-fit) 할당:&lt;/h5&gt;

&lt;p&gt;분할의 크기가 할당 요구 크기에 가장 근접하는 분할을 선택하여 할당 방법이다. 할당하고 남은 부분이 아주 작은 분할이 되어, 결국 새로운 작은 빈 분할을 낳는다.&lt;/p&gt;

&lt;h5 id=&quot;worstfit&quot;&gt;최악 적합(Worst-fit) 할당:&lt;/h5&gt;

&lt;p&gt;최적 적합과는 반대로 가장 큰 분할에서 할당하는 방법이다. 큰 분할부터 계속 작은 분할로 변해간다는 단점이 있다.&lt;/p&gt;

&lt;h5 id=&quot;firstfit&quot;&gt;최초 적합(First-fit) 할당:&lt;/h5&gt;

&lt;p&gt;빈 분할의 크기와는 관계없이 무조건 할당 요구를 만족하는 최초의 분할에서 할당하는 전략으로, 랜덤 효과를 얻으면서도 검색 부담을 줄이는 장점이 있다.&lt;/p&gt;

&lt;h5 id=&quot;nextfit&quot;&gt;순차 적합(Next-fit) 할당:&lt;/h5&gt;

&lt;p&gt;최초 적합 할당에서 검색은 매번 메모리 앞부분의 동일한 부분부터 이루어지므로 작은 분할들이 더 많이 발생할 가능성이 있다.&lt;/p&gt;&lt;p&gt;순차 적합은 랜덤 할당 성질을 더욱 강화하여 할당이 이루어진 분할을 기록해두었다가 다음번 검색은 바로 그다음 분할부터 이루어지도록 하는 방법이다.&lt;/p&gt;

&lt;h4 id=&quot;coalescing&quot;&gt;- 인접한 빈 분할들의 병합(Coalescing)&lt;/h4&gt;

&lt;p&gt;할당되었던 분할이 반납되어 빈 할당이 되었을 때, 앞이나 뒤에 인접한 빈 분할이 존재한다면 이들 분할들을 &lt;strong&gt;병합&lt;/strong&gt;하여 하나의 큰 빈 분할로 만들어 관리한다면 다음번 할당 시 외부 단편화 문제를 줄일 수 있다.&lt;/p&gt;

&lt;h4 id=&quot;compaction&quot;&gt;- 분산된 빈 분할들의 통합(Compaction)&lt;/h4&gt;

&lt;p&gt;인접한 빈 분할들을 모두 통합했는데도 불구하고 빈 분할들이 여러 곳에 분산되어 있으면 외부 단편화 문제로 메모리 활용률이 크게 저하된다. 이를 해결하기 위해서는 할당된 분할과 빈 분할들의 위치를 바꾸어 빈 분할들을 큰 분할로 병합해야 하는데 이를 &lt;strong&gt;통합&lt;/strong&gt;이라 부른다.&lt;/p&gt;&lt;p&gt;통합에 있어서 가장 큰 부담은 할당된 분할 즉, 프로세스의 적재 위치가 변경됨에 따라 재배치가 이루어져야 한다는 점인데 MMU에 의해 쉽게 해결된다.&lt;/p&gt;

&lt;h2 id=&quot;4paging&quot;&gt;4. 페이징(Paging)&lt;/h2&gt;

&lt;p&gt;프로그램을 원래 모양 그대로 메모리에 적재하는 환경에 적합한 고정 분할이나 가변 분할과는 달리, 프로그램을 여러 개의 조각으로 분리하여 메모리에 분산하여 적재하는 &lt;strong&gt;페이징&lt;/strong&gt;과 &lt;strong&gt;세그먼테이션&lt;/strong&gt;이 있다.&lt;/p&gt;&lt;p&gt;이 방법들은 프로그램을 분리하여 분산 적재하므로 비연속 할당 방법이고, 페이징과 세그먼테이션의 차이점은 프로그램을 조각으로 분리하는 방법에 있다.&lt;/p&gt;&lt;p&gt;페이징은 프로그램 전체를 일정한 크기의 작은 조각으로 분리하여 메모리에 적재한다. 이때 각각의 조각을 &lt;strong&gt;페이지(Page)&lt;/strong&gt;라 하고, 페이지는 시스템에 따라 보통 512~8K 바이트 크기를 가진다.&lt;/p&gt;&lt;p&gt;페이징에서는 프로그램을 페이지 단위로 분리하여 적재하므로 메모리 할당 단위 또한 페이지 단위로 이루어져야 한다. 즉, 메모리가 페이지 크기와 동일한 조각으로 분할되어야 하는데 그 각각을 &lt;strong&gt;페이지 프레임(Page Frame)&lt;/strong&gt;이라 부른다.&lt;/p&gt;&lt;p&gt;페이지들로 분리된 프로그램은 메모리 이곳저곳에 산재하는 빈 페이지 프레임들에 적재되므로, 특정 프로그램이 적재된 메모리 정보는 페이지들과 페이지 프레임들의 대응 관계가 표로 관리되어야 하는데 그 표를 &lt;strong&gt;페이지 테이블(Page Table)&lt;/strong&gt;이라 부른다.&lt;/p&gt;

&lt;h4 id=&quot;-7&quot;&gt;논리 주소의 물리 주소 변환&lt;/h4&gt;

&lt;p&gt;프로그램의 시작 주소가 0이라면 프로그램이 적재된 직후 운영체제는 이 프로세스에게 CPU의  PC를 0으로 설정하여 CPU를 할당한다. CPU는 첫 명령어를 인출하기 위해 논리 주소 0을 주소 버스에 올리고 메모리 접근을 시도할 것인데, 이때 MMU가 페이지 테이블을 이용하여 대응되는 물리 주소로 변환한다.&lt;/p&gt;

&lt;h4 id=&quot;mmu&quot;&gt;성능 개선을 위한 페이징 MMU 구조&lt;/h4&gt;

&lt;p&gt;페이징 MMU의 가장 큰 문제점은 메모리 접근을 하기 전에 물리 주소를 얻기 위해 반드시 페이지 테이블을 참조한다는 것이다. 즉, 페이지 테이블 또한 메모리에 존재하므로 결국 메모리에 접근할 때마다 두 번의 메모리 참조가 이루어져 성능이 1/2로 반감된다는 것이다.&lt;/p&gt;&lt;p&gt;이를 해결하기 위해 물리 주소 변환 과정에 고속의 캐시 메모리 소자로 구성된 하드웨어 연관 메모리(associative memroy) 구조를 사용한다. 연관 메모리란 저장된 내용을 주소에 의해 읽는 것이 아니라, 저장된 내용 일부와 일치하는 내용을 병렬 검색 회로에 의해 읽어 낸다. (캐싱)&lt;/p&gt;

&lt;h4 id=&quot;-8&quot;&gt;페이징 시스템 분석&lt;/h4&gt;

&lt;p&gt;프로그램의 크기가 정확하게 페이지 크기의 배수가 된다는 보장이 없으므로 마지막 페이지에서 약간의 내부 단편화가 발생한다. 이는 페이지의 크기가 클수록 심화된다. 반대로 이를 해결하기 위해 페이지 크기를 줄이면 페이지 테이블을 위한 메모리가 더 많이 사용된다는 단점이 있다.&lt;/p&gt;

&lt;h4 id=&quot;-9&quot;&gt;페이징 시스템에서의 보호와 공유&lt;/h4&gt;

&lt;p&gt;페이징 시스템 구조 자체의 특성상 어느 프로세스도 자신의 메모리 영역을 벗어나 운영체제네 다른 프로세스의 메모리 영역을 침범할 수 없다. 즉, 페이지 테이블에 연결된 페이지 프레임 외에는 주소 변환이 결코 일어나지 않는다는 것이다.&lt;/p&gt;&lt;p&gt;페이지 테이블을 적절하게 잘 구성하면 내부 페이지들에 대한 세밀한 보호 모드를 설정하여 운영할 수 있다. 예를 들어 읽기(r), 쓰기(w), 실행(x) 등의 보호 비트 3개를 추가하여 페이지에 가능 여부를 표시하면 허용되지 않은 참조 접근을 차단할 수 있다.&lt;/p&gt;&lt;p&gt;일반적으로 프로그램의 기계 명령어 부분(텍스트)에는 읽기와 실행만 허용하고, 변수(데이터)나 스택 부분은 읽기와 쓰기만 허용함으로써 해킹 행위등을 예방한다.&lt;/p&gt;&lt;p&gt;또한 페이징 시스템에서는 페이지의 공유가 매우 용이하다. 페이지의 공유란 두 프로세스가 동일한 페이지를 동시에 참조하는 것을 말한다. 예를 들어 인턴넷 브라우져등 동일한 프로그램을 여러 번 실행하여 적재하는 경우 기계 명령어는 모두 동일하고 실행 도중 내내 변경도 일어나지 않으므로, 각각 따로 적재하지 않고 한 벌만 적재하여 공유하면 메모리를 크게 절약할 수 있다.&lt;/p&gt;

&lt;h2 id=&quot;5segmentation&quot;&gt;5. 세그먼테이션 (Segmentation)&lt;/h2&gt;

&lt;p&gt;사용자 프로그램을 조각으로 분리하여 메모리에 분산해서 적재하고, MMU가 매핑 테이블을 이용해 논리 주소를 물리 주소를 변환한다는 개념에서 세그먼테이션과 페이지는 같다.&lt;/p&gt;&lt;p&gt;그러나 세그먼테이션은 사용자 프로그램을 균일한 작은 조각(페이지)으로 분리하는 것이 아니고, 프로그램 이미지를 구성 부위별 특성에 따라 분리한다는 점에서 다르다. 이때 같은 특성으로 구별되는 묶음들을 &lt;strong&gt;세그먼트&lt;/strong&gt;라 부른다.&lt;/p&gt;&lt;p&gt;보통 프로그램 이미지는 텍스트(기계 명령어), 데이터(전역 변수), 스택(지역 변수 및 서브 루틴 호출 관리), 공유 라이브러리 등 최대 7~8개의 세그먼트로 구성된다. 세그먼트는 프로그램 이미지를 논리적인 특성을 기준으로 분리한 단위를 의미하므로 하나의 큰 덩어리라 말할 수 있고, 그들의 크기는 각각 다르고 비교적 크다.&lt;/p&gt;

&lt;h4 id=&quot;-10&quot;&gt;세그먼트의 보호 및 공유&lt;/h4&gt;

&lt;p&gt;페이징에서와 마찬가지 방법으로 세그멘트 테이블을 이용하면 세그먼트의 보호와 공유를 쉽게 구현할 수 있다.&lt;/p&gt;&lt;p&gt;예를 들어, 유닉스의 vi 같은 편집기가 두 번 실행 되었을 때, 편집기 프로그램의 이미지는 텍스트, 데이터 , 스택 등 3개의 세그먼트로 구성되어 있다. 이 중 텍스트(기계 명령어)는 두 프로세스 모두 같고 변경이 없으므로 공유되고 있다. 반면에 데이터와 스택은 사용자가 편집을 진행하는 과정에서 각각 고유하게 변경되어 가므로 각각 독립적으로 할당되어야 한다.&lt;/p&gt;

&lt;h4 id=&quot;-11&quot;&gt;세그먼테이션 분석&lt;/h4&gt;

&lt;p&gt;페이징에 대비되는 세그먼테이션의 가장 눈에 띄는 점은 프로그램의 이미지를 구성 부위별에 따라 크기가 제각각인 3~8개의 세그먼트로 분리하여 적재하므로 세그먼트 테이블에 할당 주소와 길이가 관리되어야 하고, 세그먼트 테이블 항목이 소수여서 테이블의 자체의 크기가 작다는 점이다.&lt;/p&gt;&lt;p&gt;또한, 세그멘테이션에서는 메모리를 정확히 세그먼트 크기에 맞추어 할당하므로 내부 단편화가 발생하지 않는다. 그러나, 세그먼트별 메모리 할당이 가변 할당 방식으로 이루어지므로 홀에 의한 외부 단편화 문제가 심각하게 나타날 수 있다.&lt;/p&gt;

&lt;h2 id=&quot;5segmentationwithpaging&quot;&gt;5. 페이지화 된 세그먼테이션(Segmentation with Paging)&lt;/h2&gt;

&lt;p&gt;세그먼테이션의 가장 큰 문제점 중 하나는 각각의 세그먼테이션을 가변 분할 정책으로 연속적으로 할당함으로써 피할 수 없는 외부 단편화 문제이다. 반면에 페이징에서는 프로그램 이미지가 너무 작은 조각들로 분리되어 페이지별 보호 등의 관리를 위한 부담이 크다. &lt;/p&gt;&lt;p&gt;이와 같은 두 시스템의 단점을 보완하는 방법으로 논리적 특성에 따라 분리된 세그먼트를 다시 페이지 단위로 분리하여 적재하는 &lt;strong&gt;페이지화된 세그먼테이션&lt;/strong&gt; 시스템이 도입되었다. 유닉스 등 현대 범용 운영체제는 거의 모두 이 방법을 쓴다.&lt;/p&gt;&lt;p&gt;페이지화된  세그먼테이션의 페이지 테이블은 &lt;s, p=&quot;&quot; ,=&quot;&quot; f=&quot;&quot;&gt;로 구성되는데, 세그먼트 번호 s와 페이지 번호 p는 검색 키로, 그리고 페이지 프레임 번호 f는 출력값으로 사용된다.&lt;/s,&gt;&lt;/p&gt;

&lt;hr&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 64px; width: 64px; height: 64px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99E488505FB1B4D909&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99E488505FB1B4D909&quot; width=&quot;64&quot; height=&quot;64&quot; alt=&quot;memory&quot; filename=&quot;ram-memory.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 64px; height: 64px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>컴구 이야기</category>
      <category>운영체제</category>
      <category>컴퓨터구조</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/260</guid>
      <comments>https://boycoding.tistory.com/260#entry260comment</comments>
      <pubDate>Mon, 16 Nov 2020 08:08:42 +0900</pubDate>
    </item>
    <item>
      <title>7. 훑어보는 프로세스 동기화</title>
      <link>https://boycoding.tistory.com/259</link>
      <description>&lt;h1 id=&quot;&quot;&gt;프로세스 동기화&lt;/h1&gt;

&lt;p&gt;현대 운영체제는 모두 다수의 프로세스를 적재하여 처리하는 다중 프로그래밍 환경이다. 다중 프로그래밍 환경에서 프로세스들은 기본적으로 엄격하게 분리되어 상호 간섭이 불가능하여 비동기적으로 진행되지만, 프로세스들이 메모리를 공유하거나(공유 메모리) 다중 스레드를 지원하는 경우, 여러 개의 프로세스(스레드 포함)들이 변수 등 같은 자원에 대한 접근을 경쟁하는 상황이 일어날 수 있어 동기화가 필요하다.&lt;/p&gt;&lt;p&gt;접근 경쟁 상황은 병행 처리와 병렬 처리 두 가지 처리 형태에서 나타난다.&lt;/p&gt;

&lt;h2 id=&quot;1&quot;&gt;1. 병행 처리와 경쟁 상황&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;병행 처리(Concurrent Processing)&lt;/strong&gt;: 하나의 CPU가 여러 개의 프로세스를 조금씩 빠른 속도로 번갈아 처리함으로써 거시적 관점에서 여러 개의 프로세스를 한꺼번에 처리하는 효과가 있다.&lt;/p&gt;&lt;p&gt;즉, 병행 처리에서는 어느 순간에 살펴보면 오직 한 프로세스만 실행된다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;공유 변수 A = 5

CPU: P0 -&amp;gt; P1

P0: b = 3, c = 4, d = 2
A = A + b * c - d;   (A = 15)

P1: x = 2, y = 3, z = 2
A = A - x * y + z; (A = 11)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위에서 공유 변수 A를 참조하는 두 프로세스 P0과 P1에 의해 변수 A의 최종값은 11이어야 한다. 그런데, 아래 시나리오같이 CPU 스케줄링이 이루어지면 A의 최종값이 잘못 계산된다.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;P0이 A의 현재 값 5를 사용하여 A + b * c 의 계산을 마치자마자(중간값: 17) CPU 스케줄링으로 인해 CPU가 P1에 할당된다.&lt;/li&gt;

&lt;li&gt;P1은 아직 변하지 않은 A 값 5를 참조하여 계산을 마치고 A 값을 1로 변경한다.&lt;/li&gt;

&lt;li&gt;나중에 P0이 다시 할당되면, 이전에 중단되었던 수식 계산을 계속하여 이전의 중간값 17 - d를 계산하여 나온 15를 A에 저장함으로써 결국 A는 원하는 값 11이 아닌 15가 남게 된다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;이런 &lt;strong&gt;병행 처리 경쟁 상황(Race Condition)&lt;/strong&gt;은 공유 변수가 포함된 수식 계산을 완전히 마치지 못한 상태에서 CPU 스케줄링이 일어날 때 발생한다.&lt;/p&gt;

&lt;h2 id=&quot;2&quot;&gt;2. 병렬 처리와 경쟁 상황&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;병렬 처리(Parallel Processing)&lt;/strong&gt;: 두 개 이상의 CPU가 여러 프로세스를 분담하여 독립적으로 동시에 처리하는 형태를 말하는데, 어느 순간에 CPU 개수만큼의 프로세스들이 CPU를 할당받아 실행되고 있다.&lt;/p&gt;&lt;p&gt;이 같은 병렬 처리 환경에 공유 변수를 참조하는 프로세스들이 존재한다면 병행 처리 환경에서와 유사한 경쟁 상황이 발생한다.&lt;/p&gt;&lt;p&gt;예를 들어, P0과 P1이 각각 CPU0과 CPU1에 각각 할당되어 병렬 처리 중이고, 양쪽 모두 두 수식 부분을 계산 중이라면, 병행 처리와 같은 문제가 발생한다. 또한. P0과 P1의 수식이 끝나는 순서에 따라 공유 변수 A의 최종값이 달라진다.&lt;/p&gt;&lt;p&gt;즉, &lt;strong&gt;병렬 처리 경쟁상황&lt;/strong&gt;은 병렬 처리 환경에서 공유 변수를 참조하는 프로세스들이 처리될 때 발생한다.&lt;/p&gt;

&lt;h2 id=&quot;3&quot;&gt;3. 임계 영역과 상호 배제&lt;/h2&gt;

&lt;p&gt;경쟁 상황이 발생할 수 있는 부분은 어느 쪽이든 먼저 도착한 프로세스가 계산을 모두 마치고 공유 변수값을 최종 변경할 때까지 다른 프로세스는 수식에 대한 계산 시작을 연기하고 기다려야만 올바른 계산 결과가 보장된다.&lt;/p&gt;&lt;p&gt;이처럼 경쟁 상황이 발생할 수 있는 부분들을 합쳐서 &lt;strong&gt;임계 영역(Critical Section)&lt;/strong&gt;이라 부르고, 임계 영역은 어떤 경우에도 오직 하나의 프로세스만이 실행할 수 있도록 제한하는 개념을 &lt;strong&gt;상호 배제(Mutual Exclusion)&lt;/strong&gt;라 한다. 상호 배제에 기반을 둔 프로세스 진행을 &lt;strong&gt;프로세스 동기화(Process Synchronization)&lt;/strong&gt;라 한다.&lt;/p&gt;&lt;p&gt;상호배제는 크게 소프트웨어 방법과 하드웨어 방법으로 구현할 수 있다.&lt;/p&gt;

&lt;h2 id=&quot;4&quot;&gt;4. 소프트웨어 상호 배제 방법&lt;/h2&gt;

&lt;h4 id=&quot;dekkersalgorithm&quot;&gt;- Dekker's Algorithm&lt;/h4&gt;

&lt;h4 id=&quot;pertersonsalgorithm&quot;&gt;- Perterson's Algorithm&lt;/h4&gt;

&lt;h4 id=&quot;lamportsbakeryalgorithm&quot;&gt;- Lamport's Bakery Algorithm&lt;/h4&gt;

&lt;h2 id=&quot;5&quot;&gt;5. 상호 배제를 위한 하드웨어 지원&lt;/h2&gt;

&lt;p&gt;상호 배제를 위한 소프트웨어 기법들이 복잡하고 어려운 이유는 컴퓨터 하드웨어 구조상, 메모리에 존재하는 특정 변수에 대하여 CPU가 &quot;메모리 읽기 - 판단 - 메모리 저장&quot; 과정을 거치는 동안에 다른 프로세스가 중간에 끼어들어 해당 변수를 참조할 수 있기 때문이다.&lt;/p&gt;&lt;p&gt;즉, 일반적인 기계 명령어는 메모리에서 값을 읽자마자 메모리 버스를 놓아주므로 다른 프로세스의 메모리 접근이 가능한 것이다. 따라서 중간에 끼어들기를 허용하지 않는 &quot;메모리 읽기 ~ 저장&quot; 기계 명령어가 제공된다면 상호 배제는 아주 쉽게 이루어질 수 있다.&lt;/p&gt;&lt;p&gt;이런 기계 명령어는 &quot;읽기 ~ 저장&quot;하는 단계 동안 메모리 버스를 놓지 않을 뿐만 아니라, 하나의 기계 명령어이므로 이 사이에 인터럽트 당할 염려도 없다.&lt;/p&gt;

&lt;h4 id=&quot;tastestset&quot;&gt;- TAS(Test &amp;amp; Set)&lt;/h4&gt;

&lt;h4 id=&quot;swap&quot;&gt;- SWAP&lt;/h4&gt;

&lt;h2 id=&quot;6semaphores&quot;&gt;6. 세마포어(Semaphores)&lt;/h2&gt;

&lt;p&gt;앞에서 제안된 상호 배제 기법들은 모두 바쁜 대기(Busy Waiting)를 기반으로 임계 영역을 보호한다. 이러한 잠금장치를 스핀 록(Spin Lock)이라 부르는데, 임계 영역이 길면 스핀 록에 의한 CPU 사용 낭비가 심하고, 임계 영역에 진입한 프로세스가 CPU 스케줄링에 의해 스위칭 되거나 비정상적으로 종료하면 이에 따른 파급효과가 크다는 단점이 있다. 이를 개선할 수 있는 보다 보편적 개념의 잠금장치 세마포어가 출현했다.&lt;/p&gt;&lt;p&gt;세마포어는 추상적 잠금장치로 잠금 연산 &lt;code&gt;P()&lt;/code&gt;와 해제 연산 &lt;code&gt;V()&lt;/code&gt;에 의해 접근 가능한 정수 변수 &lt;code&gt;S&lt;/code&gt;를 일컫는다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;P(S) // 잠금
{
  if(S &amp;gt; 0)
    S = S - 1;
  else
    V() 연산에서 깨울 때까지 세마포어 큐에서 대기;

  return;
}

V(S) // 해제
{
  if(세마포어 큐가 비어있으면)
    S = S + 1;
  else
    세마포어 큐의 한 프로세스를 깨워줌(대기-&amp;gt;준비);

  return;
}

S = 1(세마포어 정수 변수), A = 5(공유 변수);

Process 0:
b = 1, c = 2, d = 3;
...
P(S);
A = A - b * c - d;
V(S);
...


Process 1:
e = 2, f = 3;
...
P(S);
a = a + e - f;
V(S)
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;세마포어 S에 대한 연산 &lt;code&gt;P()&lt;/code&gt;와 &lt;code&gt;V()&lt;/code&gt; 각각의 처리 절차 전체는 하나의 임계 영역으로 이루어져 원자적으로 처리된다. 즉, &lt;code&gt;P()&lt;/code&gt;와 &lt;code&gt;V()&lt;/code&gt; 연산을 합쳐서 이들은 어느 한 프로세스만이 진입하여 실행할 수 있다.&lt;/p&gt;&lt;p&gt;세마포어에서 변수 &lt;code&gt;S&lt;/code&gt;는 공통 깃발을 역할을 하고, 연산 &lt;code&gt;P(S)&lt;/code&gt;는 잠겼다는 의미로 깃발 &lt;code&gt;S&lt;/code&gt;를 내리려는 행위(Probern)이며, 연산 &lt;code&gt;V(S)&lt;/code&gt;는 열렸다는 의미로 깃발 S를 올리려는 행위다.&lt;/p&gt;&lt;p&gt;만약 두 프로세스가 거의 동시에 &lt;code&gt;P(S)&lt;/code&gt; 연산을 시도했다면, 이들 중 하나는 깃발을 내려놓고 진행을 계속하지만, 다른 한쪽은 내릴 깃발이 없으므로 세마포어 큐에서 대기한다. 이 프로세스 다음에 &lt;code&gt;P(S)&lt;/code&gt;를 시도하는 모든 프로세스도 마찬가지로 세마포어 큐에서 대기한다.&lt;/p&gt;&lt;p&gt;&lt;code&gt;P(S)&lt;/code&gt; 연산에서 성공하여 임계 영역을 마친 프로세스는 깃발을 다시 올려놓기 위해 &lt;code&gt;V(S)&lt;/code&gt; 연산을 해주어야 하는데, 이때 &lt;code&gt;V(S)&lt;/code&gt; 연산은 세마포어에서 대기하는 프로세스가 있다면 깃발은 그대로 두고 하나의 프로세스를 선택하여 준비 상태로 만들어 줌으로써 &lt;code&gt;P(S)&lt;/code&gt; 연산을 성공하고 임계 영역에 진입하도록 한다.&lt;/p&gt;&lt;p&gt;세마포어는 임계 영역 집일을 위해 대기하는 동안 바쁜 대기를 하지 않으므로 CPU 사용 낭비를 줄일 수 있으나, &lt;code&gt;P(S)&lt;/code&gt; 연산에서의 대기 상태 전환으로 인한 CPU 스케줄링이 요구되므로 문맥 교환(Context Switch) 부담이 따른다. 따라서 스핀 록과 세마 포어 사이의 선택은 임계 영역 크기와 성격에 따라 신중하게 이루어져야 한다.&lt;/p&gt;

&lt;hr&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 64px; width: 64px; height: 64px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99D954495F957DAD03&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99D954495F957DAD03&quot; width=&quot;64&quot; height=&quot;64&quot; alt=&quot;process&quot; filename=&quot;planning.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 64px; height: 64px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>컴구 이야기</category>
      <category>운영체제</category>
      <category>컴퓨터구조</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/259</guid>
      <comments>https://boycoding.tistory.com/259#entry259comment</comments>
      <pubDate>Sun, 25 Oct 2020 22:29:56 +0900</pubDate>
    </item>
    <item>
      <title>6. 훑어보는 CPU 스케줄링</title>
      <link>https://boycoding.tistory.com/258</link>
      <description>&lt;h1 id=&quot;cpu&quot;&gt;CPU 스케줄링&lt;/h1&gt;

&lt;p&gt;일반적으로 컴퓨터 시스템에서 프로세서(Processor)라 함은 주기억 장치에서 저장된 프로그램(기계 명령어) 읽어가면서 처리하는 CPU를 일컫는다.&lt;/p&gt;&lt;p&gt;운영체제의 프로세서 관리는 다수의 프로세스가 준비 상태에 있을 때, CPU가 어느 프로세스를 먼저 처리하도록 할 것인가를 결정하기 위한 제반 사항을 총칭하고, &lt;strong&gt;CPU 스케줄링(CPU Scheduling)&lt;/strong&gt;이라 부르기도 한다.&lt;/p&gt;

&lt;h2 id=&quot;1&quot;&gt;1. 단계별 처리 스케줄링&lt;/h2&gt;

&lt;p&gt;컴퓨터는 여러 개의 프로세스가 동시에 존재하므로 각각의 프로세스를 식별하거나 관리하기 위해서는 아래와 같은 여러 가지 단계들이 필요하다.&lt;/p&gt;

&lt;h4 id=&quot;1longtermscheduling&quot;&gt;1. 장기 스케줄링(Long-term Scheduling)&lt;/h4&gt;

&lt;p&gt;장기 스케줄링 단계에서는 어느 프로그램을 주기억 장치에 먼저 적재할 것인가를 결정한다. 현대 운영체제의 장기 스케줄링에서는 운영체제에 등록된 프로세스 수의 적정성 여부에 따라 프로그램의 적재를 허용하거나 거부한다. 이런 이유로 장기 스케줄링을 잡 스케줄링(Job Scheduling)이라고도 한다.&lt;/p&gt;

&lt;h4 id=&quot;2midiumtermscheduling&quot;&gt;2. 중기 스케줄링(Midium-term Scheduling)&lt;/h4&gt;

&lt;p&gt;중기 스케줄링 단계에서는 컴퓨터 자원의 고갈 등의 이유로, 적재된 프로세스 중 일부에 대하여 상당 기간 동안 처리를 보류시켜야 할 때, 여러 가지 요인을 고려하여 보류 대상 프로세스(들)를 선정한다.&lt;/p&gt;&lt;p&gt;운영체제는 처리가 보류된 프로세스들을 처리 재개가 가능할 때까지 주기억장치에서 하드 디스크로 옮기는데, 이 과정을 스왑 아웃(Swap out)이라고 부르고, 반대를 스왑 인(Swap In)이라 한다.&lt;/p&gt;

&lt;h4 id=&quot;3shorttermscheduling&quot;&gt;3. 단기 스케줄링(Short-term Scheduling)&lt;/h4&gt;

&lt;p&gt;단기 스케줄링은 운영체제가 다수의 준비 상태 프로세스 중에서 CPU를 점프 시켜 실행시킬 프로세스를 선택하는 과정을 말한다. 보통 &lt;strong&gt;CPU 스케줄링&lt;/strong&gt;이라고 하면 바로 이 단계를 말한다.&lt;/p&gt;&lt;p&gt;CPU 스케줄링은 그 전략에 따라 다르겠지만, 1초에 수십 ~ 수백 번 이루어질 수도 있다. CPU 스케줄러는 결국 프로세스들에 CPU를 배분하는 역할을 담당하므로 이를 &lt;strong&gt;디스패처(Dispatcher)&lt;/strong&gt;라 부르기도 한다.&lt;/p&gt;

&lt;h2 id=&quot;2cpu&quot;&gt;2. CPU 스케줄링 전략의 목표 및 기준&lt;/h2&gt;

&lt;p&gt;운영체제에서 구현되는 CPU 스케줄러의 기본 목표 중 하나는 사용자 프로세스들의 기다리는 시간을 줄여, 가능한 한 신속하게 처리될 수 있도록 하는 것이다.&lt;/p&gt;&lt;p&gt;이 외에도, 목표 설정의 기준은 크게 사용자 관점과 시스템 관점으로 분류할 수 있다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;사용자 관점에서의 CPU 스케줄링 목표 지표:


&lt;ul&gt;
&lt;li&gt;응답 시간(Response Time): 사용자 데이터 입력 후, 출력이 이루어질 때까지의 소요 시간&lt;/li&gt;

&lt;li&gt;반환 시간(Turnaround Time): 프로그램 제출(혹은 시작) 후, 끝날 때까지 소요되는 총 시간&lt;/li&gt;

&lt;li&gt;대기 시간(Waiting Time): 프로세스들이 준비 상태로 대기열에서 기다린 시간의 총합&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;

&lt;li&gt;시스템 관점에서의 CPU 스케줄링 목표 지표:


&lt;ul&gt;
&lt;li&gt;CPU 이용률(CPU Utilization): 총 경과 시간 대비 CPU가 순수하게 사용자 프로세스를 수행한 시간의 비&lt;/li&gt;

&lt;li&gt;처리량(Throughput): 단위 시간당 처리된 프로세스의 개수&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;사용자 관점과 시스템 관점에서의 CPU 스케줄링 목표 지표들 사이에는 상호 배타적인 관계가 존재한다. 예를 들어, 사용자에게 짧은 응답 시간을 제공하기 위해서는 시간을 작게 나누어서 프로세스들에게 CPU를 가급적 자주 골고루 할당해주어야 하는데, 이럴 경우 운영체제의 스케줄링이 빈번하게 실행하게 되어 결국은 CPU 이용률과 처리율이 낮아진다.&lt;/p&gt;&lt;p&gt;따라서 CPU 스케줄링에는 다양한 주변 여건을 고려하여 적절한 전략이 적용되어야 한다.&lt;/p&gt;

&lt;h2 id=&quot;3cpu&quot;&gt;3. CPU 스케줄링이 이루어지는 시기&lt;/h2&gt;

&lt;h4 id=&quot;&quot;&gt;- 프로세스가 입/출력을 요구할 때&lt;/h4&gt;

&lt;p&gt;CPU가 할당되어 실행 중인 주기를 CPU 버스트(CPU Burst), 입/출력이 이루어지는 주기를 I/O 버스트(I/O Burst)라 부른다. 예를 들어 accept(), connect(), send(), recv(), open(), close(), fgets(), printf() 등은 모두 운영체제의 입/출력 시스템 콜이어서, 입/출력이 완료될 때까지 그 프로세스는 CPU 할당을 받지 못하는 I/O 버스트고, 그 외 부분은 CPU 버스트다.&lt;/p&gt;&lt;p&gt;I/O 버스트가 시작되면 운영체제는 CPU를 다른 프로세스에게 할당해주어야 하는데, 이 경우가 가장 대표적인 CPU 스케줄링 시기다.&lt;/p&gt;

&lt;h4 id=&quot;-1&quot;&gt;- 프로세스가 종료를 요구할 때&lt;/h4&gt;

&lt;p&gt;사용자가 작성한 프로그램을 컴파일하고 링크하면 목적 코드(실행 파일)에는 프로그램의 끝부분에 운영체제에게 종료를 요구하는 코드가 자동으로 링크된다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;_start() {
  r = main(...);
  exit(r); // 종료 시스템 콜
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이처럼 프로세스가 종료를 요구하면 운영체제는 CPU를 보내줄 새로운 프로세스를 찾기 위해 CPU 스케줄링이 일어난다.&lt;/p&gt;

&lt;h4 id=&quot;-2&quot;&gt;- 높은 우선순위의 프로세스가 나타났을 때&lt;/h4&gt;

&lt;p&gt;입/출력 상태에 있던 프로세스는 입/출력이 완료되면 준비 상태가 되어 CPU 할당 대상에 새롭게 추가된다. 즉, 입/출력 완료 인터럽트가 발생하면 CPU는 실행 중이던 프로세스에서 잠시 떠나 운영체제의 인터럽트 핸들러로 이동하여 준비 상태의 새로운 프로세스가 나타났음을 인식하고, 만약 이 새로운 프로세스의 우선순위가 이전에 떠나왔던 프로세스보다 높다면 CPU 스케줄링 방법에 따라 CPU 할당을 즉시 교체할 수 있다.&lt;/p&gt;

&lt;h4 id=&quot;cpu-1&quot;&gt;- 주어진 CPU 실행 시간을 초과했을 때&lt;/h4&gt;

&lt;p&gt;대부분의 사용자 프로그램은 CPU 버스트와 I/O 버스터가 번갈아 섞여 있어서, 다수의 프로세스 사이에서 CPU가 자연스럽게 고루 할당된다.&lt;/p&gt;&lt;p&gt;그러나 어떤 프로그램은 입/출력이 극히 적고 계산식으로만 이루어져 있어서 CPU의 고른 분배가 어려워질 수 있다. 이를 해결하기 위해 어느 한 프로세스가 연속해서 사용할 수 있는 최대 시간을 설정하고, 시간이 초과한 경우 CPU를 다른 프로세스에게 할당할 수 있다.&lt;/p&gt;&lt;p&gt;이때 지정된 최대 시간을 타임 퀸텀(Time Quantum) 또는 타임 슬라이스(Time Slice)라 부른다.&lt;/p&gt;

&lt;h2 id=&quot;4cpu&quot;&gt;4. CPU 스케줄링 전략의 분류&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;프로세스 스스로의 요구에 의해 이루어지는 자율적 CPU 반납: 입/출력, 종료&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;외부 상황에 따라 CPU를 강제로 회수당하는 타율적 CPU 반납: 높은 우선순위 출현, 타임 퀀텀 초과&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CPU 반납 방식의 자율성 및 타율성 여부에 따라 CPU 스케줄링 전략은 크게 &lt;strong&gt;비선점형&lt;/strong&gt;과 &lt;strong&gt;선점형&lt;/strong&gt;으로 분류된다.&lt;/p&gt;

&lt;h4 id=&quot;nonpreemption&quot;&gt;- 비선점(Non-preemption)형&lt;/h4&gt;

&lt;p&gt;비선점형 CPU 스케줄링은 실행 중인 프로세스가 자율적으로 CPU를 반납할 때까지 CPU를 계속 점유하여 실행하는 운영체제 환경이다.&lt;/p&gt;&lt;p&gt;입/출력이 적은 계산 위주(CPU Bounds) 프로세스가 다수 적재되어 있다면 다른 프로세스들의 응답 시간은 매우 저조할 것이다. 반대로 프로세스들이 입/출력 위주(I/O Bounds)라면 CPU를 어느 정도 규칙적으로 번갈아 할당받을 수 있으므로 응답 시간이 크게 나쁘지 않을 것이다.&lt;/p&gt;

&lt;h4 id=&quot;preemption&quot;&gt;- 선점(Preemption)형&lt;/h4&gt;

&lt;p&gt;자율적 CPU 반납은 물론 타율적 CPU 반납까지 이루어지는 운영체제 환경이다.&lt;/p&gt;&lt;p&gt;선형 운영체제 환경에서는 어떤 프로세스도 일정 시간(타임 퀀텀) 이상 동안 연속해서 CPU를 점유할 수 없으므로 계산 위주 프로세스가 많이 적재되어 있다 하더라도, 모든 프로세스의 반응 시간 성능을 평균 이상으로 유지할 수 있다.&lt;/p&gt;

&lt;h2 id=&quot;5cpu&quot;&gt;5. CPU 스케줄링 전략들&lt;/h2&gt;

&lt;h4 id=&quot;1fcfsfirstcomefirstserved&quot;&gt;1. 선입 선처리(FCFS: First-Come First-Served) 스케줄링&lt;/h4&gt;

&lt;p&gt;가장 간단하면서도 알기 쉽다. 프로세스들이 준비 대기열에 도착하는 순서에 따라 CPU를 할당하는 방법이다.&lt;/p&gt;&lt;p&gt;| 프로세스 | CPU 버스트 시간 |
| -------- | --------------- |
| P1       | 30              |
| P2       | 3               |
| P3       | 6               |&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;P1 -&amp;gt; P2 -&amp;gt; P3:
평균 대기 시간 = (0 + 30 + 33) / 3 = 21
평균 응답 시간 = (30 + 33 + 39) / 3 = 34
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;FCFS 전략에서 CPU 버스트가 긴 계산 위주 프로세스가 먼저 도착했다면, 이후의 CPU 버스트가 짧은 입/출력 위주 프로세스들은 간발의 차로 늦게 도착했다는 이유로 억울하게 그만큼 많이 기다려야 한다. 이럴 경우 입/출력 장치에 대한 활용도는 물론, 준비 대기열이 대부분 입/출력 위주 프로세스들로 구성된다면 CPU 이용률도 저하될 수 있다.&lt;/p&gt;&lt;p&gt;위와 같은 현상을 &lt;strong&gt;호위 효과(Convoy Effect)&lt;/strong&gt;라 부르며, 근본적으로 FCFS가 비선점형 CPU 스케줄링이기 때문에 일어난다.&lt;/p&gt;

&lt;h4 id=&quot;2sjfshortestjobfirst&quot;&gt;2. 최단 작업 우선(SJF: Shortest Job First) 스케줄링&lt;/h4&gt;

&lt;p&gt;CPU 버스트가 가장 짧은 프로세스를 CPU에 먼저 할당하는 전략이다. &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;P2 -&amp;gt; P3 -&amp;gt; P1:
평균 대기 시간 = (0 + 3 + 9) / 3 = 4
평균 응답 시간 = (3 + 9 + 39) / 3 = 17
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;평균 대기 시간과 평균 응답 시간이 FCFS에 비해 개선되었으나, SJF는 비선점형이므로 계산 위주의 긴 프로세스에 CPU가 할당된 상태에서 다수의 입/출력 위주 짧은 프로세스들이 도착한다면 마찬가지로 호위 효과가 일어날 수 있다.&lt;/p&gt;&lt;p&gt;또한, 짧은 프로세스들이 지속적으로 도착한다면, 상대적으로 긴 프로세스는 계속해서 지연되는 단점이 있다. 이처럼 계속해서 지연되는 상황을 보통 &lt;strong&gt;기아 상태(Starvation State)&lt;/strong&gt;라고 부른다.&lt;/p&gt;

&lt;h4 id=&quot;3srtfshortesstremainingtimefirst&quot;&gt;3. 최단 잔여 시간 우선(SRTF: Shortesst Remaining Time First) 스케줄링&lt;/h4&gt;

&lt;p&gt;비선점형인 SJF 스케줄링인 단점을 보완하기 위해서 준비 대기열에 새로운 프로세스가 도착하면, 현재 진행 중인 프로세스의 잔여 실행 시간과 새로운 프로세스의 CPU 버스트 시간을 비교하여 새 프로세스의 실행 시간이 더 짧으면 CPU를 강제 회수하여 새로운 프로세스에 할당하는 선점형 스케줄링 전략이다.&lt;/p&gt;&lt;p&gt;| 프로세스 | 도착 시간 | CPU 버스트 시간 |
| -------- | --------- | --------------- |
| P1       | 0         | 30              |
| P2       | 1         | 15              |
| P3       | 2         | 10              |&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;P1 -&amp;gt; P3 -&amp;gt; P2:
SJF 평균 대기 시간 = (0 + 39 + 28) / 3 = 22.3

P1 -&amp;gt; P2 -&amp;gt; P3 -&amp;gt; P2 -&amp;gt; P1
SRTF 평균 대기 시간 = (25 + 10 + 0) / 3 = 11.7
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;SRTF는 평균 대기 시간과 평균 응답 시간을 더욱 개선하면서 CPU나 입/출력 장치의 이용률 저하 현상 발생을 억제할 수 있으나, 기아 상태 발생 가능성이 더 커질 수 있다.&lt;/p&gt;

&lt;h4 id=&quot;4hrrfhighestresponseratiofirst&quot;&gt;4. 최고 응답률 우선(HRRF: Highest Response Ratio First) 스케줄링&lt;/h4&gt;

&lt;p&gt;각 프로세스의 응답률을 계산하여 응답률이 가장 큰 프로세스를 먼저 처리하는 스케줄링 전략이다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;응답률 = (준비 큐 대기 시간 + CPU 버스트 시간) / (CPU 버스트 시간) = 1 + ((준비 큐 대기 시간) / (CPU 버스트 시간))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 식은 CPU 버스트 시간에 대한 준비 대기열 대기 시간이 상대적으로 클수록 응답률이 높아져 CPU 스케줄링 우선순위가 높아진다. 만약 대기 시간이 같다면 CPU 버스트 시간이 짧을수록 응답률이 높아져 SJF나 SRTF와 같다. CPU 버스트 시간이 아무리 크더라도 언젠가는 준비 대기열 시간도 커질 것이므로 무한정 지연되는 기아 현상이 예방된다.&lt;/p&gt;&lt;p&gt;HRRF 스케줄링은 선점형이나 비선점형 중 어느 형태로도 운영이 가능한데, 선점형으로 구현할 경우 새로운 프로세스가 준비 큐에 도착하면 현재 실행 중인 프로세스와 새 프로세스의 응답률 계산 결과를 활용하면 된다.&lt;/p&gt;

&lt;h4 id=&quot;5rrroundrobin&quot;&gt;5. 라운드 로빈(RR: Round Robin) 스케줄링&lt;/h4&gt;

&lt;p&gt;RR은 전형적인 선점형 스케줄링 전략으로, 모든 프로세스에 동일한 최대 CPU 점유 시간(타임 퀀텀)을 설정하고, 처리 중인 프로세스의 CPU 실행 시간이 타임 퀀텀을 초과하면 CPU를 강제로 회수하여 다음 프로세스에 할당하는 방식이다. 이렇게 하여 모든 프로세스들에게 CPU가 할당될 기회가 동일하게 주어지므로 대화식 시스템 환경에 적합하다.&lt;/p&gt;&lt;p&gt;RR 스케줄링 전략에서는 적절한 타임 퀀텀 설정이 매우 중요하다. 만약 타임 퀀텀이 아주 작다면 CPU 할당이 교체될 때마다 일어나는 컨텍스트 스위칭 횟수가 증가하여 시스템 부담을 증가시키므로 전체적인 처리율이 감소한다. 반대로 아주 크게 설정한다면 RR은 FCFS 스케줄링 전략과 거의 유사한 방식으로 후퇴할 것이다.&lt;/p&gt;

&lt;h4 id=&quot;6mqmultilevelqueue&quot;&gt;6. 다단계 큐(MQ: Multi-level Queue) 스케줄링&lt;/h4&gt;

&lt;p&gt;RR의 단점은 최적의 타임 퀀텀 찾기와 설정된 타임 퀀텀으로 인해 프로세스 특성을 고려할 수 없다는 것이다. 이를 해결하기 위한 방안으로 프로세스 특성별로 준비 큐를 여러 개 두어 우선순위를 부여하고, 높은 우선순위 큐들이 모두 비었을 때만 다음 단계의 낮은 우선순위의 큐 프로세스들에게 CPU를 할당하는 MQ 스케줄링 전략을 도입했다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;시스템 프로세스 준비 큐 (우선순위 가장 높음)&lt;/li&gt;

&lt;li&gt;대화형(입/출력 위주) 프로세스 준비 큐&lt;/li&gt;

&lt;li&gt;계산 위주 프로세스 준비 큐&lt;/li&gt;

&lt;li&gt;후면처리 프로세스 준비 큐 (우선순위 가장 낮음)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;각 준비 큐에는 해당 프로세스의 특성을 반영하는 타임 퀀텀을 설정한다. 예를 들어 대화형 프로세스들은 CPU 버스트 시간이 짧으므로 긴 타임 퀀텀이 필요 없고, 계산 위주 프로세스들은 타임 퀀텀이 길면 콘텍스트 스위칭 빈도가 줄어 시스템 부담을 줄일 수 있다. 또한, 같은 준비 큐 프로세스들 간에는 FCFS, SJF, RR 등 다른 스케줄링 전략등을 적용할 수 있다.&lt;/p&gt;

&lt;h4 id=&quot;7mfqmultilevelfeedbackqueue&quot;&gt;7. 다단계 피드백 큐(MFQ: Multi-level Feedback Queue) 스케줄링&lt;/h4&gt;

&lt;p&gt;응용 프로그램은 대부분 계산 위주 단계와 입/출력 위주 단계를 반복하므로 프로세스 진행 과정에서 그 특성이 변하면 이를 인지하여 해당 프로세스를 적절한 준비 큐로 이동시켜주는 전략이 MFQ 스케줄링이다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;실행 중인 프로세스가 해당 큐의 타임 퀀텀을 소진하지 못하고 입출력 등으로 CPU를 자진 반납하면, 이 프로세스는 입/출력 성향이 강해진 것으로 인식하여 입/출력 쪽으로 한 단계 높은 준비 큐로 이동시킨다.&lt;/li&gt;

&lt;li&gt;반대로, 실행 중인 프로세스가 주어진 타임 퀀텀을 모두 소진한 후 CPU를 강제로 회수당하면, 이 프로세스는 계산 성향이 강해진 것으로 인식하여 이 프로세스를 입/출력 성향 기준 한 단계 낮은 준비 큐로 이동시킨다.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 64px; width: 64px; height: 64px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99E8F74D5F71E0592C&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99E8F74D5F71E0592C&quot; width=&quot;64&quot; height=&quot;64&quot; filename=&quot;chronometer.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 64px; height: 64px;&quot;/&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>컴구 이야기</category>
      <category>운영체제</category>
      <category>컴퓨터구조</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/258</guid>
      <comments>https://boycoding.tistory.com/258#entry258comment</comments>
      <pubDate>Mon, 28 Sep 2020 22:09:11 +0900</pubDate>
    </item>
    <item>
      <title>5. 훑어보는 프로세스와 스레드</title>
      <link>https://boycoding.tistory.com/257</link>
      <description>&lt;h1 id=&quot;&quot;&gt;프로세스와 스레드&lt;/h1&gt;

&lt;p&gt;컴퓨터에서 프로세스란 실행 중인 프로그램을 말한다. 일반적으로 컴퓨터 프로그램은 프로그래밍 언어로 작성된 후 컴파일 과정을 거쳐 오브젝트 파일(혹은 실행 파일) 형태로 하드디스크에 저장된다.&lt;/p&gt;&lt;p&gt;이렇게 하드디스크에 저장된 프로그램이 실행되기 위해서는 주기억장치로 적재되어 운영체제의 관리 개체로 등록되어야 하는데, 이 상태를 &lt;strong&gt;프로세스&lt;/strong&gt;라 부른다.&lt;/p&gt;

&lt;h2 id=&quot;1&quot;&gt;1. 프로세스 속성&lt;/h2&gt;

&lt;p&gt;컴퓨터에는 여러 개의 프로세스가 동시에 존재하므로 각각의 프로세스를 식별하거나 관리하기 위해서는 아래와 같은 여러 가지 속성들이 필요하다.&lt;/p&gt;

&lt;h4 id=&quot;pid&quot;&gt;프로세스 고유 번호(PID)&lt;/h4&gt;

&lt;p&gt;프로세스가 새로 탄생할 때마다 운영체제는 유일한 식별 번호를 부여한다.&lt;/p&gt;&lt;p&gt;어느 한 프로세스가 끝나면 그 프로세스가 차지하고 있던 PID는 이후에 탄생하는 다른 프로세스에 재할당될 수 있다.&lt;/p&gt;

&lt;h4 id=&quot;-1&quot;&gt;프로세스 메모리 정보&lt;/h4&gt;

&lt;p&gt;하나의 연속된 메모리 공간 안에 여러 개의 프로세스가 존재하므로, 각각의 프로세스가 메모리의 어느 부분을 차지하고 있는지 관리되어야 한다.&lt;/p&gt;&lt;p&gt;만약 메모리 정보가 관리되지 않는다면 프로세스들 사이의 상호 침범을 탐지할 수 없을 것이다.&lt;/p&gt;

&lt;h4 id=&quot;processstatus&quot;&gt;프로세스 상태(Process Status)&lt;/h4&gt;

&lt;p&gt;운영체제가 CPU를 어느 프로세스로 배정할 것인가를 탐색하기 위해서는 프로세스들이 입/출력이 완료되기를 기다리고 있는지 아니면 CPU 할당을 기다리고 있는지 등 프로세스의 현재 상태가 필요하다.&lt;/p&gt;

&lt;h4 id=&quot;programcounter&quot;&gt;프로세스 진행지점(Program Counter)&lt;/h4&gt;

&lt;p&gt;운영체제가 선택한 프로세스에 CPU를 배정시켜주기 위해서는 프로세스별로 실행지점이 관리되어야 한다.&lt;/p&gt;&lt;p&gt;선택된 프로세스를 처리 중이던 CPU는 운영체제가 설정해 둔 타이머 인터럽트에 의해 언젠가는 그 프로세스를 떠나야 하는데, 이때 다음 실행할 기계 명령어에 대한 주소를 기록해두어야만 다음 기회에 연속된 처리가 가능하다.&lt;/p&gt;

&lt;h4 id=&quot;processcontext&quot;&gt;프로세스 문맥(Process Context)&lt;/h4&gt;

&lt;p&gt;기계 사이클에서 본 것처럼, 모든 연산은 CPU 안에서 이루어진다. 피연산자나 연산결과가 저장되는 CPU 안의 저장 장소를 CPU 레지스터라 하는데, 어느 연산이 끝난 시점에서 인터럽트에 의해 CPU가 운영체제로 되돌아가면 조금 전 프로세스에서 생산된 CPU 레지스터값들이 파괴된다. 이렇게 되면 해당 프로세스를 나중에 다시 연속해서 실행할 때 이전의 연산 결과값들을 잃어버렸으므로 올바른 진행이 불가능하다.&lt;/p&gt;&lt;p&gt;그러므로 현재 진행 중인 프로세스의 연산 명령어들에 의해 CPU 안에서 생산되어 보관 중인 값들을 &lt;strong&gt;프로세스 문맥&lt;/strong&gt;이라 하는데 이러한 문맥은 CPU가 진행 중인 프로세스를 떠날 때 보관되었다가 나중에 처리를 재개하지 직전에 복원되어야 한다. 이를 &lt;strong&gt;문맥 교환(Context Switching)&lt;/strong&gt; 이라고 한다.&lt;/p&gt;

&lt;h4 id=&quot;processpriority&quot;&gt;프로세스 우선순위(Process Priority)&lt;/h4&gt;

&lt;p&gt;운영체제는 CPU를 배정시킬 프로세스를 선택해야 하는데, 이때 필요한 요소가 우선순위다.&lt;/p&gt;&lt;p&gt;프로세스 우선순위는 최초에 기본으로 설정되었다가 CPU를 기다린 시간, 입/출력 여부, 메모리 포화 여부 등 주변 환경에 따라 운영체제에 의해 동적으로 조정되거나 관리자에 의해 변경될 수 있다.&lt;/p&gt;

&lt;h4 id=&quot;processresource&quot;&gt;프로세스 자원목록(Process Resource)&lt;/h4&gt;

&lt;p&gt;프로세스는 진행 도중 운영체제에 다양한 유형의 자원을 요청한다. 메모리 외에도 디스크 파일, 사운드, 그래픽 등의 물리적인 자원들과 네트워크 포트, 시그널 등 논리적인 자원들을 사용할 수 있는데, 프로세스별 할당 자원들이 관리되어야만 사후 자원의 회수 및 재활용을 할 수 있다.&lt;/p&gt;

&lt;h4 id=&quot;accountinginformation&quot;&gt;회계 정보(Accounting Information)&lt;/h4&gt;

&lt;p&gt;프로세스 최초 생성부터 지금까지 머문 시간, CPU가 배정되어 실행된 시간, 메모리 사용량 등 프로세스의 성능이나 비용산정에 필요한 정보가 관리되어야 한다. &lt;/p&gt;

&lt;h2 id=&quot;2pcbprocesscontrolblock&quot;&gt;2. 프로세스 관리 블록(PCB: Process Control Block)&lt;/h2&gt;

&lt;p&gt;프로세스가 생성된다는 것은 프로세스가 운영체제에 의해 하나의 관리 개체로 등록됨을 의미한다.&lt;/p&gt;&lt;p&gt;이때 운영체제가 프로세스 관리를 위해 필요한 모든 정보를 기록하는 표를 &lt;strong&gt;PCB&lt;/strong&gt;라 하고, 그 수는 생성 가능한 최대 프로세스 수를 의미한다.&lt;/p&gt;&lt;p&gt;PCB에는 위에 언급한 프로세스 속성들이 저장된다.&lt;/p&gt;

&lt;h2 id=&quot;3&quot;&gt;3. 스레드&lt;/h2&gt;

&lt;p&gt;프로세스의 연장 선상에서 사용되는 스레드 용어는 '실행 줄기' 정도로 설명할 수 있다. 프로세스는 프로그램의 시작 점(Entry point)에서 시작되는 명령어 처리의 흐름 줄기 즉, 실행 줄기를 갖게 되는데 이것이 곧 하나의 스레드이고 이 경우를 단일 스레드 프로세스라고 한다.&lt;/p&gt;&lt;p&gt;다시 말하면, 스레드는 프로세스의 구성 요소 중의 하나로서, 프로세스의 다른 구성 요소인 메모리(기계 명령어 및 데이터)를 따라 흐르는 개념적인 개체다.&lt;/p&gt;&lt;p&gt;스레드의 진정한 의의는 멀티 스레드 프로세스에서 찾을 수 있다.&lt;/p&gt;

&lt;h2 id=&quot;4multithreadprocess&quot;&gt;4. 멀티 스레드 프로세스(Multi-thread Process)&lt;/h2&gt;

&lt;p&gt;멀티 스레드란 메모리에 적재된 프로그램(프로세스)의 시작 점이 여러 곳 있어서, 해당 프로세스를 바탕으로 진행하는 실행 줄기가 여러 개 존재한다는 의미다. 멀티 스레드 개념은 2개 이상의 CPU가 같은 프로세스 영역에 있는 기계 명령어를 실행하는 경우에서 쉽게 이해할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int S;

void main1()
{
  int x, y, z;
  x = 1, y = 2;
  z = add(x, y);
  S = S + z;
}

void main2()
{
  int a, b, c;
  a = 3, b = 4;
  c = add(a, b);
  S = S + c;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;예를 들어 CPU1은 main1 함수부터 시작하고, CPU2는 main2 함수부터 시작한다면 이들은 각자의 제어 흐름에 따라 독립적으로 진행될 것인데, 이들 각각이 바로 스레드인 것이다.&lt;/p&gt;&lt;p&gt;이 스레드들이 main1과 main2 함수 부분을 실행할 때는 서로 다른 기계어 코드를 실행하겠지만, add 함수를 실행할 때는 완전히 같은 기계어 코드를 실행하게 된다. 이때 두 스레드가 거의 동시에 add 함수에 진입하면 각자의 계산 결과가 유지될까 하는 의문이 들 수 있다.&lt;/p&gt;&lt;p&gt;스레드들이 같은 기계 명렁어를 실행하더라도 연산 과정이 고유하게 유지되는데, 그 이유는 기계 명령어가 같더라도 피연산자를 위한 최종 목적 주소가 분리되어 있는 원리이기 때문이다.&lt;/p&gt;&lt;p&gt;즉, add 함수에서 사용되는 모든 변수는 지역 변수인데, 이들은 모두 CPU 내에 존재하는 SP(Stack Pointer)에 저장된 주소값을 기준으로 한 상대적 거리로 참조된다. 따라서 스레드 1을 위한 CPU1의 SP 값과 스레드 2를 위한 CPU2의 SP 값을 다르게 설정하면 add 함수의 기계어 코드는 같지만, 그 기계어 코드가 참조하는 최종 피연산자 주소는 다르다.&lt;/p&gt;&lt;p&gt;이처럼 스레드들의 고유성을 유지하기 위한 저장 공간을 스택(Stack) 영역이라 부르고, 스레드가 시작할 때 할당되며, 해당 스레드를 실행하는 CPU의 SP 값은 할당된 스택 영역으로 초기화된다.&lt;/p&gt;&lt;p&gt;멀티 스레드는 반드시 CPU가 여러 개 있어야만 의미 있는 것은 아니다. 운영체제가 CPU를 배정하는 단위를 프로세스가 아닌 스레드로 세분화시키기 때문에 하나의 프로세스 위를 여러 개의 스레드가 통과할 수 있다.&lt;/p&gt;&lt;p&gt;멀티 스레드 프로그래밍 환경에서 주의할 점은 변수 S와 같이 전역 변수를 동시에 참조하는 경우, 올바른 계산을 위해 동기화 장치가 필요하다는 것이다. 전역 변수 S는 상대 주소가 아닌 절대 주소로 참조되므로 해당 변수에 대한 스레드들의 참조 주소는 모두 같은 메모리 주소가 된다. 이런 경우 올바른 계산 결과를 얻지 못할 수 있다.&lt;/p&gt;

&lt;hr&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 64px; width: 64px; height: 64px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99A2153A5F688D9217&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99A2153A5F688D9217&quot; width=&quot;64&quot; height=&quot;64&quot; alt=&quot;cpu&quot; filename=&quot;cpu.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 64px; height: 64px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>컴구 이야기</category>
      <category>운영체제</category>
      <category>컴퓨터구조</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/257</guid>
      <comments>https://boycoding.tistory.com/257#entry257comment</comments>
      <pubDate>Mon, 21 Sep 2020 20:26:06 +0900</pubDate>
    </item>
    <item>
      <title>4. 훑어보는 입/출력 장치</title>
      <link>https://boycoding.tistory.com/256</link>
      <description>&lt;h1 id=&quot;iodevice&quot;&gt;입/출력 장치(I/O Device)&lt;/h1&gt;

&lt;p&gt;데이터를 처리하기 위해서는 CPU가 접근해서 연산할 수 있도록 디스크 등의 외부 장치에 저장된 데이터를 메모리로 이동시켜야 한다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;입력(Input): CPU 등 처리기가 주변 장치 레지스터로부터 주기억장치로 데이터를 복사하는 작업&lt;/li&gt;

&lt;li&gt;출력(Output): 입력과 반대로 주기억장치의 데이터를 주변장치 레지스터로 복사하는 작업&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;1&quot;&gt;1. 입/출력 장치 구조&lt;/h2&gt;

&lt;p&gt;모든 입/출력 장치는 상태(Status), 명령(Command), 데이터(Data) 등의 세 개의 레지스터로 구성된 인터페이스를 제공한다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;상태 레지스터&lt;/strong&gt;: 입력 장치인 경우 데이터 레지스터에 데이터가 입력되어 있는가를 표시하고, 출력 장치인 경우 이전 데이터의 출력이 완료되었는가를 표시한다.&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;명령 레지스터&lt;/strong&gt;: '입력' 혹은 '출력'의 기본 명령과 기타 해당 장치 고유의 명령을 저장(write)함으로써 명령을 전달한다.&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;데이터 레지스터&lt;/strong&gt;: 입력된 데이터나 출력될 데이터를 보관한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;입력의 경우 장치의 특성에 따라 동기적 입력과 비동기적 입력이 있다. 예를 들면 하드 디스크 장치는 명령 레지스터에 '입력' 명령이 주어졌을 때만 입력 작업을 진행하지만(동기적), 키보드 장치는 어느 때라도 키가 눌리면 해당 데이터를 입력시킨다(비동기적).&lt;/p&gt;&lt;p&gt;출력은 데이터 레지스터에 데이터를 기록한 후, 명령 레지스터에 '출력' 명령을 설정하면 진행되므로 언제나 동기적으로 이루어진다.&lt;/p&gt;&lt;p&gt;위와 같이 상태 레지스터, 명령 레지스터, 데이터 레지스터 등 인터페이스를 제공하는 입/출력 장티를 보통 &lt;strong&gt;장치 제어기(Device Controller)&lt;/strong&gt;라 하고, 특정 장치 제어기의 인터페이스를 이용하여 입/출력을 처리하는 소프트웨어를 &lt;strong&gt;장치 구동기(Device Driver)&lt;/strong&gt;라 부른다.&lt;/p&gt;

&lt;h2 id=&quot;2&quot;&gt;2. 입/출력 장치의 식별&lt;/h2&gt;

&lt;p&gt;입/출력은 CPU에 의해 실행되는 입/출력 장치 구동기에 의해 이루어진다. 즉, 장치 구동기는 장치 인터페이스인 상태/명령/데이터 레지스터에 대한 읽기/쓰기 작업으로 입/출력을 실현한다.&lt;/p&gt;&lt;p&gt;이때, 각 장치별로 고유 식별 위치 혹은 번호의 부여가 필요한데, 이를 &lt;strong&gt;입/출력 포트(I/O Port)&lt;/strong&gt;라 부른다.&lt;/p&gt;

&lt;h3 id=&quot;memorymappedio&quot;&gt;- 메모리 대응 입/출력(Memory Mapped I/O)&lt;/h3&gt;

&lt;p&gt;메모리 대응 입/출력은 입/출력 레지스터의 위치를 메모리 주소 영역의 일부에 대응시켜 CPU가 메모리 접근 방법과 같은 방법으로 입/출력 레지스터에 접근하는 방식이다.&lt;/p&gt;&lt;p&gt;즉, 입력 장치 레지스터에 대응된 특정 번지를 읽으면 해당 입력 장치의 대기 중인 입력 값을 얻게 되고, 출력 장치 레지스터에 대응된 특정 번지에 쓰면 출력 장치로의 출력이 이루어진다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;입력 예)


&lt;ul&gt;
&lt;li&gt;&lt;code&gt;load r1, 10&lt;/code&gt; =&amp;gt;  메모리 10번지에 대응된 입력 레지스터에서 CPU의 r1 레지스터로 읽음(입력)&lt;/li&gt;

&lt;li&gt;&lt;code&gt;save r1, 1000&lt;/code&gt; =&amp;gt; 입력된 데이터를 메모리 1000번에 저장&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;

&lt;li&gt;출력 예)


&lt;ul&gt;
&lt;li&gt;&lt;code&gt;load r1, 2000&lt;/code&gt; =&amp;gt; 메모리 2000번지의 데이터를 CPU의 r1 레지스터로 적재&lt;/li&gt;

&lt;li&gt;&lt;code&gt;Out r1, 20&lt;/code&gt; =&amp;gt; 적재된 데이터를 메모리 20번지에 대응된 출력 레지스터에 기록(출력)&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;메모리 접근과 입/출력을 위한 기계 명령어가 같으므로 개발자로서는 편리성이 있으나, 메모리 주소 영역의 일부를 메모리용으로 사용할 수 없다는 단점이 있다.&lt;/p&gt;

&lt;h3 id=&quot;isolatedio&quot;&gt;- 격리된 입/출력(Isolated I/O)&lt;/h3&gt;

&lt;p&gt;격리된 입/출력은 입/출력 포트의 식별 영역이 메모리 주소 영역과 분리된 방식이다. 예를 들어, 같은 10번지라 하더라도 메모리 10번지와 입/출력 포트 10번지는 전혀 다른 장소이다.&lt;/p&gt;&lt;p&gt;따라서, 메모리 접근을 위한 기계 명령어와 입/출력 포트 접근을 위한 기계 명령어가 따로 있어야 한다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;입력 예)


&lt;ul&gt;
&lt;li&gt;&lt;code&gt;load r1, 10&lt;/code&gt; =&amp;gt;  입/출력 포트 10번의 입력 레지스터에서 CPU의 r1 레지스터로 읽음(입력)&lt;/li&gt;

&lt;li&gt;&lt;code&gt;save r1, 1000&lt;/code&gt; =&amp;gt; 입력된 데이터를 메모리 1000번에 저장&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;

&lt;li&gt;출력 예)


&lt;ul&gt;
&lt;li&gt;&lt;code&gt;load r1, 2000&lt;/code&gt; =&amp;gt; 메모리 2000번지의 데이터를 CPU의 r1 레지스터로 적재&lt;/li&gt;

&lt;li&gt;&lt;code&gt;out r1, 20&lt;/code&gt; =&amp;gt; 적재된 데이터를 입/출력 포트 20번지의 출력 레지스터에 기록(출력)&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;시스템 버스 메커니즘이 복잡해지는 단점이 있지만, 메모리 공간을 충분히 사용할 수 있다는 장점이 있다.&lt;/p&gt;

&lt;hr&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 64px; width: 64px; height: 64px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/992E7D3C5F4F2D4C0A&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F992E7D3C5F4F2D4C0A&quot; width=&quot;64&quot; height=&quot;64&quot; alt=&quot;입출력장치&quot; filename=&quot;input.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 64px; height: 64px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>컴구 이야기</category>
      <category>운영체제</category>
      <category>컴퓨터구조</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/256</guid>
      <comments>https://boycoding.tistory.com/256#entry256comment</comments>
      <pubDate>Wed, 2 Sep 2020 16:08:36 +0900</pubDate>
    </item>
    <item>
      <title>3. 훑어보는 인터럽트</title>
      <link>https://boycoding.tistory.com/255</link>
      <description>&lt;h1 id=&quot;interrupt&quot;&gt;인터럽트(Interrupt)&lt;/h1&gt;

&lt;p&gt;입터럽트란 현재 무엇인가를 처리 중인 CPU에게 어떤 사건이 발생했음을 알리는 행위를 말하고, CPU와 연결된 선을 통하여 전달된다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;입터럽트란 CPU에 전달되는 사건 신호(Event Signal)로서, 주로 전용 회선으로 전달된다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;1&quot;&gt;1. 인터럽트 서비스 루틴&lt;/h2&gt;

&lt;p&gt;인터럽트 개념 자체가 긴급한 사건의 발생에 대한 주목을 요청하는 의미로, 인터럽트를 인지한 CPU는 현재 처리 중인 일을 일시 중지하고, 그 인터럽트가 어떤 사건인지를 식별하여 처리한 후, 다시 원래의 처리하던 일을 계속한다.&lt;/p&gt;&lt;p&gt;여기서, 어떤 사건인지를 식별하여 처리한다는 건 전달된 인터럽트 번호에 대응되는 처리 루틴(함수 혹은 프로그램 형태)을 찾아 실행시켜줌을 의미하는데, 이를 &lt;strong&gt;인터럽트 서비스 루틴(Interrupt Service Routine: ISR)&lt;/strong&gt; 혹은 인터럽트 핸들러라고 부른다.&lt;/p&gt;&lt;p&gt;예를 들어, 키보드에서 키가 눌렸을 때 발생하는 인터럽트의 번호를 몇 번으로 할 것인가는 하드웨어 설계 시 결정되고, 이를 바탕으로 유닉스 등의 운영체제 개발자는 해당하는 인터럽트 서비스 루틴에 키보드에서 문자 하나를 읽어 운영체제 내 버퍼에 저장하는 코드를 구현한다.&lt;/p&gt;

&lt;h2 id=&quot;2&quot;&gt;2. 인터럽트 벡터&lt;/h2&gt;

&lt;p&gt;인터럽트 번호와 인터럽트 서비스 루틴의 위치는 어떤 메커니즘으로 대응될까? 인터럽트 서비스 루틴들을 임의의 장소에 두고, 그들의 위치를 인터럽트 번호와 대응되는 특정 위치에 기록해 두는 방법을 사용한다. 이들 저장 위치는 테이블 형태로 존재하며 이를 &lt;strong&gt;인터럽트 벡터(Interrupt Vector)&lt;/strong&gt;라고 부른다.&lt;/p&gt;

&lt;h2 id=&quot;3&quot;&gt;3. 인터럽트 우선순위&lt;/h2&gt;

&lt;p&gt;인터럽트들은 상대적 우선순위를 가지고 있어서 높은 우선순위의 인터럽트 서비스가 먼저 처리된다.&lt;/p&gt;&lt;p&gt;즉, 처리 중인 인터럽트보다 높은 우선순위의 인터럽트가 발생하면 진행 중이던 인터럽트 서비스를 잠시 중단하고 새로운 인터럽트에 대한 서비스를 먼저 처리한 다음 이전의 인터럽트 서비스 처리를 계속한다.&lt;/p&gt;

&lt;h2 id=&quot;4&quot;&gt;4. 인터럽트 사이클&lt;/h2&gt;

&lt;p&gt;CPU가 인터럽트 발생 여부를 체크하는 주기를 &lt;strong&gt;인터럽트 사이클(Interrupt Cycle)&lt;/strong&gt;이라고 한다.&lt;/p&gt;&lt;p&gt;일반적으로 인터럽트 체크는 CPU가 하나의 기계 명령어 처리를 마칠 때마다 이루어진다. 즉, 인터럽트 사이클은 기계 사이클의 맨 마지막 단계로 추가된다.&lt;/p&gt;

&lt;h2 id=&quot;5&quot;&gt;5. 인터럽트 유형&lt;/h2&gt;

&lt;p&gt;인터럽트는 크게 디바이스 인터럽트, 오류 인터럽트, 소프트웨어 인터럽트 등 세 가지로 분류할 수 있다.&lt;/p&gt;

&lt;h4 id=&quot;deviceinterrupt&quot;&gt;- 디바이스 인터럽트(Device Interrupt)&lt;/h4&gt;

&lt;p&gt;디바이스 인터럽트는 입 • 출력 장치 등과 같이 CPU 외부 하드웨어로부터 발생하기 때문에 하드웨어 인터럽트라고 부르기도 하며, 대부분 인터럽트가 여기에 해당한다.&lt;/p&gt;

&lt;h4 id=&quot;errorinterrupt&quot;&gt;- 오류 인터럽트(Error Interrupt)&lt;/h4&gt;

&lt;p&gt;CPU가 기계 명령어를 처리하는 도중 발생하는 인터럽트를 오류 인터럽트라고 한다. 실행 중인 프로그램에 해당 CPU의 기계 명령어 포맷에 어긋나는 명령어가 포함되어 있으면 CPU는 더이상 진행할 수 없으므로 스스로 인터럽트를 발생시켜 이에 대한 오류 메시지를 출력하고, 다른 프로그램의 실행을 계속하도록 한다.&lt;/p&gt;&lt;p&gt;오류 사항은 예상 못 한 예외 사건이 발생한 경우이므로 오류 인터럽트를 &lt;strong&gt;예외(Exception)&lt;/strong&gt;라 부르기도 한다.&lt;/p&gt;

&lt;h4 id=&quot;softwareinterrupt&quot;&gt;- 소프트웨어 인터럽트(Software Interrupt)&lt;/h4&gt;

&lt;p&gt;기계 명령에 의해 프로그램에서 인위적으로 발생하는 인터럽트를 소프트웨어 인터럽트라고 한다. 소프트웨어 인터럽트는 주로 운영체제 개발 과정에서 인터럽트 서비스 루틴에 대한 테스트 용도로 사용되나, 일반 사용자 프로그램이 운영체제 내의 서비스 루틴을 호출할 수 있도록 하는 시스템 호출 메커니즘으로 활용되기도 한다.&lt;/p&gt;&lt;p&gt;소프트웨어 인터럽트는 프로그램의 흐름을 잠시 특별한 부분으로 빠져들게 하는 효과가 있으므로 &lt;strong&gt;트랩(Trap)&lt;/strong&gt;이라 부르기도 한다.&lt;/p&gt;

&lt;hr&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 64px; width: 64px; height: 64px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9936A64D5F4B40F336&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9936A64D5F4B40F336&quot; width=&quot;64&quot; height=&quot;64&quot; alt=&quot;trap&quot; filename=&quot;spider.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 64px; height: 64px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>컴구 이야기</category>
      <category>운영체제</category>
      <category>컴퓨터구조</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/255</guid>
      <comments>https://boycoding.tistory.com/255#entry255comment</comments>
      <pubDate>Sun, 30 Aug 2020 15:03:10 +0900</pubDate>
    </item>
    <item>
      <title>2. 훑어보는 컴퓨터 시스템 구조</title>
      <link>https://boycoding.tistory.com/254</link>
      <description>&lt;h1 id=&quot;&quot;&gt;컴퓨터 시스템 구조&lt;/h1&gt;

&lt;p&gt;컴퓨터의 주요 구성 요소:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;중앙처리장치(CPU)&lt;/li&gt;

&lt;li&gt;주기억장치(Main Memory)&lt;/li&gt;

&lt;li&gt;주변장치(Peripheral Device)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;1systembus&quot;&gt;1. 시스템 버스(System Bus)&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;시스템 버스&lt;/strong&gt;란 컴퓨터를 구성하는 주요 모듈을 연결하여 정보를 주고받는 통신 선로를 말하는데, 디지털 정보인 경우 보통 선 하나에 0 아니면 1의 두 가지 정보 즉, 1 비트(Bit) 정보를 전달할 수 있다.&lt;/p&gt;

&lt;h3 id=&quot;-1&quot;&gt;- 어드레스 버스&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;어드레스 버스(Address Bus)&lt;/strong&gt;는 CPU가 메모리의 정보를 읽거나 메모리에 정보를 쓰고자 할 때, 메모리 위치 즉, 주소를 전달하기 위한 버스로서 CPU가 지원할 수 있는 메모리 용량에 따라 선의 개수가 결정된다.&lt;/p&gt;

&lt;h3 id=&quot;-2&quot;&gt;- 데이터 버스&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;데이터 버스(Data Bus)&lt;/strong&gt;는 메모리에 기록할 데이터나 메모리에서 읽힌 데이터를 전달하는 용도로 사용되는데, 하드웨어 설계상 어드레스 버스와 데이터 버스가 동시에 사용되지 않dmaus 어드레스 버스의 일부를 데이터 버스로 활용하는 경우도 있다.&lt;/p&gt;&lt;p&gt;데이터 버스의 크기는 한꺼번에 실어 나를 수 있는 최대 바이트 수에 따라 결정된다.&lt;/p&gt;

&lt;h3 id=&quot;-3&quot;&gt;- 제어 버스&lt;/h3&gt;

&lt;p&gt;각종 제어 신호를 전달하기 위한 &lt;strong&gt;제어 버스(Control Bus)&lt;/strong&gt;가 있다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;2mainmemory&quot;&gt;2. 주기억 장치(Main Memory)&lt;/h2&gt;

&lt;p&gt;CPU가 접근하여 실행할 프로그램을 저장할 목적으로 사용되는 메모리를 &lt;strong&gt;메인 메모리&lt;/strong&gt;라고 한다. 메모리는 보통 읽거나 쓸 수 있는 최소 단위인 바이트(8 비트)의 연속으로 이루어진다. 다시 말하면, 메모리는 바이트 단위로 주소가 배정되어 있으므로 비트 단위로 읽거나 쓸 수 없다.&lt;/p&gt;&lt;p&gt;따라서 어느 번지의 바이트에 포함된 비트를 수정하려면 해당 바이트 전체를 CPU로 읽어온 후, 원하는 비트를 비트 연산으로 수정하고 그 바이트를 다시 기록해야 한다.&lt;/p&gt;&lt;p&gt;CPU 특성에 따라 한 번에 읽거나 쓸 수 있는 최대 바이트 수가 다양한데, 보통 1, 2, 4, 8 바이트를 읽을 수 있는 기계 명령어를 각각 가지고 있어서 프로그래머에서 편의를 제공한다.&lt;/p&gt;&lt;p&gt;만약 CPU에 2 바이트 읽기/쓰기 기계 명령어까지만 있다면 4바이트 정수값을 참조하기 위해서는 두 번의 메모리 접근이 필요하므로 불편하고 성능이 낮아진다.&lt;/p&gt;&lt;p&gt;또한, 어떤 CPU에서는 2 바이트 혹은 4 바이트 읽기•쓰기는 반드시 2 혹은 4의 배수인 주소에서만 가능하게 되어있는데 이를 &lt;strong&gt;메모리 얼라인먼트(Memory Alignment)&lt;/strong&gt;라고 한다.&lt;/p&gt;

&lt;h2 id=&quot;3machineinstruction&quot;&gt;3. 기계 명령어(Machine Instruction)&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;기계 명령어&lt;/strong&gt;는 프로그램 일부로 메모리에 저장되어 있다가 CPU에 의해 하나씩 읽히고 읽어진다. 기계 명령어의 전형적인 포맷은 연산코드(Operatiion Code)와 피연산자(Operand)로 구성된다.&lt;/p&gt;&lt;p&gt;연산 코드는 CPU에게 지시하는 명령어를 의미하고, 이 부분이 크면 기계 명령어의 종류를 많이 가지고 있다는 의미이다. 피연산자는 연산이 적용될 대상을 지시하는 부분으로, 변수에 대응되는 메모리 주소, 상수, 임시 값을 저장하고 있는 CPU 레지스터 번호 등이 기입될 수 있는데 이들은 연산 코드에 따라 가장 적절한 유형의 피연산자로 해석된다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;기계 명령어는 연산코드와 피연산자로 구성되고, 연산코드 부분의 크기는 연산 종류의 개수를, 피연산자 부분의 크기는 최대 메모리 용량을 결정한다.&lt;/li&gt;

&lt;li&gt;피연산자는 연산코드에 따라 주소, 상수, 레지스터 번호 등 다양하게 해석된다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;4machinecycle&quot;&gt;4. 기계 사이클(Machine Cycle)&lt;/h2&gt;

&lt;p&gt;CPU는 전원을 받은 순간부터 같은 활동을 끊임없이 반복하는데 이를 기계 사이클이라 한다.&lt;/p&gt;&lt;p&gt;CPU는 메모리에서 명령어를 인출하여 CPU 내부로 가져오고(Fetch Cycle), 그것이 어떤 명령어인지를 알기 위해 연산 코드를 해독하고(Decoding Cycle), 연산을 위한 피연산자 준비가 필요한 경우 메모리에 다시 접근하여 피연산자를 인출한 다음(Operand Cycle), 연산을 처리한다(Excecution Cycle).&lt;/p&gt;&lt;p&gt;이와 같은 기계 사이클 단계는 발진 회로에 의해 발생하는 톱니바퀴 형 파형(Clock)에 따라 진행되고, 하나의 명령어 처리를 위한 기계 사이클이 끝나면, CPU는 자동으로 다음 명령어에 대한 기계 사이클을 개시한다. 이때, 처리할 명령어가 저장된 메모리의 주소는 CPU 내의 PC(Program Counter) 레지스터에 의해 관리된다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;기계 사이클은 CPU가 반복적으로 처리하는 동작의 패턴으로서, &lt;strong&gt;명령어 인출(Fetch)&lt;/strong&gt;, &lt;strong&gt;명령어 해석(Decoding)&lt;/strong&gt;, &lt;strong&gt;피연산자 인출(Operand)&lt;/strong&gt;, &lt;strong&gt;연산 처리(Excution)&lt;/strong&gt; 주기 등 크게 4개의 주 스텝으로 구성된다.&lt;/li&gt;

&lt;li&gt;하나의 명령어 연산이 완료되면 CPU는 자동적으로 PC 레지스터가 가리키는 다음 명령어 처리를 개시한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;5register&quot;&gt;5. 레지스터(Register)&lt;/h2&gt;

&lt;p&gt;컴퓨터 학문에서 레지스터는 값을 저장할 수 있는 곳을 말한다. 따라서 메모리도 넓은 의미에서 레지스터에 해당하지만 일반적인 레지스터는, 메모리와 달리 규칙적이지 않으면서 소량이고, 단순한 값의 저장 기능뿐만 아니라 저장되는 값에 따라 특별한 기능이 이루어지는 곳을 말한다.&lt;/p&gt;&lt;p&gt;크게 CPU 레지스터와 특수 기능 레지스터(SFR: Special Function Register) 혹은 특수 목적 레지스터(Special Purpose Register),  그리고 입•출력 레지스터 등 세 가지로 분류할 수 있다.&lt;/p&gt;&lt;p&gt;CPU 내에 존재하는 CPU 레지스터들은 연산을 위해 피연산자를 임시 저장하는 용도가 대부분이고, 특수 목적 레지스터는 컴퓨터의 상태 설정 등 특수 용도로 사용된다.&lt;/p&gt;&lt;p&gt;입•출력 레지스터들은 입•출력 장치와 연결되어 그곳에 값을 저장하면 해당 장치로의 출력이 이루어지고, 읽으면 장치로부터 입력된 값이 얻어진다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;레지스터란 소량의 데이터를 특별한 용도로 저장하는 곳으로, 크게 CPU 레지스터와 특수 기능 레지스터, 그리고 입•출력 레지스터로 분류된다.&lt;/li&gt;

&lt;li&gt;CPU 레지스터는 주로 연산을 위한 피연산자의 임시 저장 용도로, 특수 목적 레지스터는 컴퓨터의 상태 설정 등 특수 용도로, 그리고 입•출력 레지스터는 주변 장치와의 입•출력 창구 용도로 사용된다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;6adressingmode&quot;&gt;6. 주소 지정 모드(Adressing Mode)&lt;/h2&gt;

&lt;p&gt;기계 명령어 포맷 중, 피연산자 부분은 메모리 주소뿐만 아니라 상수나 레지스터 번호 등 연산 코드에 따라 다르게 해석될 수 있다고 했다.&lt;/p&gt;&lt;p&gt;이처럼 기계 명령어의 피연산자 부분에 허용될 수 있는 값의 유형을 주소 지정 모드라 하는데, 크게 직접 • 간접, 절대 • 상대, 주소 • 상수 등 세 가지 관점에서 살펴볼 수 있다.&lt;/p&gt;

&lt;h4 id=&quot;directindirect&quot;&gt;- 직접 • 간접(Direct • Indirect) 주소 지정 모드&lt;/h4&gt;

&lt;p&gt;기계 명령어의 피연산자 값이, 연산이 적용될 값이 저장된 메모리 주소로 사용되면 이를 직접 주소라 부르는데, 가장 자주 사용된다. 이와 다르게, 피연산자 값이, 연산이 적용될 값이 저장된 곳의 주소가 저장된 메모리 주소로 사용될 때는 간접 주소라 부른다.&lt;/p&gt;

&lt;h4 id=&quot;absoluterelative&quot;&gt;- 절대 • 상대(Absolute • Relative) 주소 지정 모드&lt;/h4&gt;

&lt;p&gt;피연산자에 주어진 주소만으로 메모리를 참조하면 절대 주소라 부른다. 그런데 기계 명령어의 피연산자 주소값을 제 3의 저장 장소인 베이스 레지스터에 설정된 값에 더하여 메모리 참조 주소를 결정하는 방식이 있다. 이를 다르게 표현하면, 베이스 레지스터에 기준이 되는 주소값을 설정해 두고, 기계 명령어에는 그 기준 주소로부터 거리만을 표시하는 것으로 설명할 수 있다.&lt;/p&gt;&lt;p&gt;즉, 목적 주소를 기준 주소와 그로부터의 상대적 거리의 합으로 표현하는 방식인데, 이런 방식을 상대 주소라 부른다. 상대 주소 기법의 최대 장점은 기계 명령어를 수정하지 않고, 기준 레지스터값의 조정만으로 참조 메모리 영역을 쉽게 변경할 수 있다는 것이다.&lt;/p&gt;

&lt;h4 id=&quot;addressimmediate&quot;&gt;- 주소 • 즉시(Address • Immediate) 주소 지정 모드&lt;/h4&gt;

&lt;p&gt;위에서 살펴본 주소 지정 모드의 공통점은 피연산자에 주어진 값들이 모두 메모리 참조를 위한 주소로 사용된다는 것이다.&lt;/p&gt;&lt;p&gt;피연산자를 주소가 아닌 상수로 사용하는 경우를 상수(Constant) 혹은 즉시 주소라 부른다. &lt;/p&gt;

&lt;hr&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 64px; width: 64px; height: 64px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9990EA495F48AE4525&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9990EA495F48AE4525&quot; width=&quot;64&quot; height=&quot;64&quot; alt=&quot;컴퓨터시스템&quot; filename=&quot;project.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 64px; height: 64px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>컴구 이야기</category>
      <category>운영체제</category>
      <category>컴퓨터구조</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/254</guid>
      <comments>https://boycoding.tistory.com/254#entry254comment</comments>
      <pubDate>Fri, 28 Aug 2020 16:13:11 +0900</pubDate>
    </item>
    <item>
      <title>1. 훑어보는 운영체제</title>
      <link>https://boycoding.tistory.com/253</link>
      <description>&lt;h1 id=&quot;&quot;&gt;운영체제&lt;/h1&gt;
&lt;p&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;운영 체제&lt;/strong&gt;(運營 體制, 문화어: 조작 체계) 또는 오퍼레이팅 시스템(영어: operating system, &lt;strong&gt;OS&lt;/strong&gt;)은 시스템 하드웨어를 관리할 뿐 아니라 응용 소프트웨어를 실행하기 위하여 하드웨어 추상화 플랫폼과 공통 시스템 서비스를 제공하는 시스템 소프트웨어이다. -위키백과-&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;컴퓨터의 관점에서 일들이 처리되는 전형적인 형태는 &quot;입력 -&amp;gt; 처리(가공, 계산, 검색, 정렬 저장 등) -&amp;gt;출력 : IPO(Input-Process-Output) 모델이다.&lt;/p&gt;&lt;p&gt;즉, 사용자는 처리를 위한 요구를 키보드, 마우스 등 입력 장치를 통해서 입력하고, 그 결과를 모니터나 프린터 등 출력 장치를 통해 확인하게 되는데, 이러한 기본적인 컴퓨터 동작을 총괄하는 소프트웨어가 운영체제다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;운영체제의 가장 기본 역할 중 하나는 사용자에게 하드웨어인 입출력 장치를 편리하게 사용할 수 있게 하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;

&lt;h2 id=&quot;1&quot;&gt;1. 주요 기능&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;컴퓨터의 사용자와 하드웨어 사이에서 중개 역할을 담당&lt;/li&gt;

&lt;li&gt;컴퓨터의 자원 관리자 역할을 담당&lt;/li&gt;

&lt;li&gt;컴퓨터의 자원과 사용자 정보를 보호&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;

&lt;h2 id=&quot;2&quot;&gt;2. 실체 및 활동&lt;/h2&gt;

&lt;p&gt;운영체제의 목적을 한마디로 요약하면 컴퓨터 사용자들에게 서비스를 제공하는 것이며, 그 실체는 하나의 거대한 프로그램으로 메모리에 상주한다.&lt;/p&gt;&lt;p&gt;사용자가 정당한 서비스를 요청하면 운영체제는 그 요청을 받아 처리하고 결과를 되돌려준다.&lt;/p&gt;&lt;p&gt;사용자가 운영체제에 서비스를 요청하는 방법은, 시스템에 탑재된 프로그래밍 언어를 사용해서 함수 형태로 주어진 운영체제 진입 전용 라이브러리(시스템 콜 or 운영체제 인터페이스)를 호출하는 프로그램을 작성하여 실행시키는 것이다.&lt;/p&gt;

&lt;hr&gt;
&lt;h2 id=&quot;3&quot;&gt;3. 유형(발전과정)&lt;/h2&gt;

&lt;p&gt;사용자의 프로그램들이 실행되는 방식을 기준으로 살펴본다.&lt;/p&gt;

&lt;h3 id=&quot;1singlestreambatchprocessingsystems&quot;&gt;1 - 단일 프로그래밍 일괄 처리 시스템(Single-stream Batch Processing Systems)&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;일괄 처리 시스템&lt;/strong&gt;이란 사용자들의 프로그램을 일괄적으로 일시에 모은 후, 차례차례 실행시켜주는 운영체제 환경으로 말한다.&lt;/p&gt;&lt;p&gt;이런 유형의 운영체제는 1950년대 초창기의 천공 카드가 있으며, 카드 다발을 카드 입력장치에 쌓아두고 운영체제는 처리 중인 프로그램이 종료할 때마다 쌓인 카드 다발로부터 다음 처리할 프로그램으로 읽어 들였다.&lt;/p&gt;&lt;p&gt;단일 프로그래밍 일괄 처리 시스템은, 사용자 프로그램을 수작업으로 메모리에 적재하던 기존의 프로그램 실행 방식을 운영체제에 의한 자동 적재방식으로 발전시켰다는 점에서 컴퓨터 진화에 크게 기여했다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;운영체제는 오직 하나의 프로그램만을 적재하여 차례차례 실행시킨다.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;적재된 프로그램이 오직 하나밖에 없었으므로 사용자 프로그램이 입출력을 요구했을 때 운영체제는 입출력이 완료될 때까지 프로세서(CPU)를 대기시켜야 했으므로 허비하는 시간이 많았다.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;2multiprogrammingbatchprocessingsystems&quot;&gt;2 -  다중 프로그래밍 일괄 처리 시스템(Multi-programming Batch Processing Systems)&lt;/h3&gt;

&lt;p&gt;사용자가 컴퓨터에 접근하지 않고 프로그램을 작성하여 관리자에게 제출하고, 제출된 프로그램들은 한 곳에 모인 후 차례차례 처리된다는 점에서는 단일 프로그래밍 일괄 처리 시스템과 같다.&lt;/p&gt;&lt;p&gt;그러나, &lt;strong&gt;다중 프로그래밍 일괄 처리 시스템&lt;/strong&gt;은 두 개 이상의 프로그램을 동시에 적재할 수 있다는 점에서 다르다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;프로그램을 동시에 여러 개 적재시켜 실행 중인 프로그램이 입출력을 요구하면 그 입출력 작업이 하드웨어에서 진행되는 동안에 다른 프로그램을 실행시켜줌으로써 프로세서의 유휴 시간을 줄였다.&lt;/li&gt;

&lt;li&gt;그러나 적재된 프로그램들의 완료 시간이 다른 프로그램의 입출력 요구 빈도에 따라 큰 영향을 받으므로 각 프로그램의 완료 시간의 편차가 심하다. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;현재 실행 중인 프로그램이 입출력을 요구하면 반드시 프로세서를 다른 프로그램에 양보해야 하고, 반대로 현재 실행 중인 프로그램이 입출력하지 않는다면 다른 프로그램은 실행 중인 그 프로그램이 완료될 때까지 프로세서를 차지할 기회가 주어지지 않기 때문이다.&lt;/p&gt;

&lt;h3 id=&quot;3timesharingsystems&quot;&gt;3. 시분할 시스템(Time-Sharing Systems)&lt;/h3&gt;

&lt;p&gt;다중 프로그래밍 일괄 처리 시스템은 현재 처리 중인 프로그램이 입출력을 요구하거나 완전히 종료되는 상황이 발생했을 때만 다른 프로그램 중의 하나에 처리 기회가 돌아간다. 이 시스템은 일괄 처리 환경에서는 큰 문제가 되지 않았지만, 다수의 사용자가 단말기를 통해 컴퓨터에 동시에 접속하는 대화적 업무 환경에서는 컴퓨터에 접속한 사용자들이 요청한 프로그램들의 응답 속도 편차가 심하고 응답 예측이 불가능하기 하므로 적합하지 않다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;시분할 시스템&lt;/strong&gt;은 시간을 짧게 분할하여 여러 개의 프로그램을 골고루 처리해 주는데, 이때 짧게 분할된 시간을 타임 퀀텀 혹은 타임 슬라이스라고 한다. 즉, 하나의 프로그램을 1회에 최대 타임 퀀텀만큼의 시간 동안만 처리하고, 처리 기회를 다른 프로그램에 넘기며 이 과정을 끊임없이 반복한다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;여러 개의 프로그램을 짧은 시간(타임 퀀텀) 동안 조금씩 번갈아 가면서 처리함으로써 사용자에게 시스템을 독차지하고 있다는 느낌을 준다.&lt;/li&gt;

&lt;li&gt;다수의 사용자들이 단말기를 통해서 컴퓨터에 동시 접속하는 대화적 처리 환경에 적합하다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;만약 현재 처리 중인 프로그램이 타임 퀀텀을 모두 소진하지 못하고 입출력등의 대기 상황이 발생하면 처리 기회는 즉시 다음 프로그램에 돌아간다.&lt;/p&gt;

&lt;h3 id=&quot;4parallelprocessingsystems&quot;&gt;4 - 병렬 처리 시스템(Parallel Processing Systems)&lt;/h3&gt;

&lt;p&gt;CPU가 하나 장착된 기존의 단일 프로세서 시스템의 성능을 향상하기 위해 다중 프로세서 시스템이 출현하였다. CPU가 여러 개이므로 CPU 개수만큼의 프로그램들이 병렬로 처리될 수 있는데, 이를 지원하기 위한 운영체제는 한층 더 복잡하고 고도화해졌다&lt;/p&gt;&lt;p&gt;병렬 처리 시스템의 목적은 단순히 성능 향상뿐만 아니라 프로세서의 고장에도 처리 기능을 완전히 잃지 않는 결함 허용에 있다. 예를 들어 2개의 프로세서로 구성된 병렬 컴퓨터는, 하나의 프로세서가 고장 났을 때 성능은 약간 저하되더라도 처리 기능은 여전히 유지하므로 높은 신뢰성을 가지게 된다.&lt;/p&gt;&lt;p&gt;일반적으로 &lt;strong&gt;병렬 처리(Parallel Processing)&lt;/strong&gt;는 어느 시점을 기준으로 보았을 때 2개 이상의 프로그램을 처리하는 것을 말하고, &lt;strong&gt;병행 처리(Concurrent Processing)&lt;/strong&gt;는 단일 프로세서 시스템에서 일정 기간 2개 이상의 프로그램을 번갈아 처리하는 것을 말한다. 즉, 병행 처리는 어느 한 시점에서 보면 오직 하나의 프로그램만이 처리된다.&lt;/p&gt;&lt;p&gt;다중 프로세서 시스템을 넓게 보면 분산처리 시스템의 일종으로 볼 수 있는데, 프로세서들이 시스템 버스를 통해서 메모리, 주변 장치, 클록 등을 공유하므로 강결합 시스템(Tightly-coupled System)이라고 부른다.&lt;/p&gt;&lt;p&gt;다중 프로세서 시스템에서 처리기, 메모리 주변장치 등을 연결하는 버스는 그 구성 형태에 따라 단일 공유 버스, 다중 버스, 크로스바 스위치 버스, 계층 버스, 다중 포트 메모리 버스 등으로 분류된다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;병렬 처리 시스템은 여러 개의 프로세서(CPU)를 이용하여 여러 개의 프로그램을 병렬로 처리한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;5distributedcomputingsystems&quot;&gt;5 - 분산 처리 시스템(Distributed Computing Systems)&lt;/h3&gt;

&lt;p&gt;여러 개의 독립된 시스템을 버스가 아닌 네트워크로 연결하여 주어진 일을 분담하여 처리함으로써 병렬 처리 그 이상의 효과를 얻고자 하는 경우를 &lt;strong&gt;분산 처리&lt;/strong&gt;라 한다. 여기서 독립된 시스템이란 독자적인 하드웨어(프로세서, 메모리 등)와 운영체제를 가진 시스템을 의미한다.&lt;/p&gt;&lt;p&gt;분산 처리 시스템은, 프로세서들이 버스를 통해 연결되는 다중 처리기 시스템보다 결합 정도가 낮으므로 약결합(Loosely-coupled System)이라 부르기도 하고, 시스템들이 대역폭이 높은 네트워크로 가깝게 연결되므로 &lt;strong&gt;클러스터 시스템(Clustered System)&lt;/strong&gt;이라 부르기도 한다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;큰 문제를 여러 개의 독립된 시스템에 분할 배분하여 병렬로 처리함으로써 처리 속도 및 전체적인 처리량 향상&lt;/li&gt;

&lt;li&gt;독립된 시스템들을 연결함으로써 각 시스템에 산재해 있는 자원의 활용도 향상&lt;/li&gt;

&lt;li&gt;연결된 일부 시스템이 다운되더라도 전체 시스템의 기능을 유지함으로써 신뢰성 향상&lt;/li&gt;

&lt;li&gt;전체적인 시스템 처리 성능이 필요한 경우 새로운 시스템을 간편하게 추가 연결함으로써 점진적 시스템 향상&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;6realtimesystems&quot;&gt;6 - 실시간 시스템(Real-Time Systems)&lt;/h3&gt;

&lt;p&gt;앞에선 본 운영체제들의 공통점 중 하나는 처리 대상인 프로그램들의 처리재개 마감 시한을 설정할 수 없다는 것인데, 이는 실시간 처리가 요구되는 환경이라면 허용될 수 없는 중대한 요인이다.&lt;/p&gt;&lt;p&gt;예를 들어, 크루즈 미사일을 통제하는 컴퓨터가 일반적인 운영체제 환경이라면 그 프로그램에 대한 처리 재개 시기가 불규칙하여 미사일이 추락할 수 있다. 즉, 측정 센서로부터 고도가 입력되면 이를 기다리던 프로그램은 운영체제에 의해 지정된 기간 내에 처리가 재개되어 미사일 날개를 신속하게 조정할 수 있어야 한다.&lt;/p&gt;&lt;p&gt;실간 처리 환경을 지원하는 실시간 운영체제는 처리 대상인 여러 개의 프로그램 각각의 특성을 고려하여, 모든 프로그램에 처리 마감 시한을 보장해 준다. 이를 위해서는 운영체제 기능 일부를 포기해야 하므로 유닉스(리눅스)나 윈도우는 완벽한 실시간 서비스가 아닌 실시간에 가까운 &lt;strong&gt;연성 실시간 시스템(Soft Real-Time System)&lt;/strong&gt;이다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hard Real-Time System: 프로그램들의 처리 마감 시한을 엄격하게 보장&lt;/li&gt;

&lt;li&gt;Soft Real-Time System: 실시간 처리를 위한 최소한의 서비스만 제공&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 64px; width: 64px; height: 64px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99FB1E395F38E9160D&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99FB1E395F38E9160D&quot; width=&quot;64&quot; height=&quot;64&quot; alt=&quot;운영체제&quot; filename=&quot;brands.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 64px; height: 64px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>컴구 이야기</category>
      <category>운영체제</category>
      <category>컴퓨터구조</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/253</guid>
      <comments>https://boycoding.tistory.com/253#entry253comment</comments>
      <pubDate>Sun, 16 Aug 2020 16:57:36 +0900</pubDate>
    </item>
    <item>
      <title>09.12 - 상수 클래스 객체와 멤버 함수 (Const class object and member function)</title>
      <link>https://boycoding.tistory.com/252</link>
      <description>&lt;h1 id=&quot;constclassobjectandmemberfunction&quot;&gt;상수 클래스 객체와 멤버 함수 (Const class object and member function)&lt;/h1&gt;

&lt;p&gt;이전 포스트 &lt;a href=&quot;https://boycoding.tistory.com/156?category=1007180&quot;&gt;상수 (const, constexpr, and symbolic constants)&lt;/a&gt; 에서 &lt;code&gt;const&lt;/code&gt; 키워드를 통해 상수를 만들 수 있고, 모든 상수 변수는 생성할 때 초기화해야 한다는 것을 배웠다. 기본 상수 자료형이면 &lt;strong&gt;복사&lt;/strong&gt;, &lt;strong&gt;직접&lt;/strong&gt; 또는 &lt;strong&gt;유니폼&lt;/strong&gt; 초기화를 통해 초기화를 수행할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;const int value1 = 5;      // 복사 초기화
const int value2(7);       // 직접 초기화
const int value3 { 9 };    // 유니폼 초기화(C++ 11)
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;

&lt;h2 id=&quot;1constclass&quot;&gt;1. 상수 클래스 (Const class)&lt;/h2&gt;

&lt;p&gt;마찬가지로 클래스 객체도 &lt;code&gt;const&lt;/code&gt; 키워드를 사용해서 상수 객체로 만들 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;const Date date1;                   // 기본 생성자를 이용한 초기화
const Date Date2(2020, 8, 7);       // 인수가 있는 생성자를 이용한 초기화
const Date Date3 {2020, 8, 7};      // 인수가 있는 생성자를 이용한 초기화(C++ 11)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;상수 객체가 생성자를 통해 초기화되면 객체의 멤버 변수의 값을 직접 변경하거나 멤버 변수의 값을 설정하는 멤버 함수를 호출하는 작업 등 멤버 변수를 수정하려는 어떠한 시도도 허용되지 않는다. &lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Something
{
public:
    int m_Value;

    Somting() : m_Value(0) { }

    void SetValue(int value) { m_Value = value; }
    int GetValue() { return m_Value; }
};

int main()
{
    const Something something{}; // 기본 생성자 호출

    something.m_Value = 5;      // 컴파일 에러: const 위반
    something.SetValue(5);      // 컴파일 에러: const 위반

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 코드에서 상수 객체의 멤버 변수값을 바꾸는 행위는 모두 const 위반을 하므로 컴파일 에러가 발생한다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;2constmemberfunction&quot;&gt;2. 상수 멤버 함수 (Const member function)&lt;/h2&gt;

&lt;p&gt;아래 코드를 봐보자:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;std::cout &amp;lt;&amp;lt; something.GetValue();  // 컴파일 에러: const 위반
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;놀랍게도 &lt;em&gt;GetValue()&lt;/em&gt; 멤버 함수는 멤버 변수의 값을 변경하는 어떠한 작업도 하지 않지만, 컴파일러 에러를 일으킨다. &lt;strong&gt;상수 클래스 객체는 상수 멤버 함수만 명시적으로 호출할 수 있다.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;상수 멤버 함수(const member function)는 호출한 객체의 멤버 변수를 변경할 수 없는 멤버 함수를 말한다. (멤버 함수의 선언 마지막에 const 키워드를 추가함으로써 사용한다.)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;GetValue()&lt;/em&gt;를 상수 멤버 함수로 만들려면 &lt;code&gt;const&lt;/code&gt; 키워드를 함수 선언의 매개 변수 목록 뒤에 추가하기만 하면 된다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Something
{
public:
    int m_Value;

    Somting() : m_Value(0) { }

    void SetValue(int value) { m_Value = value; }
    int GetValue() const { return m_Value; } // 함수 선언에서 매개 변수 목록 뒤에 const 키워드를 추가한다.
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이제 &lt;em&gt;GetValue()&lt;/em&gt;는 상수 멤버 함수인데 이것은 아래와 같은 특징을 가지고 있다.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;상수 멤버 함수에서 멤버 변수 값을 바꾸는 행위를 할 수 없다.&lt;/li&gt;

&lt;li&gt;상수 멤버 함수 안에서 비-상수 멤버 함수를 호출할 수 없다.&lt;/li&gt;

&lt;li&gt;상수 클래스 객체에 대해서도 호출할 수 있다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;클래스 선언 외부에서 구현된 멤버 함수의 &lt;code&gt;const&lt;/code&gt; 키워드는 클래스 선언과 정의 모두에서 포함되어야 한다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Something
{
public:
    int m_Value;

    Somting() : m_Value(0) { }

    void SetValue(int value) { m_Value = value; }
    int GetValue() const; // 함수 선언에서 매개 변수 목록 뒤에 const 키워드를 추가한다.
}

int Something::GetValue() const // 정의에도 추가해야 한다.
{
    return m_Value;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;또한, 위에서 잠깐 설명한 특징에서 말한 것 처럼 상수 멤버 함수에서 멤버 변수의 값을 변경하거나 비-상수 멤버 함수를 호출하면 컴파일러는 오류를 발생시킨다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int Something::GetValue() const // 정의에도 추가해야 한다.
{
    m_Value = 3; // 컴파일 에러: 상수 멤버 함수는 멤버 변수의 값을 바꿀 수 없다.
    SetValue(3); // 컴파일 에러: 상수 멤버 함수는 비-상수 멤버 함수를 호출할 수 없다.

    return m_Value;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;마지막으로 &lt;strong&gt;C++ 클래스의 생성자는 멤버 변수를 초기화할 수 있어야 하므로 &lt;code&gt;const&lt;/code&gt; 키워드를 사용을 허용하지 않는다.&lt;/strong&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;3constreference&quot;&gt;3. 상수 참조(레퍼런스) (Const reference)&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;함수의 인자로 객체를 전달할 때 상수 참조(레퍼런스)를 이용하는 것이 일반적인 방법이다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;이전 포스트 참조로 전달(Pass by reference)에서 참조로 전달의 장점을 다루었다. 요약하자면, 값으로 전달의 경우 복사본이 만들어지게 되는데, 복사본이 필요하지 않은 경우가 대부분이므로 참조로 전달이 불필요한 복사를 하지 않아 효과적이라는 것이다. 또한, 프로그래머 무심코 함수 안에서 전달된 인수를 변경하는 것을 막기 위해 일반적으로 상수로 참조를 만든다는 것이다.&lt;/p&gt;&lt;p&gt;아래 코드에서 문제점을 찾아보자:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

class Date
{
private:
    int m_Year;
    int m_Month;
    int m_Day;

public:
    Date(int year, int month, int date)
    {
        SetDate(year, month, date);
    }

    void SetDate(int year, int month, int date)
    {
        m_Year  = year;
        m_Month = month;
        m_Day   = day;
    }

    int GetYear() { return m_Year; }
    int GetMonth() { return m_Month; }
    int GetDay() { returm m_Day; }
};

// date 객체를 복사하지 않기 위해 const 참조를 통해 date 객체를 전달하고 있다.
void PrintDate(const Date&amp;amp; date)
{
    cout &amp;lt;&amp;lt; date.GetYear() &amp;lt;&amp;lt; '/' &amp;lt;&amp;lt; date.GetMonth() &amp;lt;&amp;lt; '/' &amp;lt;&amp;lt; date.GetDay() &amp;lt;&amp;lt; endl;
}

int main()
{
    Date date{2020, 8, 7};

    PrintDate(date);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;정답은 &lt;strong&gt;&lt;code&gt;PrintDate()&lt;/code&gt; 함수 안에서 date 객체는 상수 객체로 취급된다는 것&lt;/strong&gt;이다. 그리고 상수 date 객체와 함께 &lt;em&gt;GetYear()&lt;/em&gt;, &lt;em&gt;GetMonth()&lt;/em&gt; 및 &lt;em&gt;GetDay()&lt;/em&gt; 멤버 함수를 호출하는데, 이 멤버 함수는 모두 상수 멤버 함수가 아니다. &lt;strong&gt;상수 객체에 대해 비-상수 멤버 함수를 호출할 수 없으므로 컴파일 오류가 발생할 것&lt;/strong&gt;이다.&lt;/p&gt;&lt;p&gt;해결법은 간단하다. 상수 멤버 함수로 만든다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Date
{
    // 생략..


    // getter를 모두 상수 멤버 함수로 만든다.
    int GetYear() const { return m_Year; }
    int GetMonth() const { return m_Month; }
    int GetDay() const { returm m_Day; }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이제 &lt;em&gt;PrintData()&lt;/em&gt; 함수 안에서 상수 date 객체의 &lt;em&gt;GetYear()&lt;/em&gt;, &lt;em&gt;GetMonth()&lt;/em&gt; 및 &lt;em&gt;GetDay()&lt;/em&gt;를 성공적으로 호출할 수 있다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;4overloadingconstandnonconstfunction&quot;&gt;4. 상수 멤버 함수와 비-상수 멤버 함수의 오버로딩 (Overloading const and non-const function)&lt;/h2&gt;

&lt;p&gt;마지막으로, 같은 멤버 함수에 대해 상수 버전과 비-상수 버전으로 오버로딩이 가능하다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;string&amp;gt;
using namespace std;

class Something
{
private:
    string m_Value;

public:
    Something(const string&amp;amp; value = &quot;&quot;): m_Value{value} {}

    string&amp;amp; GetValue() const { return m_Value; }  // 상수 버전
    string&amp;amp; GetValue() { return m_Value; }        // 비-상수 버전
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;멤버 함수의 상수 버전은 상수 객체에서 호출되며, 비-상수 버전은 비-상수 객체에서 호출된다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int main()
{
    Something something{};
    something.GetValue() = &quot;Hi&quot;; // 비-상수 버전 호출

    const Something something2{};
    something2.GetValue();       // 상수 버전 호출

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이와 같은 오버로딩은 반환 값에 대해서 상수/비-상수 차이가 필요할 때 사용한다. 위의 코드에서 GetValue()의 비-상수 버전은 비-상수 객체에서 m_Value 문자열의 값을 읽고 쓸 수 있게 하고, 상수 버전은 상수 객체에서 데이터를 수정할 수 없도록 강제하므로 유연하게 작동한다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;상수 참조로 전달하는 경우가 매우 많고 일반적이므로 &lt;code&gt;const&lt;/code&gt; 키워드에 친숙해져야 한다. 즉, 객체의 상태를 수정하지 않는 멤버 함수를 만드는 것이다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99F358425F2C747D07&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99F358425F2C747D07&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;cpp&quot; filename=&quot;cpp3.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt; color: rgb(93, 93, 93);&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt; color: rgb(93, 93, 93);&quot;&gt;번역: 이 포스트의 원문은 https://www.learncpp.com/cpp-tutorial/810-const-class-objects-and-member-functions/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <category>C++ 이야기/09. 객체지향 프로그래밍</category>
      <category>C++</category>
      <category>C++ 기초</category>
      <category>객체지향 프로그래밍</category>
      <category>클래스</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/252</guid>
      <comments>https://boycoding.tistory.com/252#entry252comment</comments>
      <pubDate>Fri, 7 Aug 2020 10:30:00 +0900</pubDate>
    </item>
    <item>
      <title>C++ 09.11 - 클래스 코드와 헤더 파일 (선언과 구현의 분리)</title>
      <link>https://boycoding.tistory.com/251</link>
      <description>&lt;h1 id=&quot;&quot;&gt;클래스 코드와 헤더 파일 (선언과 구현의 분리)&lt;/h1&gt;

&lt;h2 id=&quot;1&quot;&gt;1. 클래스 정의 외부에서 멤버 함수 정의하기&lt;/h2&gt;

&lt;p&gt;지금까지는 모든 클래스 정의 코드 내에서 바로 멤버 함수를 구현할 정도로 간단한 예제였다.&lt;/p&gt;&lt;p&gt;예를 들어, 아래와 같은 날짜 클래스를 보자:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Date
{
private:
  int m_Year;
  int m_Month;
  int m_Day;

public:
  Date(int year, int month, int day)
  {
    SetDate(year, month, day);
  }

  void SetDate(int year, int month, int day)
  {
    m_Year = year;
    m_Month = month;
    m_Day = day;
  }

  int GetYear() { return m_Year; }
  int GetMonth() { return m_Month; }
  int GetDat() { return m_Day; }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 예제 코드 정도는 멤버 함수의 양이 적으며, 코드가 복잡하지 않으므로 이해하기 쉽다.&lt;/p&gt;&lt;p&gt;그러나 클래스가 점점 길고 복잡해질 경우(=멤버 함수가 많아질 경우)에는 클래스 관리와 작업이 어려워질 수 있다. 또한, 이미 작성된 클래스를 사용하려면 클래스가 작동 방식이 아닌 공용 인터페이스(공개 멤버 함수)만 이해하면 된다. 멤버 함수의 세부 구현 사항은 방해만 될 뿐이다.&lt;/p&gt;&lt;p&gt;다행히도 C++에서는  &lt;strong&gt;클래스 정의 외부에서 클래스 멤버 함수를 정의&lt;/strong&gt;하는 방법으로 클래스의 '&lt;strong&gt;선언'&lt;/strong&gt;과 &lt;strong&gt;'구현'&lt;/strong&gt;을 분리할 수 있다. 이렇게 하려면 클래스의 멤버 함수를 일반 함수인 것처럼 정의하되, 클래스 이름과 &lt;strong&gt;범위 확인 연산자(scope resolution operator)&lt;/strong&gt; &lt;code&gt;::&lt;/code&gt;을 사용해서 외부에서 멤버 함수를 정의할 수 있다.&lt;/p&gt;&lt;p&gt;아래는 위 Date 클래스의 일부 멤버 함수를 클래스 외부에서 정의한 예제다: (실제 함수는 여전히 클래스 정의 내부에 존재하지만, 실제 구현의 외부에서 한다.)&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Date
{
private:
  int m_Year;
  int m_Month;
  int m_Day;

public:
  Date(int year, int month, int day);

  void SetDate(int year, int month, int day);

  int GetYear() { return m_Year; }
  int GetMonth() { return m_Month; }
  int GetDat() { return m_Day; }
};

// Date 클래스의 생성자
Date::Date(int year, int month, int day)
{
  SetDate(year, month, day)
}

// Date 클래스의 멤버 함수
void Date::SetDate(int year, int month, int day)
{
    m_Year = year;
    m_Month = month;
    m_Day = day;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;참고로 &lt;em&gt;GetYear()&lt;/em&gt;같은 멤버 함수는 코드 길이가 한 줄밖에 되지 않으므로 클래스 정의에 남겨두는 게 일반적이다.&lt;/p&gt;&lt;p&gt;아래는 멤버 초기화 리스트를 사용하는 생성자가 있는 또 다른 예제다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Calc
{
private:
    int m_value = 0;

public:
    Calc(int value=0): m_value(value) {}

    Calc&amp;amp; add(int value) { m_value  += value;  return *this; }
    Calc&amp;amp; sub(int value) { m_value -= value;  return *this; }
    Calc&amp;amp; mult(int value) { m_value *= value;  return *this; }

    int getValue() { return m_value ; }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;분리 후:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Calc
{
private:
    int m_value = 0;

public:
    Calc(int value=0);

    Calc&amp;amp; add(int value);
    Calc&amp;amp; sub(int value);
    Calc&amp;amp; mult(int value);

    int getValue() { return m_value; }
};

Calc::Calc(int value): m_value(value)
{
}

Calc&amp;amp; Calc::add(int value)
{
    m_value += value;
    return *this;
}

Calc&amp;amp; Calc::sub(int value) 
{
    m_value -= value;
    return *this;
}

Calc&amp;amp; Calc::mult(int value)
{
    m_value *= value;
    return *this;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;

&lt;h2 id=&quot;2&quot;&gt;2. 클래스 정의 헤더 파일 만들기&lt;/h2&gt;

&lt;p&gt;이전 포스트 &lt;a href=&quot;https://boycoding.tistory.com/144?category=1006674&quot;&gt;헤더 파일&lt;/a&gt;에서 여러 파일 또는 여러 프로젝트에서 함수 선언을 사용하기 위해 헤더 파일을 작성하는 방법을 배웠다. 클래스 또한 다르지 않다. 여러 파일 또는 여러 프로젝트에서 쉽게 재사용할 수 있도록 클래스를 헤더 파일에 넣을 수 있다. 일반적으로 클래스 정의는 클래스와 이름이 같은 &lt;code&gt;헤더 파일(.h)&lt;/code&gt;에, 클래스 외부에 정의된 멤버 함수는 클래스와 이름이 같은 &lt;code&gt;.cpp&lt;/code&gt; 파일에 넣는다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;이 방식으로 선언과 구현을 분리할 수 있다.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;Date.h&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Date
{
private:
  int m_Year;
  int m_Month;
  int m_Day;

public:
  Date(int year, int month, int day);

  void SetDate(int year, int month, int day);

  int GetYear() { return m_Year; }
  int GetMonth() { return m_Month; }
  int GetDat() { return m_Day; }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;Data.cpp&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &quot;Date.h&quot;

// Date 클래스의 생성자
Date::Date(int year, int month, int day)
{
    SetDate(year, month, day);
}

// Date 클래스의 멤버 함수
void Date::SetDate(int year, int month, int day)
{
    m_Month = month;
    m_Day = day;
    m_Year = year;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이제 Data 클래스를 사용하는 다른 헤더 또는 코드 파일은 간단히 &lt;code&gt;#include Date.h&lt;/code&gt;를 포함시키기만 하면 된다. (주의: Date.cpp 또한 링커가 Date가 어떻게 구현됐는지 알 수 있기 위해 Date.h를 포함시켜야 한다. )&lt;/p&gt;

&lt;h3 id=&quot;defaultparameter&quot;&gt;기본 매개 변수 (Default Parameter)&lt;/h3&gt;

&lt;p&gt;멤버 함수의 기본 매개 변수(default parameter)는 클래스 정의(헤더 파일)에 선언되어야 한다.&lt;/p&gt;&lt;p&gt;&lt;code&gt;Calc.h&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Calc
{
private:
    int m_value = 0;

public:
    // 기본 매개 변수(default parameter)는 클래스 정의(헤더 파일)에 선언되어야한다. 
    Calc(int value=0);

    //...
};
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;

&lt;h2 id=&quot;3cpp&quot;&gt;3. 라이브러리와 cpp 파일&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;iostream.h&lt;/code&gt;, &lt;code&gt;string.h&lt;/code&gt;, &lt;code&gt;vector.h&lt;/code&gt;와 같은 표준 라이브러리는 모두 클래스 정의와 구현이 분리되어있다. 컴파일러는 문법적으로 올바른 프로그램이 작성되었는지 확인하기 위해 헤더 파일의 선언만 필요로 하므로 &lt;code&gt;iostream.cpp&lt;/code&gt;와 같은 파일은 프로젝트에 추가할 필요가 없다. 이처럼 C++ 표준 라이브러리에 속하는 클래스에 대한 구현(cpp 파일)은 링크 단계에서 링크된 미리 컴파일된 파일에 포함되어있으므로 구현 소스 코드를 볼 수 없다.&lt;/p&gt;&lt;p&gt;.h 및 .cpp가 모두 제공되는 일부 오픈 소스 코드를 제외한 대부분의 타사 라이브러리는 미리 컴파일된 라이브러리 파일과 함께 헤더 파일만 제공한다. 그 이유는:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;필요할 때마다 다시 컴파일하는 것보다 미리 컴파일된 라이브러리 연결하는 것이 더 빠르다.&lt;/li&gt;

&lt;li&gt;미리 컴파일된 라이브러리는 다른 많은 애플리케이션에 의해 공유될 수 있다. (컴파일된 코드는 그 프로그램에서만 사용할 수 있다.)&lt;/li&gt;

&lt;li&gt;지적 재산권 보호 (코드를 못 훔치도록..)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;자신의 코드를 선언(헤더)과 구현(코드 파일)으로 분리하는 것은 관리와 작업에 있어 좋은 형태일 뿐만 아니라, 자신만의 라이브러리를 쉽게 만들 수 있다. 자신만의 라이브러리를 만드는 것은 이 포스트의 범위를 벗어나지만, 선언과 구현을 분리하는 것은 그렇게 하기 위한 필수 조건이다.&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99C94F345F20034519&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99C94F345F20034519&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;cpp&quot; filename=&quot;cpp3.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt; color: rgb(93, 93, 93);&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt; color: rgb(93, 93, 93);&quot;&gt;번역: 이 포스트의 원문은 https://www.learncpp.com/cpp-tutorial/89-class-code-and-header-files/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <category>C++ 이야기/09. 객체지향 프로그래밍</category>
      <category>C++</category>
      <category>C++ 기초</category>
      <category>객체지향 프로그래밍</category>
      <category>클래스</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/251</guid>
      <comments>https://boycoding.tistory.com/251#entry251comment</comments>
      <pubDate>Tue, 28 Jul 2020 19:53:38 +0900</pubDate>
    </item>
    <item>
      <title>C++ 09.10 - this 포인터</title>
      <link>https://boycoding.tistory.com/250</link>
      <description>&lt;h1 id=&quot;this&quot;&gt;this 포인터&lt;/h1&gt;

&lt;p&gt;객체 지향 프로그래밍에서 가장 많은 질문 중 하나는 &quot;클래스의 멤버 함수를 호출할 때 C++는 어떻게 호출할 객체(인스턴스)를 찾는가?&quot; 이다. 이 질문에 대한 정답은 &lt;strong&gt;&lt;code&gt;this&lt;/code&gt;라는 숨겨진 포인터를 사용한다&lt;/strong&gt;는 것이다. &lt;/p&gt;&lt;p&gt;&lt;code&gt;this&lt;/code&gt;를 자세히 알아보자:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Simple
{
private:
    int m_ID;

public:
    Simple(int id)
    {
        SetID(id);
    }

    void SetID(int id)
    {
        m_ID = id;
    }

    int GetID()
    {
        return m_ID;
    }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 클래스를 이용한 간단한 프로그램:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int main()
{
    Simple simple(1);
    simple.SetID(2);
    cout &amp;lt;&amp;lt; simple.GetID() &amp;lt;&amp;lt; endl; // 2

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;simple.SetID(2);&lt;/code&gt;를 호출하면 C++은 &lt;code&gt;SetID()&lt;/code&gt; 멤버 함수가 객체(인스턴스) &lt;strong&gt;&lt;em&gt;simple&lt;/em&gt;&lt;/strong&gt;에서 작동해야 한다는 것을 알고 &lt;em&gt;m_ID&lt;/em&gt;는 &lt;em&gt;simple.m_ID&lt;/em&gt;를 참조한다. 이 과정이 어떻게 작동하는지 살펴보자.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;this-1&quot;&gt;숨겨져 있는 this 포인터&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;simple.SetID(2);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;멤버 함수 setID()는 하나의 인수만 가지고 호출하는 것처럼 보이지만 실제로는 두 개의 인수를 가지고 있다! 이 코드를 컴파일하면 다음과 같이 변환된다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;simple.SetID(&amp;amp;simple, 2); // 이렇게 함수 인수가 변환된다.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;즉, 멤버 함수의 인수로 객체(인스턴스)의 주소가 전달된다는 것이다.&lt;/p&gt;&lt;p&gt;멤버 함수 호출에 매개 변수가 추가되었으므로 멤버 함수의 정의 역시 매개 변수를 하나 더 받도록 수정되어야 한다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void SetID(int id)
{
    m_ID = id;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 멤버 함수의 정의는 컴파일러에 의해 다음과 같이 변환된다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void SetID(Simple* const this, int id) // 이렇게 함수 정의가 변환된다.
{
    this-&amp;gt;m_ID = id;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;컴파일할 때 컴파일러는 &lt;code&gt;this&lt;/code&gt;를 함수에 추가한다. 이 &lt;strong&gt;this 포인터는 멤버 함수가 호출된 객체의 주소를 가리키는 숨겨진 포인터다.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;멤버 함수 안에서 모든 클래스의 멤버 변수의 참조 또한 &lt;code&gt;this-&amp;gt;&lt;/code&gt;가 추가되는 형식으로 변환되므로 위 코드에서는 &lt;code&gt;m_ID&lt;/code&gt;에 대한 참조가 &lt;code&gt;this-&amp;gt;m_ID&lt;/code&gt;로 변환되었다. 결과적으로 &lt;code&gt;this-&amp;gt;m_ID&lt;/code&gt;는 &lt;code&gt;simple-&amp;gt;m_ID&lt;/code&gt;가 되는 것이다.&lt;/p&gt;

&lt;h3 id=&quot;&quot;&gt;- 컴파일러 수행 과정 요약&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;simple.SetID(2); 를 호출하면 컴파일러는 실제로 simple.SetID(&amp;amp;simple, 2); 로 변환해서 호출한다.&lt;/li&gt;

&lt;li&gt;setID() 멤버 함수 내부에서 'this' 포인터는 simple 객체의 주소를 가리킨다.&lt;/li&gt;

&lt;li&gt;SetID() 내부의 모든 멤버 변수 앞에는 &lt;code&gt;this-&amp;gt;&lt;/code&gt;가 붙는다. 그래서 m&lt;em&gt;ID = id; 는 실제로 this-&amp;gt;m&lt;/em&gt;ID = id;로 변환된다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;이 모든 과정은 컴파일러에 의해 자동으로 수행되므로 세세하게 기억하지 않아도 된다. 그러나 기억해야 할 것은 &lt;strong&gt;모든 멤버 함수는 함수가 호출된 객체(인스턴스)를 가리키는 &lt;code&gt;this&lt;/code&gt; 포인터를 가지고 있다&lt;/strong&gt;는 것이다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;1this&quot;&gt;1. this는 항상 호출된 객체(인스턴스)를 가리킨다.&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int main()
{
    Simple A(1); // =Simple A(&amp;amp;A, 1); -&amp;gt; Simple 클래스의 생성자 내부에서: this = &amp;amp;A    
    Simple B(2); // =Simple B(&amp;amp;B, 2); -&amp;gt; Simple 클래스의 생성자 내부에서: this = &amp;amp;B  
    A.SetID(3);  // =A.SetID(&amp;amp;A, 3);  -&amp;gt; 멤버 함수 SetID 내부에서: this = &amp;amp;A 
    B.SetID(4);  // =B.SetID(&amp;amp;B, 4);  -&amp;gt; 멤버 함수 SetID 내부에서: this = &amp;amp;B 

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;지금까지 설명한 내용처럼 this 포인터는 어떤 객체(인스턴스)에서 멤버 함수를 호출했는지에 따라 가리키는 주소가 다르다. &lt;/p&gt;&lt;p&gt;또한, this는 함수 매개 변수에 불과하므로 클래스에 메모리 사용량을 추가하지 않는다. (함수가 실행되는 동안에만 스택에 매개 변수 쌓인다.)&lt;/p&gt;

&lt;h2 id=&quot;2this&quot;&gt;2. 명시적으로 this를 참조해야 하는 경우가 있다.&lt;/h2&gt;

&lt;p&gt;this를 명시적으로 참조해서 유용할 수 있는 몇 가지 경우가 있다.&lt;/p&gt;&lt;p&gt;첫째: 멤버 변수와 이름이 같은 매개 변수를 가진 생성자(또는 멤버 함수)가 있는 경우: this를 사용해서 구분할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Something
{
private:
    int data;

public:
    Something(int data)
    {
        this-&amp;gt;data = data;
        // this-&amp;gt;data는 멤버 변수이고,
        // data는 매개 변수
    }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 예제 코드에서는 멤버 변수의 이름과 매개 변수의 이름이 같다. 이럴때 this-&amp;gt; 를 명시적으로 사용해서 구분할 수 있지만, 멤버 변수 이름에 m_과 같은 접두사를 이용해서 이름 중복을 방지하는 게 좀 더 바람직하다. (여기서 m은 member의 약자를 나타내는 코딩 관행이다.)&lt;/p&gt;

&lt;h2 id=&quot;3&quot;&gt;3. 멤버 함수 체이닝 기법&lt;/h2&gt;

&lt;p&gt;둘째로 클래스 멤버 함수가 작업 중이던 객체(인스턴스)를 반환하는 방식이 유용할 때가 종종있다. 이렇게 하면 같은 객체의 여러 멤버 함수를 연속해서 호출할 수 있는 방법이다.&lt;/p&gt;&lt;p&gt;다음 클래스를 보자:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Calc
{
private:
    int m_Value = 0;

public:
    void Add(int value) { m_Value += value;}
    void Sub(int value) { m_Value -= value;}
    void Mul(int value) { m_Value *= value;}

    int GetValue() { return m_Value; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 클래스와 함께 5를 더하고 3을 빼고 4를 곱하려면 다음과 같이 해야 한다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int main()
{
    Calc calc;

    calc.Add(5); // return void
    calc.Sub(3); // return void
    calc.Mul(4); // return void

    cout &amp;lt;&amp;lt; calc.GetValue() &amp;lt;&amp;lt; endl; // 8
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;그러나 만약 각각의 멤버 함수가 &lt;strong&gt;this를 반환한다면 체이닝 기능이 있는 새로운 버전의 Calc를 만들 수 있다.&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Calc
{
private:
    int m_Value = 0;

public:
    Calc&amp;amp; Add(int value) { m_Value += value; return *this; }
    Calc&amp;amp; Sub(int value) { m_Value -= value; return *this; }
    Calc&amp;amp; Mul(int value) { m_Value *= value; return *this; }

    int GetValue() { return m_Value; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이제 &lt;em&gt;Add()&lt;/em&gt;, &lt;em&gt;Sub()&lt;/em&gt; 및 &lt;em&gt;Mul()&lt;/em&gt; 멤버 함수는 &lt;code&gt;*this&lt;/code&gt;를 반환한다. 따라서 다음과 같이 프로그래밍할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int main()
{
    Calc calc;

    calc.Add(5).Sub(3).Mul(4);

    cout &amp;lt;&amp;lt; calc.GetValue() &amp;lt;&amp;lt; endl; // 8
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;코드 3줄을 단 1줄로 바꾸었다!&lt;/p&gt;&lt;p&gt;각 멤버 함수가 호출되면서 &lt;em&gt;calc&lt;/em&gt; 객체의 &lt;em&gt;m_Value&lt;/em&gt;를 수정하고 &lt;code&gt;*this&lt;/code&gt;를 반환함으로써 자기 자신(인스턴스)을 반환한다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;1. calc.Add(5).Sub(3).Mul(4);
2. (calc.Add(5)).Sub(3).Mul(4); // m_Value = 0 + 5
3. (calc.Sub(3)).Mul(4);        // m_Value = 5 - 3
4. calc.Mul(4);                 // m_Value = 2 * 4; =&amp;gt; 8
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;

&lt;h2 id=&quot;-1&quot;&gt;요약&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;this&lt;/code&gt; 포인터는 모든 멤버 함수에 추가되는 숨겨진 매개 변수다. &lt;/li&gt;

&lt;li&gt;호출된 객체의 주소를 가리키는 상수 포인터다.&lt;/li&gt;

&lt;li&gt;상수 포인터 자료형이므로 포인터 자체가 다른 것을 가리키도록 할 수는 없다.&lt;/li&gt;

&lt;li&gt;멤버 변수 이름과 멤버 함수의 매개 변수 이름이 같으면 명시적으로 this를 참조해서 구분할 수 있다. (ex: &lt;code&gt;this-&amp;gt;data = data;&lt;/code&gt;)&lt;/li&gt;

&lt;li&gt;&lt;code&gt;*this&lt;/code&gt;를 반환하는 방식으로 &lt;strong&gt;함수 체이닝 기법&lt;/strong&gt;을 만들 수 있다.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/990D003F5F180A8C1E&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F990D003F5F180A8C1E&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;cpp&quot; filename=&quot;cpp3.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;&amp;nbsp;번역: 이 포스트의 원문은 https://www.learncpp.com/cpp-tutorial/8-8-the-hidden-this-pointer/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <category>C++ 이야기/09. 객체지향 프로그래밍</category>
      <category>C++</category>
      <category>C++ 기초</category>
      <category>this</category>
      <category>객체지향 프로그래밍</category>
      <category>생성자</category>
      <category>소멸자</category>
      <category>클래스</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/250</guid>
      <comments>https://boycoding.tistory.com/250#entry250comment</comments>
      <pubDate>Wed, 22 Jul 2020 18:45:38 +0900</pubDate>
    </item>
    <item>
      <title>C++ 09.09 - 소멸자 (Destructor)</title>
      <link>https://boycoding.tistory.com/249</link>
      <description>&lt;h1 id=&quot;destructor&quot;&gt;소멸자 (Destructor)&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;소멸자&lt;/strong&gt;는 객체가 소멸될 때 자동으로 실행되는 클래스의 멤버 함수다. &lt;strong&gt;생성자는 클래스의 초기화를 돕도록 설계됐지만 소멸자는 청소를 돕도록 설계되었다.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;지역에서 생성된 객체가 지역 범위를 벗어나거나 동적으로 할당된 객체가 삭제 키워드를 사용해 명시적으로 삭제되면, 객체가 메모리에서 제거되기 전에 필요한 정리를 수행하기 위해 클래스는 소멸자가 있는 경우 소멸자를 자동으로 호출한다.&lt;/p&gt;&lt;p&gt;클래스의 멤버 변수들이 단순하게 기본 자료형이 값 형식이라면 크게 필요 없지만 다른 리소스(예: 동적 메모리, 파일 또는 데이터베이스 핸들러)라면 객체가 소멸되기 전에 어떤 종류의 유지보수를 해야 한다. 이때 소멸자는 객체가 소멸되기 전에 마지막으로 호출되는 특별한 함수이므로 완벽한 장소가 된다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;&quot;&gt;소멸자 규칙&lt;/h2&gt;

&lt;p&gt;생성자처럼 소멸자도 특별한 규칙이 있다.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;소멸자 이름은 클래스 이름과 같아야 하고 앞에 &lt;code&gt;~&lt;/code&gt;를 달아야 한다.&lt;/li&gt;

&lt;li&gt;소멸자는 인수가 없다.&lt;/li&gt;

&lt;li&gt;소멸자는 반환 값이 없다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;이런 규칙 때문에 소멸자는 클래스당 하나밖에 존재할 수 없다.&lt;/p&gt;&lt;p&gt;또한, 소멸자를 명시적으로 호출하는 경우는 없다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;-1&quot;&gt;소멸자 사용 예제&lt;/h2&gt;

&lt;p&gt;소멸자를 사용하는 간단한 클래스를 살펴보자:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;cstddef&amp;gt;

using namespace std;

class IntArray
{
private:
    int* m_Array;
    int m_Length;

public:
    IntArray(int length) // 생성자
    {        
        m_Array = new int[static_cast&amp;lt;size_t&amp;gt;(length)]{};
        m_Length = length;
    }

    ~IntArray() // 소멸자
    {
        // 동적으로 할당한 배열을 삭제한다.
        delete[] m_Array;
    }

    void SetValue(int index, int value)
    {
        m_Array[index] = value;
    }

    int GetValue(int inudex)
    {
        return m_Array[index];
    }

    int GetLength()
    {
        return m_Length;
    }
};

int main()
{
    IntArray ar(10);

    for(int count = 0; count &amp;lt; ar.GetLength(); ++count)
    {
        ar.SetValue(count, count + 1);
    }

    cout &amp;lt;&amp;lt; &quot;The value of element 5 is: &quot; &amp;lt;&amp;lt; ar.getValue(5) &amp;lt;&amp;lt; '\n';
    // The value of element 5 is: 6

    return 0;
} // ar 객체는 여기서 삭제되므로 ~IntArray() 소멸자 함수는 여기서 호출된다.
&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;IntArray ar(10);&lt;/code&gt;에서 &lt;em&gt;ar&lt;/em&gt;라는 새로운 &lt;em&gt;IntArray&lt;/em&gt; 클래스의 객체를 인스턴스화 하고 10의 길이를 가진 배열을 동적 할당한다.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;em&gt;main()&lt;/em&gt; 함수의 끝에서 &lt;em&gt;ar&lt;/em&gt; 객체는 스코프 범위를 벗어난다. 이로 인해 &lt;code&gt;~IntArray()&lt;/code&gt; 소멸자가 호출되어 생성자에서 할당한 배열이 삭제된다.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;

&lt;h2 id=&quot;-2&quot;&gt;생성자 및 소멸자의 생명주기&lt;/h2&gt;

&lt;p&gt;지금까지 설명한 것처럼 객체가 생성되는 시점에는 생성자를, 소멸되는 시점에는 소멸자를 호출한다. 다음 예에서는 생성자와 소멸자 내부에서 &lt;em&gt;cout&lt;/em&gt;를 이용해 이를 더 자세하게 알 수 있다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Simple
{
private:
    int m_ID;

public:
    Simple(int id) : m_ID(id)
    {
        cout &amp;lt;&amp;lt; &quot;Constructing Simple &quot; &amp;lt;&amp;lt; m_ID &amp;lt;&amp;lt; endl;
    }

    ~Simple()
    {
        cout &amp;lt;&amp;lt; &quot;Destructing Simple &quot; &amp;lt;&amp;lt; m_ID &amp;lt;&amp;lt; endl;
    }

    int GetID()
    {
        return m_ID;
    }
};

int main()
{
    // 스택에 단순 할당
    Simple simple(1);
    cout &amp;lt;&amp;lt; simple.GetID() &amp;lt;&amp;lt; endl;

    // 동적으로 할당
    Simple* pSimple = new Simple(2);
    cout &amp;lt;&amp;lt; pSimple-&amp;gt;GetID() &amp;lt;&amp;lt; endl;

    // pSimple을 동적으로 할당했으므로 삭제해야 한다.
    delete pSimple;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이 프로그램을 실행하면 아래와 같은 결과를 볼 수 있다:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Constructing Simple 1        // 스택 영역에 단순 할당된 simple 객체(인스턴스)의 생성자
1
Constructing Simple 2        // 힙 엽역에 동적 할당된 pSimple 객체(인스턴스)의 생성자
2
Destructing Simple 2         // 명시적으로 pSimple 객체 삭제 -&amp;gt; pSimple 객체(인스턴스)의 소멸자
Destructing Simple 1         // main 함수의 스택을 벗어 났으므로 simple 객체 삭제 -&amp;gt; simple 객체의(인스턴스)의 소멸자
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;main&lt;/em&gt; 함수가 끝나기 전에 &lt;em&gt;pSimple&lt;/em&gt;을 &lt;strong&gt;명시적으로 삭제했으므로&lt;/strong&gt; 'Destructing Simple 2' 출력문을 먼저 볼 수 있다. &lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;raii&quot;&gt;RAII&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;RAII (Resource Acquisition Is Initialization)&lt;/strong&gt;는 객체의 수명과 리소스 관리와 연관있는 프로그래밍 디자인 기법이다. C++ 에서 RAII는 클래스의 생성자와 소멸자로 구현되었다. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;리소스(메모리, 파일 또는 데이터베이스 핸들 등)는 일반적으로 객체의 생성자에서 획득된다. &lt;/li&gt;

&lt;li&gt;그러면 그 리소스는 객체가 살아있는 동안 사용될 수 있다. &lt;/li&gt;

&lt;li&gt;객체가 소멸하면 자원이 소멸된다. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RAII의 장점은 리소스를 보유한 객체가 자동으로 정리됨에 따라 리소스 누수(예: Memory Leak)를 방지하는 데 도움이 된다는 것이다.&lt;/p&gt;&lt;p&gt;이 포스트의 맨 위에 있는 IntArray 클래스는 RAII를 구현하는 클래스의 예제로서 생성자에서의 할당, 소멸자에서의 할당 해제를 포함한다. 나중에 설명할 &lt;code&gt;std::string&lt;/code&gt; 및 &lt;code&gt;std::vector&lt;/code&gt;는 RAII를 따르는 표준 라이브러리의 클래스로서 동적 메모리는 초기화 시 획득하고, 파괴 시 자동으로 정리된다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;-3&quot;&gt;요약&lt;/h2&gt;

&lt;p&gt;생성자와 소멸자를 함께 사용하면 프로그래머가 특별한 작업을 하지 않아도 클래스는 스스로 초기화하고 정리할 수 있다. 이렇게 하면 오류가 발생할 확률을 낮추고 클래스를 더 쉽게 사용할 수 있다.&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99955B3A5F1701AD0C&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99955B3A5F1701AD0C&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;cpp&quot; filename=&quot;cpp3.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt; color: rgb(93, 93, 93);&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt; color: rgb(93, 93, 93);&quot;&gt;번역: 이 포스트의 원문은 https://www.learncpp.com/cpp-tutorial/8-7-destructors/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <category>C++ 이야기/09. 객체지향 프로그래밍</category>
      <category>C++</category>
      <category>C++ 기초</category>
      <category>객체지향 프로그래밍</category>
      <category>생성자</category>
      <category>소멸자</category>
      <category>클래스</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/249</guid>
      <comments>https://boycoding.tistory.com/249#entry249comment</comments>
      <pubDate>Tue, 21 Jul 2020 23:56:18 +0900</pubDate>
    </item>
    <item>
      <title>C++ 09.08 - 생성자 위임 (Delegating constructors)</title>
      <link>https://boycoding.tistory.com/248</link>
      <description>&lt;h1 id=&quot;delegatingconstructors&quot;&gt;생성자 위임 (Delegating constructors)&lt;/h1&gt;

&lt;p&gt;새 객체를 인스턴스화하면 C++ 컴파일러에 의해 생성자가 호출된다. 클래스에 여러 개의 생성자가 있는 경우 각 생성자의 기능이 중복되는 경우가 많다. 다음 예시를 보자:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Test
{
public:
    Test()
    {
        // Code A
    }

    Test(int value)
    {
        // Code A
        // Code B
    }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 코드에서 &lt;em&gt;Test&lt;/em&gt; 클래스는 생성자 두 개를 가지고 있다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;기본 생성자&lt;/li&gt;

&lt;li&gt;정수를 인수로 받는 생성자&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;두 생성자 모두 &lt;code&gt;Code A&lt;/code&gt; 를 수행하므로 중복된 코드를 가지게 된다.&lt;/p&gt;&lt;p&gt;중복 코드를 갖는 것은 가능한 한 피해야 좋으므로 이 문제를 해결하기 위한 몇 가지 방법을 살펴보자.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;&quot;&gt;확실하지만 잘못된 방법&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Test
{
public:
    Test()
    {
        // Code A
    }

    Test(int value)
    {
        Test(); // 위의 생성자(기본 생성자)를 호출한다.
        // Code B
    }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 코드처럼 생성자 안에서 다른 생성자를 호출하면 잘 작동한다. 그러나 나중에 순서가 꼬이는 등의 이유로 오류가 일어난다면 디버거 하는데 오랜 시간을 할애하게 될 것이다. &lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;delegatingconstructors-1&quot;&gt;생성자 위임 (Delegating constructors)&lt;/h2&gt;

&lt;p&gt;C++ 11에서는 생성자에서 다른 생성자를 호출하는 방법으로 &lt;strong&gt;생성자 위임(Delegating constructors)&lt;/strong&gt;이라는 기능을 제공한다.&lt;/p&gt;&lt;p&gt;생성자에서 다른 생성자를 호출하려면 &lt;strong&gt;멤버 초기화 리스트&lt;/strong&gt;에서 생성자를 호출하면 된다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Test
{
private:

public:
    Test()
    {
        // Code A
    }

    Test(int value) : Test() // Test() 기본 생성자를 호출한다.
    {
        // Code B
    }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이렇게 하면 Test 인스턴스가 생성될 때 기본 생성자(&lt;code&gt;Test()&lt;/code&gt;)가 호출된 후 정수를 인수로 받는 생성자(&lt;code&gt;Test(int)&lt;/code&gt;)가 호출되는 순으로 처리된다. &lt;/p&gt;&lt;p&gt;중복코드를 줄이기 위해 생성자 위임을 하는 또 다른 예시를 보자:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;iostream&amp;gt;

using namespace std;

class Employee
{
private:
    int m_ID;
    string m_Name;

public:
    Employee(int id, const string &amp;amp;name) : m_ID(id), m_Name(name)
    {
        cout &amp;lt;&amp;lt; &quot;Employee &quot; &amp;lt;&amp;lt; m_Name &amp;lt;&amp;lt; &quot; created.\n&quot; &amp;lt;&amp;lt; endl;
    }

    // 중복 코드를 최소화하기 위해 생성자 위임을 사용
    Employee(const string&amp;amp; name) : Employee(0, name)
    {

    }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 클래스는 2개의 생성자를 가지고 있으며, 그중 1개는 다른 생성자를 위임한다. 이렇게 하면 중복 코드의 양이 최소화된다.&lt;/p&gt;

&lt;h3 id=&quot;note&quot;&gt;Note&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;같은 기능을 가진 여러 생성자가 있는 경우, 중복 코드를 최소화하도록 생성자를 위임하자!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr&gt;

&lt;h2 id=&quot;-1&quot;&gt;함수 사용하기&lt;/h2&gt;

&lt;p&gt;관련해서, 생성자에서 멤버 함수를 호출할 수 있으므로 클래스의 멤버 변수를 기본값으로 초기화하기 위해 멤버 함수를 쓰고 싶은 상황이 있을 수 있다. &lt;/p&gt;&lt;p&gt;다음 예시를 보자:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Test
{
public:
    Test()
    {
        Init();
    }

    Test(int value)
    {
        Init();

        // value를 가지고 뭔가하는 코드
    }

    void Init()
    {
        Test를 초기화하는 코드
    }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;Init()&lt;/em&gt; 함수의 기능을 초기화(Initialize)라고 부르겠지만 &lt;strong&gt;이것은 진정한 초기화가 아니다.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;생성자가 &lt;em&gt;init()&lt;/em&gt; 함수를 호출할 시점에는 멤버 변수가 이미 존재하고 기본값(default value)으로 초기화되었을 것이다. &lt;em&gt;Init()&lt;/em&gt; 함수는 멤버 변수의 값을 할당할 뿐이다. 이 방식으로는 const 또는 reference 변수와 같이 초기값이 필요한 멤버를 초기화할 수 없다.&lt;/p&gt;

&lt;h3 id=&quot;-2&quot;&gt;주의사항&lt;/h3&gt;

&lt;p&gt;멤버 변수를 기본값으로 초기화하는 기능을 가진 &lt;em&gt;init()&lt;/em&gt;과 같은 함수는 일반적으로 많이 쓰인다. 그러나 &lt;em&gt;init()&lt;/em&gt; 함수는 언제든지 누구나 호출할 수 있으므로 &lt;em&gt;init()&lt;/em&gt; 함수를 호출할 때 동적으로 할당된 메모리가 이미 할당되어 있거나 하는 상황이 있을 수 있다. 그러므로 이 상황을 적절하게 처리하도록 주의해서 프로그래밍하자.&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99D734415F15711F32&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99D734415F15711F32&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;cpp&quot; filename=&quot;cpp3.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt; color: rgb(93, 93, 93);&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt; color: rgb(93, 93, 93);&quot;&gt;번역: 이 포스트의 원문은 https://www.learncpp.com/cpp-tutorial/8-6-overlapping-and-delegating-constructors/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <category>C++ 이야기/09. 객체지향 프로그래밍</category>
      <category>C++</category>
      <category>C++ 기초</category>
      <category>객체지향 프로그래밍</category>
      <category>생성자</category>
      <category>클래스</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/248</guid>
      <comments>https://boycoding.tistory.com/248#entry248comment</comments>
      <pubDate>Mon, 20 Jul 2020 20:37:27 +0900</pubDate>
    </item>
    <item>
      <title>C++ 09.07 - 멤버 초기화 (Member initialization)</title>
      <link>https://boycoding.tistory.com/247</link>
      <description>&lt;h1 id=&quot;memberinitialization&quot;&gt;멤버 초기화 (Member initialization)&lt;/h1&gt;

&lt;p&gt;c++ 11에서는 클래스의 일반 멤버 변수에 직접 기본 초기값을 할당할 수 있다. (즉, 초기화를 할 수 있다.)&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;iostream&amp;gt;

class Rectangle
{
private:
    double m_Length = 1.0;  // or m_Length { 1.0 };
    double m_Width = 1.0;

public:
    void Print()
    {
        std::cout &amp;lt;&amp;lt; &quot;Length: &quot; &amp;lt;&amp;lt; m_Length &amp;lt;&amp;lt; &quot;, Width: &quot; &amp;lt;&amp;lt; m_Widt &amp;lt;&amp;lt; std::endl;
    }    
}

int main()
{
    Rectangle x;
    x.Print(); // Length: 1.0, Width: 1.0

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;생성자에서 멤버 변수의 값을 지정하지 않는 경우 이렇게 &lt;strong&gt;멤버 초기화&lt;/strong&gt;를 통해서 기본값을 할당하는 것이다.&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;주의해야 할 점은 기본값이 지정되어 있더라도 &lt;a href=&quot;https://boycoding.tistory.com/246?category=1067100&quot;&gt;&lt;strong&gt;생성자 멤버 초기화 리스트&lt;/strong&gt;&lt;/a&gt;가 가장 우선시 된다는 것이다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;iostream&amp;gt;

class Rectangle
{
private:
    double m_Length = 1.0;  // or m_Length { 1.0 };
    double m_Width = 1.0;

public:
    Rectangle(double length, double width) : m_Length(length), m_Width(width)
    {
        // m_Length와, m_Width 변수는 생성자 멤버 초기화 목록에 의해 값이 초기화된다. (기본값이 사용되지 않는다.)
    }

    Rectangle(double length) : m_Length(length)
    {
        // m_Length 변수는 값이 초기화된다.
        // m_Width 변수는 기본값 1.0
    }

    void Print()
    {
        std::cout &amp;lt;&amp;lt; &quot;Length: &quot; &amp;lt;&amp;lt; m_Length &amp;lt;&amp;lt; &quot;, Width: &quot; &amp;lt;&amp;lt; m_Widt &amp;lt;&amp;lt; std::endl;
    }    
}

int main()
{
    // Rectangle x; // Error: 기본 생성자는 이제 암시적으로 생기지 않는다.

    Rectangle y { 2.0, 3.0 };
    y.Print(); // Length: 2.0, Width: 3.0

    Rectangle z { 2.0 };
    z.Print(); // Length: 2.0, Width: 1.0

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;또한 멤버 초기화는&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;=&lt;/code&gt;  연산자를 이용한 &lt;strong&gt;복사 초기화&lt;/strong&gt; 방식 &lt;code&gt;int x = 5;&lt;/code&gt;&lt;/li&gt;

&lt;li&gt;&lt;code&gt;{}&lt;/code&gt;를 이용한 &lt;strong&gt;유니폼 초기화&lt;/strong&gt; 방식 &lt;code&gt;int x { 5 };&lt;/code&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;을 지원하지만&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;()&lt;/code&gt;를 이용한 &lt;strong&gt;직접 초기화&lt;/strong&gt;방식은 지원하지 않는다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;참고: &lt;a href=&quot;https://boycoding.tistory.com/181?category=1006674&quot;&gt;여러 가지 변수 초기화 방법&lt;/a&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/999B443A5F141BFC11&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F999B443A5F141BFC11&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;cpp&quot; filename=&quot;cpp3.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;font-size: 10pt; color: rgb(93, 93, 93);&quot;&gt;번역: 이 포스트의 원문은 https://www.learncpp.com/cpp-programming/8-5b-non-static-member-initialization/ 입니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>C++ 이야기/09. 객체지향 프로그래밍</category>
      <category>C++</category>
      <category>C++ 기초</category>
      <category>객체지향 프로그래밍</category>
      <category>생성자</category>
      <category>클래스</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/247</guid>
      <comments>https://boycoding.tistory.com/247#entry247comment</comments>
      <pubDate>Sun, 19 Jul 2020 19:11:09 +0900</pubDate>
    </item>
    <item>
      <title>C++ 09.06 - 생성자 멤버 초기화 리스트 (Constructor member initializer list)</title>
      <link>https://boycoding.tistory.com/246</link>
      <description>&lt;h1 id=&quot;constructormemberinitializerlist&quot;&gt;생성자 멤버 초기화 리스트 (Constructor member initializer list)&lt;/h1&gt;

&lt;p&gt;이전 포스트 &lt;a href=&quot;https://boycoding.tistory.com/244&quot;&gt;생성자&lt;/a&gt;에서 대입 연산자(=)를 사용하여 클래스 멤버 변수를 초기화했다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Something
{
private:
    int m_value1;
    double m_value2;
    char m_value3;

public:
    Something()
    {
        // 이것은 모두 할당이지 초기화가 아니다.
        m_value1 = 1;
        m_value2 = 2.2;
        m_value3 = 'c';
    }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;클래스 생성자가 실행될 때 &lt;em&gt;m_value1&lt;/em&gt;, &lt;em&gt;m_value2&lt;/em&gt; 및 &lt;em&gt;m_value3&lt;/em&gt; 변수가 생성된다. 그런 다음 생성자 본문이 실행되며, 멤버 변수값이 할당된다. 이 순서는 C++에서 다음 코드와 흐름이 비슷하다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int m_value1;
double m_value2;
char m_value3;

m_value1 = 1;
m_value2 = 2.2;
m_value3 = 'c';
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이 순서는 C++ 언어의 문법에서는 유효하지만, 초기화보다 효율적이지 않다.&lt;/p&gt;&lt;p&gt;그리고 일부 유형의 데이터(const 및 참조 변수)는 선언과 동시에 초기화해야 한다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Something
{
private:
    const int m_value;

public:
    Something()
    {
        m_value = 1; // error: const 상수 변수는 할당할 수 없다.
    } 
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;다음과 유사한 코드가 생성된다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;const int m_value; // error: const 상수 변수는 값으로 초기화되어야 한다.
m_value = 5; //  error: const 상수 변수는 할당할 수 없다.
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;

&lt;p&gt;이 문제를 해결하기 위해 C++은 &lt;strong&gt;생성자 멤버 초기화 리스트 (Constructor member initializer list)&lt;/strong&gt;을 통해 멤버 변수를 초기화하는 방법을 제공한다.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/181&quot;&gt;여러 가지 변수 초기화 방법&lt;/a&gt; 포스트에서 변수를 초기화하는 세 가지 방법을 배웠다:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;복사 초기화 (copy initialization)&lt;/li&gt;

&lt;li&gt;직접 초기화 (direct initialization)&lt;/li&gt;

&lt;li&gt;유니폼 초기화 (uniform initialization)&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int value1 = 1; // 복사 초기화
double value2(2.2); // 직접 초기화
char value3 {'c'}; // 유니폼 초기화
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;멤버 초기화 리스트&lt;/strong&gt;를 사용하는 것은 직접 초기화(또는 C++ 11에서는 유니폼 초기화)를 수행하는 것과 유사하다. &lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Something
{
private:
    int m_value1;
    double m_value2;
    char m_value3;

public:
    Something()
    {
        // These are all assignments, not initializations
        m_value1 = 1;
        m_value2 = 2.2;
        m_value3 = 'c';
    }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위와 같은 예제를 이제 초기화 리스트를 사용해서 같은 코드를 작성해보겠다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Something
{
private:
    int m_value1;
    double m_value2;
    char m_value3;

public:
    Something() : m_value1(1), m_value2(2.2), m_value3('c') // 멤버 초기화 리스트를 통해 멤버 변수를 직접 초기화할 수 있다.
    {
        // 멤버 변수를 여기서 할당할 필요가 없다.
    }

    void print()
    {
         std::cout &amp;lt;&amp;lt; &quot;Something(&quot; &amp;lt;&amp;lt; m_value1 &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; m_value2 &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; m_value3 &amp;lt;&amp;lt; &quot;)\n&quot;;
    }
};

int main()
{
    Something something;
    something.print();
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;This prints:
Something(1, 2.2, c)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;멤버 초기화 리스트&lt;/strong&gt;는 생성자 매개 변수 뒤에 삽입된다. 콜론 &lt;code&gt;:&lt;/code&gt;으로 시작한 다음 초기화 할 각 변수를 쉼표로 구분하여 해당 변수의 값과 함께 나열한다. 또한, 초기화 리스트는 세미콜론 &lt;code&gt;;&lt;/code&gt;으로 끝나지 않는다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;Something() : m_value1(1), m_value2(2.2), m_value3('c')
{
    // ...
    // No need for assignment here
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;초기화 리스트가 멤버 변수 초기화 기능을 대체하므로 더는 생성자 본문에서 할당을 수행할 필요가 없다. &lt;/p&gt;

&lt;hr&gt;

&lt;h3 id=&quot;&quot;&gt;초기화 리스트는 호출자가 초기화 값을 전달할 때 더 유용하다:&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;iostream&amp;gt;

class Something
{
private:
    int m_value1;
    double m_value2;
    char m_value3;

public:
    Something(int value1, double value2, char value3='c')
        : m_value1(value1), m_value2(value2), m_value3(value3) // 멤버 초기화 리스트를 통해 멤버 변수를 직접 초기화할 수 있다.
    {
        // 멤버 변수를 여기서 할당할 필요가 없다.
    }

    void print()
    {
         std::cout &amp;lt;&amp;lt; &quot;Something(&quot; &amp;lt;&amp;lt; m_value1 &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; m_value2 &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; m_value3 &amp;lt;&amp;lt; &quot;)\n&quot;;
    }

};

int main()
{
    Something something(1, 2.2); // value1 = 1, value2=2.2, value3 gets default value 'c'
    something.print();
    return 0;
}#include &amp;lt;iostream&amp;gt;

class Something
{
private:
    int m_value1;
    double m_value2;
    char m_value3;

public:
    Something(int value1, double value2, char value3='c')
        : m_value1(value1), m_value2(value2), m_value3(value3) // 멤버 초기화 리스트를 통해 멤버 변수를 직접 초기화할 수 있다.
    {
        // 멤버 변수를 여기서 할당할 필요가 없다.
    }

    void print()
    {
         std::cout &amp;lt;&amp;lt; &quot;Something(&quot; &amp;lt;&amp;lt; m_value1 &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; m_value2 &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; m_value3 &amp;lt;&amp;lt; &quot;)\n&quot;;
    }

};

int main()
{
    Something something(1, 2.2); // value1 = 1, value2=2.2, value3 gets default value 'c'
    something.print();
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;This prints:
Something(1, 2.2, c)
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;

&lt;h3 id=&quot;c11&quot;&gt;C++ 11의 유니폼 초기화&lt;/h3&gt;

&lt;p&gt;C++ 11에서는 직접 초기화 대신 유니폼 초기화를 사용할 수 있다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Something
{
private:
    const int m_value;

public:
    Something(): m_value { 5 } // 멤버 초기화 리스트를 통해 멤버 변수를 유니폼 초기화 할 수도 있다.
    {
    } 
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;나중에 상속을 수행할 때 초기화 리스트가 필요하므로 const 및 reference 변수를 사용하지 않더라도 이 초기화 리스트를 사용하는 게 좋다.&lt;/p&gt;

&lt;hr&gt;

&lt;h3 id=&quot;-1&quot;&gt;초기화 리스트를 사용한 배열 멤버 초기화&lt;/h3&gt;

&lt;p&gt;배열 멤버가 있는 클래스를 보자:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Something
{
private:
    const int m_array[5];
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;C++ 11 이전에는 멤버 초기화 리스트를 통해서 배열 멤버를 0으로만 만들 수 있었다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Something
{
private:
    const int m_array[5];

public:
    Something(): m_array {} // zero the member array
    {
    }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;그러나 C++ 11에서는 유니폼 초기화를 사용하여 멤버 배열을 완전히 초기화할 수 있다:&lt;/p&gt;

&lt;hr&gt;

&lt;h3 id=&quot;-2&quot;&gt;클래스인 멤버 변수 초기화&lt;/h3&gt;

&lt;p&gt;멤버 초기화 리스트를 사용해서 클래스인 멤버를 초기화할 수 있다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;iostream&amp;gt;

class A
{
public:
    A(int x) { std::cout &amp;lt;&amp;lt; &quot;A &quot; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; &quot;\n&quot;; }
};

class B
{
private:
    A m_a;
public:
    B(int y) : m_a(y-1) // call A(int) constructor to initialize member m_a
    {
        std::cout &amp;lt;&amp;lt; &quot;B &quot; &amp;lt;&amp;lt; y &amp;lt;&amp;lt; &quot;\n&quot;;
    }
};

int main()
{
    B b(5);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;This prints:

A 4
B 5
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;변수 b가 생성되면 B(int) 생성자가 값 5로 호출된다. 생성자 본문이 실행되기 전에 m_a가 초기화되므로 값 4로 A(int) 생성자가 호출된다. 그러면 &quot;A 4&quot;가 인쇄되고, 그런 다음 제어는 B 생성자로 돌아가므로 B 생성자의 본문이 실행되어 &quot;B 5&quot;를 인쇄한다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;-3&quot;&gt;요약&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;멤버 초기화 리스트를 사용하면 값을 할당하지 않고 멤버 변수를 초기화할 수 있다.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;생성자 본문에서 값을 할당하는 것보다 성능이 더 우수하다.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;const 또는 reference 변수와 같이 초기값이 필요한 멤버를 초기화할 수 있는 유일한 방법이다.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;멤버 초기화 리스트는 기본 자료형 변수와 클래스 자체인 멤버 모두에서 잘 작동한다.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99DCEE465F1303811B&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99DCEE465F1303811B&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;cpp&quot; filename=&quot;cpp3.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt; color: rgb(93, 93, 93);&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt; color: rgb(93, 93, 93);&quot;&gt;번역: 이 포스트의 원문은 https://www.learncpp.com/cpp-tutorial/8-5a-constructor-member-initializer-lists/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>C++ 이야기/09. 객체지향 프로그래밍</category>
      <category>C++</category>
      <category>C++ 기초</category>
      <category>객체지향 프로그래밍</category>
      <category>생성자</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/246</guid>
      <comments>https://boycoding.tistory.com/246#entry246comment</comments>
      <pubDate>Sat, 18 Jul 2020 23:18:01 +0900</pubDate>
    </item>
    <item>
      <title>C++ 09.05 - 생성자 (Constructor)</title>
      <link>https://boycoding.tistory.com/244</link>
      <description>&lt;h1 id=&quot;constructor-NaN&quot;&gt;생성자 (Constructor)&lt;/h1&gt;

&lt;p&gt;클래스의 모든 멤버 변수가 모두 &lt;code&gt;public&lt;/code&gt;인 경우 &lt;strong&gt;초기화 목록(initialization list)&lt;/strong&gt; 또는 &lt;strong&gt;유니폼 초기화(uniform initialization)&lt;/strong&gt;를 사용해서 초기화를 직접 초기화할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Foo
{
public:
    int m_x;
    int m_y;
};

int main()
{
    Foo foo1 = { 4, 5 }; // initialization list
    Foo foo2 { 6, 7 }; // uniform initialization (C++11)

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;그러나 멤버 변수가 &lt;code&gt;private&lt;/code&gt;인 경우에는 변수에 직접 접근할 수 없는 비공개 상태이므로 더는 위와 같은 방법으로는 클래스를 초기화할 수 없다.&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;&lt;strong&gt;생성자 (Constructor)&lt;/strong&gt;는 해당 클래스의 객체가 인스턴스화될 때 자동으로 호출되는 특수한 종류의 멤버 함수다. 생성자는 일반적으로 클래스의 멤버 변수를 적절한 기본값 또는 사용자 제공 값으로 초기화하거나 클래스를 사용하는 데 필요한 설정(ex. 파일 열기 등)이 필요한 경우 사용된다.&lt;/p&gt;&lt;p&gt;일반적인 멤버 함수와 다르게 생성자 정의 방법에는 특정 규칙이 있다:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;생성자 이름은 클래스와 이름이 같아야 한다.&lt;/li&gt;

&lt;li&gt;생성자는 리턴 타입이 없다. (void도 아니다.)&lt;/li&gt;
&lt;/ol&gt;

&lt;hr&gt;

&lt;h2 id=&quot;1defaultconstructor&quot;&gt;1. 기본 생성자 (Default constructor)&lt;/h2&gt;

&lt;p&gt;매개 변수를 갖지 않거나 모두 기본값이 설정된 매개 변수를 가지고 있는 생성자를 &lt;strong&gt;기본 생성자(Default constructor)&lt;/strong&gt;라고 한다. 클래스를 인스턴스화할 때 사용자가 초기값을 제공하지 않으면 기본 생성자가 호출된다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;iostream&amp;gt;

class Fraction
{
private:
    int m_numerator;   // 분자
    int m_denominator; // 분모

public:
    Fraction() // default constructor
    {
         m_numerator = 0;
         m_denominator = 1;
    }

    int getNumerator() { return m_numerator; }
    int getDenominator() { return m_denominator; }
    double getValue() { return static_cast&amp;lt;double&amp;gt;(m_numerator) / m_denominator; }
};

int main()
{
    Fraction frac; // Since no arguments, calls Fraction() default constructor
    std::cout &amp;lt;&amp;lt; frac.getNumerator() &amp;lt;&amp;lt; &quot;/&quot; &amp;lt;&amp;lt; frac.getDenominator() &amp;lt;&amp;lt; '\n';
         // Output: 0/1
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 예제는 분자(&lt;em&gt;m_numerator&lt;/em&gt;)와 분모(&lt;em&gt;m_denominantor&lt;/em&gt;)값을 가진 &lt;em&gt;Fraction&lt;/em&gt; 클래스가 있고, 클래스 이름과 같은 &lt;em&gt;Fraction()&lt;/em&gt; 기본 생성자를 정의했다.&lt;/p&gt;&lt;p&gt;인수 없이 &lt;em&gt;Fraction&lt;/em&gt; 타입의 객체를 인스턴스화 했으므로 객체에 메모리가 할당된 직후 기본 생성자가 호출되고 객체가 초기화된다.&lt;/p&gt;&lt;p&gt;분자(&lt;em&gt;m_numerator&lt;/em&gt;)와 분모(&lt;em&gt;m_denominantor&lt;/em&gt;) 변수는 기본 생성장에서 지정된 값으로 초기화되었다! 이처럼 기본 생성자는 대부분 클래스에서 매우 유용한 기능이다. 만약 기본 생성자가 없었다면 위 변수들은 값을 명시적으로 할당할 때까지 쓰레기값을 가지고 있었을 것이다. (기본 자료형인 멤버 변수는 자동으로 초기화되지 않는다.)&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;2&quot;&gt;2. 매개 변수가 있는 생성자를 사용한 초기화&lt;/h2&gt;

&lt;p&gt;기본 생성자는 클래스 멤버 변수의 기본값을 설정하는 데 유용하지만, 클래스 인스턴스 별 멤버 변수의 값을 특정한 값으로 초기화하고 싶은 경우가 있다. 다행히 생성자에 매개 변수를 선언할 수 있다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp  language-cpp &quot;&gt;#include &amp;lt;cassert&amp;gt;

class Fraction
{
private:
    int m_numerator;
    int m_denominator;

public:
    Fraction() // default constructor
    {
         m_numerator = 0;
         m_denominator = 1;
    }

    // Constructor with two parameters, one parameter having a default value
    Fraction(int numerator, int denominator=1)
    {
        assert(denominator != 0);
        m_numerator = numerator;
        m_denominator = denominator;
    }

    int getNumerator() { return m_numerator; }
    int getDenominator() { return m_denominator; }
    double getValue() { return static_cast&amp;lt;double&amp;gt;(m_numerator) / m_denominator; }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 예제에는 한 클래스 안에 두 개의 생성자가 있다: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;기본 경우에 호출될 기본 생성자&lt;/li&gt;

&lt;li&gt;두 개의 매개 변수를 사용하는 생성자&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이 두 생성자는 함수 오버로드로 인해 같은 클래스 안에서 공존할 수 있다. 실제로 각각 고유한 서명(매개 변수 개수 및 타입)으로 원하는 수 만큼 생성자를 정의할 수 있다.&lt;/p&gt;&lt;p&gt;그렇다면 이 생성자를 매개 변수와 함께 어떻게 사용할까? 간단하게 직접 초기화 형식의 초기화를 사용하면 된다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int x(5); // Direct initialize an integer
Fraction fiveThirds(5, 3); // Direct initialize a Fraction, calls Fraction(int, int) constructor
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;C++ 11에서는 유니폼 초기화 방식을 선호하기도 한다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int x { 5 }; // Uniform initialization of an integer
Fraction fiveThirds {5, 3}; // Uniform initialization of a Fraction, calls Fraction(int, int) constructor
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 예제의 매개 변수가 있는 생성자에서 두 번째 매개 변수는 기본값이 지정되어 있으므로 다음과 같은 방식도 유효하다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;Fraction six(6); // calls Fraction(int, int) constructor, second parameter uses default value
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;

&lt;h2 id=&quot;3&quot;&gt;3. 클래스와 대입 연산자(=)를 이용한 복사 초기화&lt;/h2&gt;

&lt;p&gt;기본 자료형인 변수와 마찬가지로 대입 연산자(=)를 이용해서 클래스를 초기화할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int x = 6; // Copy initialize an integer
Fraction six = Fraction(6); // Copy initialize a Fraction, will call Fraction(6, 1)
Fraction seven = 7; // Copy initialize a Fraction.  The compiler will try to find a way to convert 7 to a Fraction, which will invoke the Fraction(7, 1) constructor.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;그러나 이런 방식의 초기화는 효율이 떨어지므로 사용하지 않는 것이 좋다. 직접 초기화, 유니폼 초기화, 복사 초기화 모두 기본 자료형과는 같은 방식으로 작동하지만, 복사 초기화는 클래스와는 다르게 동작한다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;4&quot;&gt;4. 생성자 줄이기&lt;/h2&gt;

&lt;p&gt;위 예제에 있는 두 생성자를 아래와 같이 하나로 단순화할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;cassert&amp;gt;

class Fraction
{
private:
    int m_numerator;
    int m_denominator;

public:
    // Default constructor
    Fraction(int numerator=0, int denominator=1)
    {
        assert(denominator != 0);
        m_numerator = numerator;
        m_denominator = denominator;
    }

    int getNumerator() { return m_numerator; }
    int getDenominator() { return m_denominator; }
    double getValue() { return static_cast&amp;lt;double&amp;gt;(m_numerator) / m_denominator; }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이 생성자는 여전히 기본 생성자이지만 이제는 하나 또는 두 개의 사용자 제공 값을 허용할 수 있는 방식으로 정의되었다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;Fraction zero; // will call Fraction(0, 1)
Fraction six(6); // will call Fraction(6, 1)
Fraction fiveThirds(5,3); // will call Fraction(5, 3)
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;

&lt;h2 id=&quot;5&quot;&gt;5. 암시적으로 생성되는 기본 생성자&lt;/h2&gt;

&lt;p&gt;클래스에 다른 생성자가 없으면 C++ 컴파일러는 자동으로 기본 생성자를 생성한다. 이를 &lt;strong&gt;암시적 생성자&lt;/strong&gt;라고 한다.&lt;/p&gt;&lt;p&gt;아래 클래스를 보자:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Date
{
private:
    int m_year = 1900;
    int m_month = 1;
    int m_day = 1;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 클래스에는 생성자가 없다. 그러므로 컴파일러는 아래와 같게 동작하는 생성자를 생성한다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class Date
{
private:
    int m_year = 1900;
    int m_month = 1;
    int m_day = 1;

public:
    Date() // implicitly generated constructor
    {
    }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이 암시적으로 생성되는 기본 생성자를 사용하면 매개 변수 없이 &lt;code&gt;Date&lt;/code&gt; 객체를 만들 수 있지만 멤버를 초기화하지는 않는다. (모든 멤버 변수가 기본 자료형이고, 생성 시 초기화하지 않으므로)&lt;/p&gt;&lt;p&gt;일반적으로 클래스의 객체를 만드는 법을 명시적으로 나타내기 위해 항상 하나 이상의 생성자를 정의하는 게 좋다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;6&quot;&gt;6. 클래스를 포함하는 클래스&lt;/h2&gt;

&lt;p&gt;클래스는 다른 클래스를 멤버 변수로 포함할 수 있다. 기본적으로 외부 클래스가 생성될 때 멤버 변수는 기본 생성자가 호출되는데, 이것은 생성자의 본문이 실행되기 전에 발생한다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;iostream&amp;gt;

class A
{
public:
    A() { std::cout &amp;lt;&amp;lt; &quot;A\n&quot;; }
};

class B
{
private:
    A m_a; // B contains A as a member variable

public:
    B() { std::cout &amp;lt;&amp;lt; &quot;B\n&quot;; }
};

int main()
{
    B b;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;This prints:
A
B
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;변수 &lt;em&gt;b&lt;/em&gt;가 생성되면 B() 생성자가 호출된다. 생성자의 본문이 실행되기 전에 &lt;code&gt;m_a&lt;/code&gt;가 초기화되어 클래스 A의 기본 생성자 A()가 호출된다. 그래서 &quot;A&quot;가 출력된 다음 제어가 B 생성자로 돌아가서 본문이 실행된다.&lt;/p&gt;&lt;p&gt;B() 생성자가 변수 &lt;code&gt;m_a&lt;/code&gt;를 사용하고 싶을 수도 있으므로 &lt;code&gt;m_a&lt;/code&gt;를 먼저 초기화하는 것이 좋다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;7&quot;&gt;7. 생성자 노트&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;컴파일러는 생성자 호출 전에 객체에 대한 메모리를 할당하지 않는다.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;생성자는 두 가지 목적으로 사용된다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;누가 객체를 만들 수 있는지 결정한다.&lt;/li&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;li&gt;객체를 초기화할 수 있다.&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99C1F7495E2AE75B3A&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99C1F7495E2AE75B3A&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;cpp&quot; filename=&quot;cpp3.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;번역: 이 포스트의 원문은 https://www.learncpp.com/cpp-tutorial/85-constructors/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>C++ 이야기/09. 객체지향 프로그래밍</category>
      <category>C++</category>
      <category>C++ 기초</category>
      <category>객체지향 프로그래밍</category>
      <category>클래스</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/244</guid>
      <comments>https://boycoding.tistory.com/244#entry244comment</comments>
      <pubDate>Fri, 24 Jan 2020 21:49:01 +0900</pubDate>
    </item>
    <item>
      <title>C++ 09.04 - 캡슐화 (Encapusulation)</title>
      <link>https://boycoding.tistory.com/243</link>
      <description>&lt;h1 id=&quot;encapusulation&quot;&gt;캡슐화 (Encapusulation)&lt;/h1&gt;

&lt;p&gt;왜 멤버 변수를 비공개(&lt;code&gt;private&lt;/code&gt;)로 할까?&lt;/p&gt;&lt;p&gt;이전 포스트 &lt;a href=&quot;https://boycoding.tistory.com/242?category=1067100&quot;&gt;public과 private 접근 지정자&lt;/a&gt;에서 멤버 변수는 일반적으로 private로 설정한다고 했다. 왜 이렇게 설정하는지 현실 세계에서 비유를 들어보자. TV에는 TV를 끄고 켜기 위한 리모컨이 있다. 이 리모컨에는 버튼과 휠 등의 TV를 조작할 수 있는 인터페이스(interface)를 제공하지만, 리모컨의 내부가 어떻게 구현되어 있는지는 숨겨져 있다. 리모컨의 버튼을 누르면 리모컨 내부에서 어떻게 동작해서 TV를 조작하는지 알 필요가 없다. 인터페이스와 구현의 분리는 이렇게 리모컨의 작동 방식을 이해하지 않아도 리모컨을 사용할 수 있으므로 효과적이다.&lt;/p&gt;&lt;p&gt;비슷한 이유로, 프로그래밍에서도 구현과 인터페이스의 분리는 매우 유용하다.&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;객체 지향 프로그래밍에서 &lt;strong&gt;캡슐화&lt;/strong&gt;는 객체가 구현된 방식의 세부 정보를 사용자로부터 숨기면서 유지하는 방식이다. 대신 사용자는 객체의 공개 인터페이스를 통해 접근할 수 있다. &lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;캡슐화&lt;/strong&gt;(영어: encapsulation)는 객체 지향 프로그래밍에서 다음 2가지 측면이 있다: 객체의 속성(data fields)과 행위(메서드, methods)를 하나로 묶고, 실제 구현 내용 일부를 외부에 감추어 은닉한다. -위키백과-&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;C++에서는 접근 지정자를 통해 캡슐화를 구현한다. 일반적으로 클래스의 멤버 변수(&lt;em&gt;구현 세부 정보&lt;/em&gt;)는 &lt;code&gt;private&lt;/code&gt;로 설정하고, 멤버 함수(&lt;em&gt;공개 인터페이스&lt;/em&gt;)는 &lt;code&gt;public&lt;/code&gt;으로 설정한다. 클래스의 사용자가 인터페이스를 사용하도록 요구하는 것은 멤버 변수에 대한 직접 접근을 제공하는 것보다 부담스러울 수 있지만, 클래스 재사용성과 유지보수 측면에서 더 많은 이점이 있다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;&quot;&gt;캡슐화된 클래스는 사용하기 쉽고 프로그램의 복잡성을 줄여준다.&lt;/h2&gt;

&lt;p&gt;완전히 캡슐화된 클래스를 사용하면, 클래스를 사용하기 위해 공개적으로 사용할 수 있는 멤버 함수와 이 함수의 인수 및 반환 값만 알면 된다. 예를 들어 &lt;code&gt;std::string&lt;/code&gt;, &lt;code&gt;std::array&lt;/code&gt;, &lt;code&gt;std::vector&lt;/code&gt;, &lt;code&gt;std::map&lt;/code&gt; 등을 사용하는 데 있어서 클래스가 내부적으로 어떻게 구현되었는지 몰라도 사용할 수 있다. 이렇게 캡슐화는 프로그램의 복잡성을 줄여주고, 실수도 줄여준다. 다른 어떤 이유보다 캡슐화의 핵심 장점이다.&lt;/p&gt;&lt;p&gt;C++ 표준 라이브러리의 클래스는 모두 캡슐화되어있다. 만약 &lt;code&gt;std::string&lt;/code&gt;, &lt;code&gt;std::cout&lt;/code&gt; 등이 어떻게 구현되었는지 이해해야지만 사용할 수 있다면 훨씬 더 복잡한 C++가 됬을것이다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;-1&quot;&gt;캡슐화된 클래스는 데이터를 보호하고 오용을 방지한다.&lt;/h2&gt;

&lt;p&gt;전역 변수는 아무나 전역 변수에 접근할 수 있으므로 엄격하게 제어할 수 없어서 위험하다. &lt;code&gt;public&lt;/code&gt; 멤버 변수도 같은 이유로 위험하다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class MyString
{
private:
    char* m_string; // we'll dynamically allocate our string here
    int m_length;   // we need to keep track of the string length
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 프로그램에서 두 멤버 변수는 서로 관련이 있다. &lt;em&gt;m_length&lt;/em&gt;는 항상 &lt;em&gt;m_string&lt;/em&gt;이 보유하는 문자열의 길이와 같아야 한다. 만약 &lt;em&gt;m_length&lt;/em&gt;가 &lt;code&gt;public&lt;/code&gt;이라면 누구나 &lt;em&gt;m_string&lt;/em&gt;을 변경하지 않고도 문자열의 길이를 변경할 수 있는 문제가 있을 것이다.&lt;/p&gt;&lt;p&gt;또한, 멤버를 실수로 사용하지 못하도록 보호할 수 있다. &lt;code&gt;public&lt;/code&gt; 멤버 배열 변수가 있는 클래스를 봐보자.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class IntArray
{
public:
    int m_array[10];
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;사용자가 배열에 직접 접근할 수 있는 경우, 만약 배열을 잘못된 인덱스로 접근하면 에러가 발생한다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int main()
{
    IntArray array;
    array.m_array[16] = 2; // invalid array index, now we overwrote memory that we don't own
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;그러나 배열을 private로 설정하면 인덱스가 유효한지에 대한 확인을 하는 함수를 만들어 사용자가 강제로 사용하게 만들 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class IntArray
{
private:
    int m_array[10]; // user can not access this directly any more

public:
    void setValue(int index, int value)
    {
        // If the index is invalid, do nothing
        if (index &amp;lt; 0 || index &amp;gt;= 10)
            return;

        m_array[index] = value;
    }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99571D405E2959F72E&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99571D405E2959F72E&quot; width=&quot;50&quot; height=&quot;50&quot; filename=&quot;cpp3.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;번역: 이 포스트의 원문은 https://www.learncpp.com/cpp-tutorial/84-access-functions-and-encapsulation/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>C++ 이야기/09. 객체지향 프로그래밍</category>
      <category>C++</category>
      <category>C++ 기초</category>
      <category>객체지향 프로그래밍</category>
      <category>클래스</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/243</guid>
      <comments>https://boycoding.tistory.com/243#entry243comment</comments>
      <pubDate>Thu, 23 Jan 2020 17:33:37 +0900</pubDate>
    </item>
    <item>
      <title>C++ 09.03 - 클래스의 public과 private 접근 지정자</title>
      <link>https://boycoding.tistory.com/242</link>
      <description>&lt;h1 id=&quot;public-private-&quot;&gt;public과 private 접근 지정자&lt;/h1&gt;
&lt;p&gt;다음 구조체를 보자:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;lang-cpp&quot;&gt;struct DateStruct &lt;span class=&quot;hljs-comment&quot;&gt;// members are public by default&lt;/span&gt;
{
    &lt;span class=&quot;hljs-built_in&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;month&lt;/span&gt;; &lt;span class=&quot;hljs-comment&quot;&gt;// public by default, can be accessed by anyone&lt;/span&gt;
    &lt;span class=&quot;hljs-built_in&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;day&lt;/span&gt;;   &lt;span class=&quot;hljs-comment&quot;&gt;// public by default, can be accessed by anyone&lt;/span&gt;
    &lt;span class=&quot;hljs-built_in&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;year&lt;/span&gt;;  &lt;span class=&quot;hljs-comment&quot;&gt;// public by default, can be accessed by anyone&lt;/span&gt;
};

&lt;span class=&quot;hljs-built_in&quot;&gt;int&lt;/span&gt; main()
{
    DateStruct date;
    date.&lt;span class=&quot;hljs-built_in&quot;&gt;month&lt;/span&gt; = &lt;span class=&quot;hljs-number&quot;&gt;10&lt;/span&gt;;
    date.&lt;span class=&quot;hljs-built_in&quot;&gt;day&lt;/span&gt; = &lt;span class=&quot;hljs-number&quot;&gt;14&lt;/span&gt;;
    date.&lt;span class=&quot;hljs-built_in&quot;&gt;year&lt;/span&gt;= &lt;span class=&quot;hljs-number&quot;&gt;2020&lt;/span&gt;;

    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 프로그램에서는 &lt;em&gt;DataStruct&lt;/em&gt;를 선언한 다음 멤버를 직접 초기화하기 위해서 직접 접근한다. 이것은 구조체의 모든 멤버가 기본적으로 &lt;code&gt;public&lt;/code&gt; 멤버이기 때문에 작동한다. &lt;code&gt;public&lt;/code&gt; 멤버는 구조체 또는 클래스 외부에서 접근할 수 있는 구조체 또는 클래스의 공개 멤버다. 이 경우 &lt;em&gt;main()&lt;/em&gt; 함수는 구조체 외부에 있지만 공개되어 있으므로 &lt;em&gt;month&lt;/em&gt;, &lt;em&gt;day&lt;/em&gt;, &lt;em&gt;year&lt;/em&gt; 멤버에 직접 접근할 수 있다.&lt;/p&gt;&lt;p&gt;반면에 다음과 같이 거의 같은 클래스를 보자:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;lang-cpp&quot;&gt;&lt;span class=&quot;hljs-class&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;DateClass&lt;/span&gt; // &lt;span class=&quot;hljs-title&quot;&gt;members&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;are&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;default&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; m_month; &lt;span class=&quot;hljs-comment&quot;&gt;// private by default, can only be accessed by other members&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; m_day;   &lt;span class=&quot;hljs-comment&quot;&gt;// private by default, can only be accessed by other members&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; m_year;  &lt;span class=&quot;hljs-comment&quot;&gt;// private by default, can only be accessed by other members&lt;/span&gt;
};

&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; main()
{
    DateClass &lt;span class=&quot;hljs-keyword&quot;&gt;date&lt;/span&gt;;
    &lt;span class=&quot;hljs-keyword&quot;&gt;date&lt;/span&gt;.m_month = &lt;span class=&quot;hljs-number&quot;&gt;10&lt;/span&gt;;  &lt;span class=&quot;hljs-comment&quot;&gt;// error&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;date&lt;/span&gt;.m_day = &lt;span class=&quot;hljs-number&quot;&gt;14&lt;/span&gt;;    &lt;span class=&quot;hljs-comment&quot;&gt;// error&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;date&lt;/span&gt;.m_year = &lt;span class=&quot;hljs-number&quot;&gt;2020&lt;/span&gt;; &lt;span class=&quot;hljs-comment&quot;&gt;// error&lt;/span&gt;

    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 프로그램을 컴파일하면 오류가 발생한다. 왜냐하면, 기본적으로 클래스의 모든 구성원이 &lt;code&gt;private&lt;/code&gt;이기 때문이다. &lt;code&gt;private&lt;/code&gt; 멤버는 오직 클래스의 다른 멤버만 접근할 수 있는 비공개 멤버다. &lt;em&gt;main()&lt;/em&gt;은 &lt;em&gt;DateClass&lt;/em&gt;의 멤버가 아니므로 &lt;em&gt;m_month&lt;/em&gt; 등에 접근할 수 없다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-access-specifiers-&quot;&gt;접근 지정자 (access specifiers)&lt;/h2&gt;
&lt;p&gt;클래스 멤버는 기본적으로 &lt;code&gt;private&lt;/code&gt;이지만 &lt;code&gt;public&lt;/code&gt; 키워드를 사용해서 공개할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;lang-cpp&quot;&gt;&lt;span class=&quot;hljs-class&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;DateClass&lt;/span&gt;
&lt;/span&gt;{
&lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt;: &lt;span class=&quot;hljs-comment&quot;&gt;// note use of public keyword here, and the colon&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; m_month; &lt;span class=&quot;hljs-comment&quot;&gt;// public, can be accessed by anyone&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; m_day;   &lt;span class=&quot;hljs-comment&quot;&gt;// public, can be accessed by anyone&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; m_year;  &lt;span class=&quot;hljs-comment&quot;&gt;// public, can be accessed by anyone&lt;/span&gt;
};

&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; main()
{
    DateClass &lt;span class=&quot;hljs-keyword&quot;&gt;date&lt;/span&gt;;
    &lt;span class=&quot;hljs-keyword&quot;&gt;date&lt;/span&gt;.m_month = &lt;span class=&quot;hljs-number&quot;&gt;10&lt;/span&gt;;   &lt;span class=&quot;hljs-comment&quot;&gt;// okay because m_month is public&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;date&lt;/span&gt;.m_day = &lt;span class=&quot;hljs-number&quot;&gt;14&lt;/span&gt;;     &lt;span class=&quot;hljs-comment&quot;&gt;// okay because m_day is public&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;date&lt;/span&gt;.m_year = &lt;span class=&quot;hljs-number&quot;&gt;2020&lt;/span&gt;;  &lt;span class=&quot;hljs-comment&quot;&gt;// okay because m_year is public&lt;/span&gt;

    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;DateClass&lt;/em&gt;의 멤버는 이제 &lt;code&gt;public&lt;/code&gt;이므로 &lt;em&gt;main()&lt;/em&gt;에서 직접 접근할 수 있다.&lt;/p&gt;&lt;p&gt;클론 특수문자와 함께 사용하는 &lt;code&gt;public:&lt;/code&gt;을 &lt;strong&gt;접근 지정자&lt;/strong&gt;라고 한다. 접근 지정자는 클래스의 멤버에 접근할 수 있는 사용자를 결정한다. 각 멤버는 접근 지정자의 접근 수준을 &quot;획득&quot;한다. (기본 접근 수준은 private)&lt;/p&gt;&lt;p&gt;C++은 &lt;code&gt;public&lt;/code&gt;, &lt;code&gt;priavte&lt;/code&gt; 및 &lt;code&gt;protected&lt;/code&gt; 세 가지 접근 지정 키워드를 제공한다. &lt;code&gt;public&lt;/code&gt;은 공개 멤버이므로 클래스 외부에서도 접근할 수 있고, &lt;code&gt;private&lt;/code&gt;는 비공개 멤버이므로 클래스 내에서만 접근할 수 있다. &lt;code&gt;protected&lt;/code&gt;는 나중에 &quot;상속&quot;을 다룰 때 설명할 예정이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;public&lt;/code&gt;: 공개 멤버, 클래스 외부에서도 접근 가능&lt;/li&gt;
&lt;li&gt;&lt;code&gt;priavte&lt;/code&gt;: 비공개 멤버, 클래스 내에서만 접근 가능&lt;/li&gt;&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;클래스에는 여러 접근 지정자를 사용하여 각 멤버의 접근 수준을 설정할 수 있다. 한 클래스에서 사용할 수 있는 접근 지정자의 개수에는 제한이 없다.&lt;/p&gt;&lt;p&gt;일반적으로 멤버 변수는 비공개로 하고, 멤버 함수는 공개하는 것이 일반적이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;lang-cpp&quot;&gt; class DateClass &lt;span class=&quot;hljs-comment&quot;&gt;// members are private by default&lt;/span&gt;
{
    &lt;span class=&quot;hljs-built_in&quot;&gt;int&lt;/span&gt; m_month; &lt;span class=&quot;hljs-comment&quot;&gt;// private by default, can only be accessed by other members&lt;/span&gt;
    &lt;span class=&quot;hljs-built_in&quot;&gt;int&lt;/span&gt; m_day;   &lt;span class=&quot;hljs-comment&quot;&gt;// private by default, can only be accessed by other members&lt;/span&gt;
    &lt;span class=&quot;hljs-built_in&quot;&gt;int&lt;/span&gt; m_year;  &lt;span class=&quot;hljs-comment&quot;&gt;// private by default, can only be accessed by other members&lt;/span&gt;

&lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt;:
    &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; setDate(&lt;span class=&quot;hljs-built_in&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;month&lt;/span&gt;, &lt;span class=&quot;hljs-built_in&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;day&lt;/span&gt;, &lt;span class=&quot;hljs-built_in&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;year&lt;/span&gt;) &lt;span class=&quot;hljs-comment&quot;&gt;// public, can be accessed by anyone&lt;/span&gt;
    {
        &lt;span class=&quot;hljs-comment&quot;&gt;// setDate() can access the private members of the class because it is a member of the class itself&lt;/span&gt;
        m_month = &lt;span class=&quot;hljs-built_in&quot;&gt;month&lt;/span&gt;;
        m_day = &lt;span class=&quot;hljs-built_in&quot;&gt;day&lt;/span&gt;;
        m_year = &lt;span class=&quot;hljs-built_in&quot;&gt;year&lt;/span&gt;;
    }

    &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;print&lt;/span&gt;() &lt;span class=&quot;hljs-comment&quot;&gt;// public, can be accessed by anyone&lt;/span&gt;
    {
        std::cout &amp;lt;&amp;lt; m_month &amp;lt;&amp;lt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;/&quot;&lt;/span&gt; &amp;lt;&amp;lt; m_day &amp;lt;&amp;lt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;/&quot;&lt;/span&gt; &amp;lt;&amp;lt; m_year;
    }
};

&lt;span class=&quot;hljs-built_in&quot;&gt;int&lt;/span&gt; main()
{
    DateClass birthDate;
    birthDate.setDate(&lt;span class=&quot;hljs-number&quot;&gt;7&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;28&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;1992&lt;/span&gt;); &lt;span class=&quot;hljs-comment&quot;&gt;// okay, because setDate() is public&lt;/span&gt;
    birthDate.&lt;span class=&quot;hljs-built_in&quot;&gt;print&lt;/span&gt;(); &lt;span class=&quot;hljs-comment&quot;&gt;// okay, because print() is public&lt;/span&gt;

    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;m_month&lt;/em&gt;, &lt;em&gt;m_day&lt;/em&gt;, &lt;em&gt;m_year&lt;/em&gt;은 &lt;code&gt;private&lt;/code&gt;하므로 &lt;em&gt;main()&lt;/em&gt;에서 직접 접근할 수 없지만, &lt;em&gt;setDate()&lt;/em&gt; 및 &lt;em&gt;print()&lt;/em&gt; &lt;code&gt;public&lt;/code&gt; 멤버 함수를 통해 간접적으로 접근할 수 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/998260405E26ABFA33&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F998260405E26ABFA33&quot; width=&quot;50&quot; height=&quot;50&quot; filename=&quot;cpp3.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;번역: 이 포스트의 원문은 &lt;/span&gt;&lt;a href=&quot;https://www.learncpp.com/cpp-tutorial/83-public-vs-private-access-specifiers/&quot;&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;https://www.learncpp.com/cpp-tutorial/83-public-vs-private-access-specifiers/&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt; 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>C++ 이야기/09. 객체지향 프로그래밍</category>
      <category>C++</category>
      <category>C++ 기초</category>
      <category>객체지향 프로그래밍</category>
      <category>클래스</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/242</guid>
      <comments>https://boycoding.tistory.com/242#entry242comment</comments>
      <pubDate>Tue, 21 Jan 2020 16:54:35 +0900</pubDate>
    </item>
    <item>
      <title>[2019년 회고] 게으름, 성장, 두려움 그리고 도전</title>
      <link>https://boycoding.tistory.com/241</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/995356495DF30C2D1C?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/995356495DF30C2D1C?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/995356495DF30C2D1C&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F995356495DF30C2D1C&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;2019calendar&quot; loading=&quot;lazy&quot; width=&quot;128&quot; height=&quot;128&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1 style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span style=&quot;font-size: 15px; font-weight: normal;&quot;&gt;2019년을 회고하려 한다. 2019년은 학교의 품을 떠나 여러 경험을 했고, 그 과정에서 많은 것을 배울 수 있었다. 아랫글은 2018년 8월부터 2019년 11월까지의 내 삶 일부가 담겨있다.&lt;/span&gt;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. N에서의 경험&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난해 말부터 올해 초까지 6개월간 소위 3N이라고 불리는 큰 게임회사 중 한 곳에서 근무했다. 아직 한 학기가 남은 학생 신분이었으나 학교 교수님의 추천장에 힘입어 코테와 2번의 면접을 거친 후 근무를 시작하게 된 것이다. 입사 후에는 OJT를 시작으로 크고 작은 이슈와 함께 신규 파티 시스템이나 업적 같은 신규 콘텐츠 개발에도 기여를 했다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;203&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/991495495DF237B041?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/991495495DF237B041?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/991495495DF237B041&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F991495495DF237B041&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;3n&quot; loading=&quot;lazy&quot; width=&quot;662&quot; height=&quot;203&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;203&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 팀에 속한 클라이언트 프로그래머는 20명이나 되었고, 개개인이 모두 나보다 뛰어난 경험과 깊은 지식을 가지고 있었다. 학원과 학교에서 항상 최고라고 생각했던&amp;nbsp;나는 내가 우물 안 개구리라는 걸 깨닫게 되었고, 그분들을 보며 배운게 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 이 과정에서 나는 6개월도 안 돼서 거대한 게임 공장의 부속품이라는 생각과 함께 매너리즘에 빠지게 되었다. 이 글을 읽는 독자는 '게임 회사 신입이 무슨 매너리즘에 빠질 수 있겠는가?' 하며 궁금할 수 있겠다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매너리즘(mannerism): 예술 창작에 있어서, 늘 같은 수법을 되풀이하여 신선미나 독창성을 잃는 일. 순화어는 `타성'.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 빠진 매너리즘은 게임 그 자체에 대한 매너리즘이다. 나는 이 기간에 MMORPG 게임을 개발했고, 이 과정에서 게임에 대한 내 생각과 의견은 전혀 필요하지 않았다. 내가 2017년에 게임 프로그래머로 방향을 튼 이유는 단순히 게임 프로그래밍을 하기 위해서가 아니다. 내가 표현하고 싶은 걸 게임으로 표현하고 싶어 시작했고, 이 과정에는 나의 프로그래밍뿐만 아니라 내 생각도 들어가야 한다. 이러한 이유로 언제부터인가 출근하는 내 모습이 오로지 프로그래밍만을 위해 게임 공장에 가는 부속품같이 느껴지기 시작했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 반성과 성장&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 나는 퇴사했다. 그리고 나는 근무 기간 동안 직무에 있어서 자기계발을 하지 않았다. 입사 면접 때 나는 유니티를 모른다고 말했기에 주로 C++과 DirectX 및 CS에 대한 질문으로 이루어졌다. 입사 한 후 계속해서 업무에 유니티 엔진을 사용했지만 내가 관련 서적을 본 것은 처음 딱 한 번 얇은 책이 끝이었다. 유니티에 대한 이 얕은 지식으로 업무를 계속 해왔고, 큰 불편함을 느끼지 못했기에 당연히 게을러 졌다. 그 결과 유니티에 대한 깊은 지식의 부재로 이어졌다. 내가 얼마나 오만했는지 알 수 있는 대목이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/997FAB4A5DF237E701?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/997FAB4A5DF237E701?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/997FAB4A5DF237E701&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F997FAB4A5DF237E701&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;git&quot; loading=&quot;lazy&quot; width=&quot;960&quot; height=&quot;226&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퇴사 후에는 크게 반성하고 나의 약점을 찾아가며 공부했다. 나는 게임을 만드는 사람이다. 내가 어떤 게임을 만들든 그 게임에 내 목소리를 내고 싶다면 적어도 한 가지는 잘해야 한다고 생각한다. 그리고 그 한 가지는 나에게 있어 프로그래밍이다. 가장 기본 지식인 컴퓨터 구조와 운영체제를 시작으로 유니티 엔진까지 집중해서 공부했다. 유니티 엔진은 더 이상 책을 통한 학습이 나에게 의미가 없다고 생각하여 유니티 엔진 아래에서 잘 만들어진 프레임워크들을 분석하면서 유니티 본연의 컴포넌트와 기능 및 게임 설계에 대한 공부를 이어나갔다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 게임 기획을 배우다.&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 졸업 후에 바로 취직하기보다는 좀 더 내 게임을 만드는 도전을 하고 싶었다. 그러나 내가 게임공학과에서 재학하던 시절 학기마다 과제로 만들었던 게임 프로젝트들은 성적은 좋았지만, 재미는 없었다. 내가 웹 프로그래머일 당시에도 여러 번 개인 프로젝트를 통해 창업을 도전을 한 적이 있다. 재미가 없는 게임을 만드는 것과 함께 기존의 도전이 실패한 원인은 기획력의 부재라고 판단했다. 그리고 이 기획력을 키우기 위한 교육 기간에서 교육을 받기로 결심한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/9928CA465DF23FC216?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/9928CA465DF23FC216?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9928CA465DF23FC216&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9928CA465DF23FC216&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;gamedesign&quot; loading=&quot;lazy&quot; width=&quot;660&quot; height=&quot;340&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: center;&quot;&gt;학원에서 6개월가량 게임 기획을 교육받았다. 여러 게임에 대한 기획 및 분석 뿐만 아니라 두 번의 보드 게임 만들기 프로젝트와 2D 게임 개발 프로젝트도 경험했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;이 과정을 통해 다른 시선으로 게임을 바라보는 법을 배웠다.&quot;&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 로그라이크 슈팅 게임&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그라이크(Roguelike): 거의 최초의 &lt;a href=&quot;https://namu.wiki/w/던전&quot;&gt;던전&lt;/a&gt; 탐색 &lt;a href=&quot;https://namu.wiki/w/RPG&quot;&gt;RPG&lt;/a&gt;인 《&lt;a href=&quot;https://namu.wiki/w/로그#s-2.2&quot;&gt;Rogue&lt;/a&gt;》의 특징과 시스템을 모방하여 만든 &lt;a href=&quot;https://namu.wiki/w/게임&quot;&gt;게임&lt;/a&gt;을 총칭하는 말.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔터 더 건전, 던그리드, 아이작, 뉴클리어쓰론, 궁수의 전설 등등 내가 평소 좋아하고 즐기는 게임들은 모두 로그라이크 시스템과 슈팅을 가지고 있다. 게임을 하는 시간이 부족하면 유튜브를 통해 신작 로그라이크 슈팅 게임을 즐겨 보는 등 이 장르는 내가 가장 좋아하는 게임 장르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학교에서 진행한 프로젝트 게임은 모두 로그라이크 슈팅과 관련이 있으며, 심지어 기획 학원에서 만든 대부분의 역기획서와 분석서 및 프로젝트 역시 로그라이크 슈팅으로 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기획 학원이 끝나갈 무렵 한 게임 회사로부터 먼저 면접 요청을 받았다. 면접날 로그라이크 슈팅 프로젝트를 보여줬고, 이 프로젝트는 마치 나를 위해 존재하는 것 같았다. 내가 가장 자신 있는 시스템 뿐만 아니라 익숙한 콘텐츠와 아트 풍까지 담겨있으니 입사를 하지 않을 이유가 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-decoration: line-through;&quot;&gt;그러나 여러 이유로 인해 5주 후 퇴사한다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;55&quot; data-ke-size=&quot;size23&quot;&gt;5. 5주 동안의 경험 (게임 개발에 있어 나의 약점을 알게되다.)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임은 만들 수 있다고 만들어지는 게 아니다. 기획/아트/프로그래밍/마케팅/운영 등 여러 요소가 어우러져야 한다. 그러나 나는 이 모든 것을 미숙함과 함께 혼자 도전하고 있다. 이 글을 쓰면서 어떤 분의 여러 충고가 생각난다. 게임 개발에 있어서 프로페셔널한 분의 충고인 만큼 기억에 남는다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 코드는 나중에 고민하라. 기능 구현에 집중하고, 이 경험들을 통해 나중에 더 좋은 코드를 자연스럽게 적용해라.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 코드와 설계에 고민하고 집중하니라 기능 구현에 있어 일정을 못 맞출 때의 충고다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘텐츠를 밀어 넣는 단계까지 가본 경험이 없다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확하다.&amp;nbsp;지금까지 경험한 여러 게임 프로젝트들은 모두 게임을 대표하는 시스템 구현과 구현 과정에서 필요한 최소 콘텐츠만을 가진 체로 끝났다. 이것은 그분이 말한 '지구력'과 '좋은 코드'와도 관계가 있는데 멀리 내다보지 못하고 초반에 너무 힘을 쓴 나머지 쉽게 지치고 쉽게 흥미를 잃어 다른 것을 찾아보게 되는 거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이러한 약점들은 현재 고쳐나가는 중이다.&lt;/b&gt;&lt;/p&gt;
&lt;h2 id=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;한 번 더 내 게임이라는 꿈을 향해&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제목 그대로 &quot;한 번 더 내 게임이라는 꿈을 향해&quot; 도전하고 있다. 11월부터 현재까지 진행 중인 사항이므로 회고할 것은 없다. 내년에 쓸 회고문에서 실패가 아닌 성공으로 표현할 수 있길 바랄 뿐이다.&lt;/p&gt;</description>
      <category>겜공 이야기</category>
      <category>2019</category>
      <category>회고</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/241</guid>
      <comments>https://boycoding.tistory.com/241#entry241comment</comments>
      <pubDate>Thu, 12 Dec 2019 22:31:04 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 예산, 쉽게 생각하면 쉬운 문제</title>
      <link>https://boycoding.tistory.com/240</link>
      <description>&lt;h1 style=&quot;text-align: left; clear: none; float: none; margin-bottom: 15px; font-size: 30px;&quot; id=&quot;h1h1&quot;&gt;예산&lt;/h1&gt;

&lt;blockquote&gt;
  &lt;p&gt;S사에서는 각 부서에 필요한 물품을 지원해 주기 위해 부서별로 물품을 구매하는데 필요한 금액을 조사했습니다. 그러나, 전체 예산이 정해져 있기 때문에 모든 부서의 물품을 구매해 줄 수는 없습니다. 그래서 최대한 많은 부서의 물품을 구매해 줄 수 있도록 하려고 합니다.&lt;/p&gt;
  
  &lt;p&gt;물품을 구매해 줄 때는 각 부서가 신청한 금액만큼을 모두 지원해 줘야 합니다. 예를 들어 1,000원을 신청한 부서에는 정확히 1,000원을 지원해야 하며, 1,000원보다 적은 금액을 지원해 줄 수는 없습니다.&lt;/p&gt;
  
  &lt;p&gt;부서별로 신청한 금액이 들어있는 배열 d와 예산 budget이 매개변수로 주어질 때, 최대 몇 개의 부서에 물품을 지원할 수 있는지 return 하도록 solution 함수를 완성해주세요.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;완전탐색 문제인지 알았다. 조합을 통해서 요소들의 합이 &lt;em&gt;budget&lt;/em&gt;보다 같거나 작을 때 가장 개수가 많은 방식으로 접근했다. 그러나 시간 효율성을 통과하지 못했고, 그 결과 깊은 고민에 빠졌다.&lt;/p&gt;&lt;p&gt;문제를 어렵게 생각하니 풀이도 어려웠다. 프로그래머스 1단계 문제 중 가장 오랫동안 고민했다. 답은 너무나도 쉬웠다. 혹시나 해봐서 했더니 되었다. 그냥 주어진 배열을 오름차순으로 정렬한 뒤 앞에서부터 차례대로 더해간 뒤 budget 값보다 크면 지금까지 더한 개수를 반환하면 된다... 수학적으로도 맞는 접근이다...&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

int solution(vector&amp;lt;int&amp;gt; d, int budget)
{
    int count = 0;
    int sum = 0;
    sort(d.begin(), d.end());

    for(int num : d)
    {
        sum += num;

        if(sum &amp;lt;= budget)
        {
            count++;
        }
        else
        {
            break;
        }
    }

    return count;
}
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 100px; width: 100px; height: 100px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99980A485D946A3101&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99980A485D946A3101&quot; width=&quot;100&quot; height=&quot;100&quot; alt=&quot;budget&quot; filename=&quot;money-bag.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 100px; height: 100px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/225&quot; target=&quot;_blank&quot;&gt;2018/12/19 - [프로그래머스] 소수의 합, 소수 판별 알고리즘&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/226&quot; target=&quot;_blank&quot;&gt;2018/12/21 - [프로그래머스] 완주하지 못한 선수, map과 unordered_map의 차이&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/227&quot; target=&quot;_blank&quot;&gt;2018/12/23 - [프로그래머스] N으로 표현&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/228&quot; target=&quot;_blank&quot;&gt;2018/12/23 - [프로그래머스] 모의고사&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/229&quot; target=&quot;_blank&quot;&gt;2018/12/23 - [프로그래머스] K번째수, vector.assign()&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/230&quot; target=&quot;_blank&quot;&gt;2018/12/25 - [프로그래머스] 체육복, 벡터 삭제 시 주의점 (iterator, erase)&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/231&quot; target=&quot;_blank&quot;&gt;2018/12/26 - [프로그래머스] 가운데 글자 가져오기, 짝수 홀수 판별 알고리즘&lt;/a&gt;&lt;/p&gt;</description>
      <category>프로그래머스/Level 1</category>
      <category>Level 1</category>
      <category>프로그래머스</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/240</guid>
      <comments>https://boycoding.tistory.com/240#entry240comment</comments>
      <pubDate>Wed, 2 Oct 2019 18:14:52 +0900</pubDate>
    </item>
    <item>
      <title>C++ 09.02 - 클래스와 클래스 멤버</title>
      <link>https://boycoding.tistory.com/239</link>
      <description>&lt;h1 id=&quot;&quot;&gt;클래스와 클래스 멤버&lt;/h1&gt;

&lt;p&gt;객체 지향 프로그래밍에서, 우리는 종종 우리의 타입이 데이터를 보유할 뿐만 아니라, 데이터와 함께 작동하는 기능을 제공하기를 원한다. C++에서는 이 작업을 &lt;code&gt;class&lt;/code&gt; 키워드를 통해 수행한다. &lt;code&gt;class&lt;/code&gt; 키워드를 사용하면 &lt;strong&gt;클래스&lt;/strong&gt;라고 하는 새로운 사용자 정의 타입을 정의할 수 있다.&lt;/p&gt;

&lt;h2 id=&quot;class&quot;&gt;클래스 (class)&lt;/h2&gt;

&lt;p&gt;C++에서 클래스와 구조체는 본질적으로 같다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;struct DateStruct
{
    int year;
    int month;
    int day;
};

class DateClass
{
public:
    int m_year;
    int m_month;
    int m_day;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;중요한 차이점은 클래스의 &lt;code&gt;public&lt;/code&gt; 키워드뿐이다. 이것은 다음 포스트에서 자세히 설명할 예정이다.&lt;/p&gt;&lt;p&gt;구조체 선언과 마찬가지로, 클래스 선언은 메모리에 할당되지 않는다. 클래스가 어떻게 보이는지만 정의한다.&lt;/p&gt;&lt;p&gt;구조체와 마찬가지로, 클래스를 사용하려면 해당 클래스 타입의 변수를 선언해야 한다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;DateClass birthDay { 1992, 7, 28 }; // declare a variable of class DateClass
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;C++에서는 클래스의 변수를 정의할 때 클래스의 &lt;strong&gt;인스턴스화(instantiating)&lt;/strong&gt;라고 부른다. 변수 자체를 클래스의 &lt;strong&gt;인스턴스(instance)&lt;/strong&gt;라고 한다. 또한, 클래스 타입의 변수는 &lt;strong&gt;객체(object)&lt;/strong&gt;라고도 불린다. 내장형(예: &lt;code&gt;int x&lt;/code&gt;)의 변수를 정의하는 방식이 해당 변수에 메모리를 할당하는 방법과 마찬가지로, 객체를 인스턴스화(예: &lt;code&gt;DateClass birthDay&lt;/code&gt;)하면 해당 객체에 대한 메모리가 할당된다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;memberfunction&quot;&gt;멤버 함수 (member function)&lt;/h2&gt;

&lt;p&gt;데이터를 보유하는 것 외에도 클래스(or 구조체)에는 함수가 포함될 수 있다. 클래스의 내부에서 정의된 함수를 &lt;strong&gt;멤버 함수(member function)&lt;/strong&gt;라고 한다. 때로는 메소드(method)라고 불리기도 한다. 멤버 함수는 클래스 정의의 내부 또는 외부에서 정의할 수 있다. 여기서는 클래스 안에 정의하고 나중에 클래스 외부에 정의하는 방법을 보여줄 것이다.&lt;/p&gt;&lt;p&gt;다음은 날짜를 출력하는 멤버 함수가 있는 &lt;em&gt;Date&lt;/em&gt; 클래스다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class DateClass
{
public:
    int m_year;
    int m_month;
    int m_day;

    void print() // defines a member function named print()
    {
        std::cout &amp;lt;&amp;lt; m_year &amp;lt;&amp;lt; &quot;/&quot; &amp;lt;&amp;lt; m_month &amp;lt;&amp;lt; &quot;/&quot; &amp;lt;&amp;lt; m_day;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;구조체의 멤버처럼 클래스의 멤버(변수 및 함수)는 멤버 선택 연산자(&lt;code&gt;.&lt;/code&gt;)를 사용하여 접근할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int main()
{
    DateClass birthDay { 2019, 7, 28 };

    birthDay.m_year = 1992; // 클래스의 멤버 변수 접근, 값 수정
    birthDay.print();       // 클래스의 멤버 함수 호출

    return 0;
}

// 1992/7/28
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;모든 멤버 함수 호출은 클래스의 객체와 연결되어 있어야 한다. &lt;code&gt;birthDay.print()&lt;/code&gt;를 호출하면 컴파일러에게 &lt;em&gt;birthDay&lt;/em&gt; 객체와 연관된 &lt;em&gt;print()&lt;/em&gt; 멤버 함수를 호출하도록 알려주게 된다.&lt;/p&gt;&lt;p&gt;&lt;em&gt;print()&lt;/em&gt; 멤버 함수를 다시 살펴보자:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void print() // defines a member function named print()
{
    std::cout &amp;lt;&amp;lt; m_year &amp;lt;&amp;lt; &quot;/&quot; &amp;lt;&amp;lt; m_month &amp;lt;&amp;lt; &quot;/&quot; &amp;lt;&amp;lt; m_day;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;m_year&lt;/em&gt;, &lt;em&gt;m_month&lt;/em&gt; 및 &lt;em&gt;m_day&lt;/em&gt;는 멤버 함수가 실제로 연관된 객체의 멤버를 참조한다.&lt;/p&gt;&lt;p&gt;따라서 &lt;code&gt;birthDay.print()&lt;/code&gt;를 호출하면 &lt;em&gt;m_year&lt;/em&gt;을 &lt;em&gt;birthDay.m_year&lt;/em&gt;로 .. &lt;em&gt;m_day&lt;/em&gt;를 &lt;em&gt;birthDay.m_day&lt;/em&gt;로 해석한다.&lt;/p&gt;&lt;p&gt;이런 식으로, 연관된 객체가 암시적으로 멤버 함수에 전달된다. 이러한 이유로 &lt;strong&gt;암시적 객체(implict object)&lt;/strong&gt;라고도 한다. 다음 포스트에서 암시적 객체에 관한 내용을 자세히 다룰 예정이다. 중요한 점은 멤버 함수가 항상 작업할 클래스의 암시적 객체를 가지고 있다는 것이다.&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99FEC7465D932EC519&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99FEC7465D932EC519&quot; width=&quot;50&quot; height=&quot;50&quot; filename=&quot;cpp3.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;번역: 이 포스트의 원문은 https://www.learncpp.com/cpp-tutorial/82-classes-and-class-members/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <category>C++ 이야기/09. 객체지향 프로그래밍</category>
      <category>C++</category>
      <category>C++ 기초</category>
      <category>객체지향 프로그래밍</category>
      <category>클래스</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/239</guid>
      <comments>https://boycoding.tistory.com/239#entry239comment</comments>
      <pubDate>Tue, 1 Oct 2019 19:50:06 +0900</pubDate>
    </item>
    <item>
      <title>C++ 09.01 - 객체 지향 프로그래밍 소개</title>
      <link>https://boycoding.tistory.com/238</link>
      <description>&lt;h1&gt;객체 지향 프로그래밍 소개&lt;/h1&gt;
&lt;p&gt;01.02 - 변수에서 객체는 값을 저장하고 사용할 수 있는 메모리 조각이라고 정의했다. 이름있는 객체를 변수라고 한다.&lt;/p&gt;&lt;p&gt;전통적으로 프로그래밍은 기본적으로 객체라는 데이터를 정의한 다음 해당 데이터로 작업하는 컴퓨터에 대한 명령 목록이다. 데이터와 그 데이터에 작용하는 함수들은 원하는 결과를 내기 위해 결합하는 분리된 엔티티다. 이러한 분리 때문에 현실에 대한 직관적인 표현을 제공하지 않는다. 속성(변수)을 적절히 관리하고 행동(함수)에 연결하는 것은 프로그래머의 몪이다. 예를 들면 다음과 같다:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;driveTo(you, work);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;객체 지향 프로그래밍(OOP: Object-oriented programming)은 속성과 동작을 함께 결합하여 재사용 가능한 패키지로 만드는 기능을 제공한다. 다음과 같이 보이는 코드로 이어진다:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;you.driveTo(work);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이것은 더 명확하게 읽을 수 있을 뿐만 아니라, 누가 주체이고, 어떤 행동을 할지 분명히 알 수 있다. 함수를 작성하는 데 초점을 맞추기보다는 명확하게 정의된 동작 세트가 있는 객체를 정의하는 데 중점을 둔다. 때문에 이 패러다임을 &quot;객체 지향&quot;이라고 한다.&lt;/p&gt;&lt;p&gt;즉, 프로그램을 단순히 데이터와 처리 방법으로 나누는 것이 아니라, 프로그램을 수많은 '객체'라는 기본 단위로 나누고 이 객체들의 상호작용으로 서술하는 방식이다.&lt;/p&gt;&lt;p&gt;객체 지향은 프로그램을 보다 모듈화된 방식으로 작성할 수 있고, 더 쉽게 쓰고 이해할 수 있으며, 더 높은 수준의 코드 재사용성을 제공한다. 또한, 이러한 객체들은 프로그래머가 객체들과 어떻게 상호작용하는지 그리고 그것들이 다른 객체들과 어떻게 상호작용하는지를 정의함으로써 프로그래머의 데이터로 작업하는 더 직관적인 방법을 제공한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;컴퓨터과학에서 &lt;strong&gt;객체&lt;/strong&gt; 또는 &lt;strong&gt;오브젝트&lt;/strong&gt;(object)는 저장공간에서 할당되어 값을 가지거나식별자, 자료 구조, 함수 또는 메소드가 될 수 있다. 프로그래밍 언어는 변수를 이용해 객체에 접근하므로 &lt;em&gt;객체&lt;/em&gt;와 &lt;em&gt;변수&lt;/em&gt;라는 용어는 종종 함께 사용된다. 그러나 메모리가 할당되기 전까지 객체는 존재하지 않는다. &lt;/p&gt;&lt;p&gt;절차적 프로그래밍에서 하나의 객체는 자료나 명령을 포함할 수 있지만 두 가지를 동시에 포함하지는 않는다. (명령은 프로시저나 함수의 형태를 가진다).&lt;/p&gt;&lt;p&gt;객체 지향 프로그래밍에서 객체는 클래스의 인스턴스이다. 클래스 객체는 자료와 그 자료를 다루는 명령의 조합을 포함하여 객체가 메시지를 받고 자료를 처리하며 메시지를 다른 객체로 보낼 수 있도록 한다. 실세계의 유추로 설명하자면, 만약 어떤 사람이 집에서 살기를 원할 때, 그 집의 청사진(집의 사진)이나 축소 모형 따위는 전혀 필요가 없다. 필요한 것은 설계에 맞는 실제 집이다. 이 유추에서 청사진은 클래스를 나타내고 실제 집은 객체를 나타낸다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;객체라는 용어에 혼란이 올 수 있다. 전통적인 프로그래밍에서 '객체'는 값을 저장하는 메모리 조각이다. 객체 지향 프로그래밍에서 '객체'는 속성과 동작을 결합한 객체라는 것을 의미한다.&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/998DC9365D8434C406&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F998DC9365D8434C406&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;cpp&quot; filename=&quot;cpp3.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 11pt;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: 11pt;&quot;&gt;번역: 이 포스트의 원문은 &lt;/span&gt;&lt;a href=&quot;https://www.learncpp.com/cpp-tutorial/81-welcome-to-object-oriented-programming/&quot;&gt;&lt;span style=&quot;font-size: 11pt;&quot;&gt;https://www.learncpp.com/cpp-tutorial/81-welcome-to-object-oriented-programming/&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-size: 11pt;&quot;&gt; 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>C++ 이야기/09. 객체지향 프로그래밍</category>
      <category>C++</category>
      <category>C++ 기초</category>
      <category>객체지향 프로그래밍</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/238</guid>
      <comments>https://boycoding.tistory.com/238#entry238comment</comments>
      <pubDate>Fri, 20 Sep 2019 11:12:01 +0900</pubDate>
    </item>
    <item>
      <title>정수의 표시</title>
      <link>https://boycoding.tistory.com/237</link>
      <description>&lt;h1 style=&quot;text-align: left; clear: none; float: none; font-size: 30px;&quot; id=&quot;h1h1&quot;&gt;정수의 표시&lt;/h1&gt;

&lt;p&gt;이 포스팅에서는 정수를 표시하기 위해 사용할 수 있는 두 가지 방법:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;양수만 표시할 수 있는 방법&lt;/li&gt;

&lt;li&gt;음수, 0, 양수 모두를 표시할 수 있는 방법&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;에 대해 이야기할 예정이다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;c&quot; style=&quot;margin-bottom: 10px;&quot;&gt;C 언어의 정수형 데이터 타입&lt;/h2&gt;

&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 450px; width: 450px; height: 187px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99CFAC3D5C66EE7E06&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99CFAC3D5C66EE7E06&quot; width=&quot;450&quot; height=&quot;187&quot; alt=&quot;0401_64-bit&quot; filename=&quot;0401_64-bit.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 450px; height: 187px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;위 그림은 64비트 프로그램에서의 C 언어의 정수형 데이터 타입의 일반적인 범위를 보여준다. 주목해야 할 중요한 특징은 음수의 범위가 1 더 넓다는 것인데, 왜 이런 특징이 생기는지에 대해서는 음수가 컴퓨터에서 어떻게 표시되는지 알면 이해할 수 있을 것이다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;-1&quot;&gt;비부호형 인코딩&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;unsigned&lt;/strong&gt;는 C/C++ 언어에서 사용되는 지정자로 정수형과 같이 사용되어 부호 비트를 제거해 저장 가능한 양수 범위를 두배로 늘이는 역할을 한다. char와 int의 signed 정수형 변수에서 MSB가 부호 비트이다. 1이면 음수이고 0이면 양수이다. 그러나 unsigned을 사용하면 음수를 사용하지 않겠다는 의미 이므로 부호 비트가 필요 없다. 따라서 이진수와 같은 십진수가 된다. -위키백과-&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;n비트의 정수형 데이터 타입이 있다고 하자. 전체  벡터를 나타내기 위해서 벡터의 각 비트를 [Xn-1, Xn-2,  ..., X0]으로 표시해서 사용할 수 있다. X를 이진수 표시(0 or1)로 작성된 숫자로 생각하면 &lt;strong&gt;비부호형&lt;/strong&gt; 해석을 할 수 있다. X가 1의 값을 갖는 경우는 2^i 값이 정수의 값을 계산할 때 포함되어야 한다는 것을 의미한다.&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 400px; width: 400px; height: 248px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/998E47335C66EE9F26&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F998E47335C66EE9F26&quot; width=&quot;400&quot; height=&quot;248&quot; alt=&quot;0402_unsigned&quot; filename=&quot;0402_unsigned.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 400px; height: 248px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;ul&gt;

&lt;li&gt;[0001] = 0 * 2^3 + 0 * 2^2 + 0 * 2^1 + 1 * 2^0 = 0 + 0 + 0 + 1 = 1&lt;/li&gt;

&lt;li&gt;[0101] = 0 * 2^3 + 1 * 2^2 + 0 * 2^1 + 1 * 2^0 = 0 + 4 + 0 + 1 = 5&lt;/li&gt;

&lt;li&gt;[1011] = 1 * 2^3 + 0 * 2^2 + 1 * 2^1 + 1 * 2^0 = 8 + 0 + 2 + 1 = 11&lt;/li&gt;

&lt;li&gt;[1111] = 1 * 2^3 + 1 * 2^2 + 1 * 2^1 + 1 * 2^0 = 8 + 4 + 2 + 1 = 15&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;n비트로 표시할 수 있는 값의 범위를 보자. 정수 0은 비트 벡터 [00 ... 0]을 가지며 최소값이 되고, 최대값은  [11 ... 1]이 된다. n이 4인 4비트의 경우를 예로 들면 [1111] = 2^4 -1 =.15가 된다. 즉, 비부호형 값의 범위는 0과 2*n -1이다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;2&quot;&gt;2의 보수 인코딩: 부호형&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;2의 보수&lt;/strong&gt;란 어떤 수를 커다란 2의 제곱수에서 빼서 얻은 이진수이다. 2의 보수는 대부분의 산술연산에서 원래 숫자의 음수처럼 취급된다. 주어진 이진수보다 한 자리 높고 가장 높은 자리가 1이며 나머지가 0인 수에서 주어진 수를 빼서 얻은 수가 2의 보수이다. 혹은 주어진 이진수의 모든 자리의 숫자를 반전(0을 1로, 1을 0으로)시킨 뒤 여기에 1을 더하면 2의 보수를 얻을 수 있다. -위키백과-&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;많은 시스템에서 음수 값을 표시한다. 부호형 숫자를 표시하는 경우 가장 일반적으로 &lt;strong&gt;2의 보수&lt;/strong&gt;를 사용한다. 이것은 워드(word)에서 가장 중요한 비트(MSB)를 음수 자리값을 갖는 것으로 해석하는 형태로 정의된다.&lt;/p&gt;
&lt;p&gt;벡터의 각 비트 [Xn-1, Xn-2,  ..., X0]에서 가장 중요한 비트(MSB)인 Xn-1을 &lt;strong&gt;부호 비트&lt;/strong&gt;라고 부른다.&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 400px; width: 400px; height: 267px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/992161375C66EEE52E&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F992161375C66EEE52E&quot; width=&quot;400&quot; height=&quot;267&quot; alt=&quot;0403_signed&quot; filename=&quot;0403_signed.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 400px; height: 267px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[0001] = -0 * 2^3 + 0 * 2^2 + 0 * 2^1 + 1 * 2^0 = 0 + 0 + 0 + 1 =  1&lt;/li&gt;

&lt;li&gt;[0101] = -0 * 2^3 + 1 * 2^2 + 0 * 2^1 + 1 * 2^0 = 0 + 4 + 0 + 1 =  5&lt;/li&gt;

&lt;li&gt;[1011] = -1 * 2^3 + 0 * 2^2 + 1 * 2^1 + 1 * 2^0 = -8 + 0 + 2 + 1 = -5&lt;/li&gt;

&lt;li&gt;[1111] = -1 * 2^3 + 1 * 2^2 + 1 * 2^1 + 1 * 2^0 = -8 + 4 + 2 + 1 = -1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;위 그림에서 왼쪽으로 향하는 막대를 사용해서 부호 비트가 음의 자리값을 갖는 것을 나타낸다. 가장 중요한 비트(MSB)가 1일 때 이것은 음수를 나타낸다.&lt;/p&gt;
&lt;p&gt;n비트 2의 보수로 나타낼 수 있는 값의 범위를 보자. 가장 장은 수는 비트 벡터 [10 ... 0]이며, 가장 큰 값은 [01 ... 1이다. 4비트를 예로 들면 -8 ~ 7이다. 즉, 2의 보수 값의 범위는 2^(n-1) ~ 2^(n-1) - 1이다.&lt;/p&gt;

&lt;hr&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/992BDA495C66F09338&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F992BDA495C66F09338&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;digital&quot; filename=&quot;digital.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>컴구 이야기</category>
      <category>컴퓨터시스템</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/237</guid>
      <comments>https://boycoding.tistory.com/237#entry237comment</comments>
      <pubDate>Sat, 16 Feb 2019 02:00:51 +0900</pubDate>
    </item>
    <item>
      <title>C++ 08.11 - std::vector의 capacity 및 stack 동작</title>
      <link>https://boycoding.tistory.com/236</link>
      <description>&lt;h1 style=&quot;text-align: left; clear: none; float: none; font-size: 30px;&quot; id=&quot;h1h1&quot;&gt;08.11 - std::vector의 capacity 및 stack 동작&lt;/h1&gt;

&lt;p&gt;이전 포스트&amp;nbsp;&lt;a href=&quot;https://boycoding.tistory.com/214&quot; target=&quot;_blank&quot;&gt;2018/07/31 - C++ 07.22 - std::vector 소개&lt;/a&gt;에서&amp;nbsp;&lt;code&gt;std::vector&lt;/code&gt;가 동적 배열이라는 것을 이야기했다.&lt;/p&gt;&lt;p&gt;&lt;code&gt;std::vector&lt;/code&gt;는 가장 유용하게 많이 사용되므로 다른 속성과 기능에 대해서 좀 더 이야기해보자.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;lengthvscapacity&quot;&gt;Length vs. Capacity&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int* array = new int[10] { 1, 2, 3, 4, 5 };
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 예제 코드는 요소 5개만 할당하였어도 배열의 길이는 10이라고 말할 수 있다.&lt;/p&gt;&lt;p&gt;초기화한 요소만 사용하고, 사용하지 않는 요소들을 미래에 확장하기 위해 남겨두기 위해서는 어떻게 해야 할까? 위 예제에서는 할당된 요소 수에서 &quot;사용하는&quot; 요소 수를 별도로 기억해야 한다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int* array = new int[10] { 1, 2, 3, 4, 5 }; // 미래에 확장 위해 10개로 미리 잡아놓는다.
int length = 5; // 사용하는 요소 수

for(int i = 0; i &amp;lt; length; ++i)
{
    // ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;길이만 기억하는 내장 배열이나 std::array와 달리 &lt;code&gt;std::vector&lt;/code&gt;에는 &lt;code&gt;length&lt;/code&gt;와 &lt;code&gt;capacity&lt;/code&gt;의 두 가지 속성이 있다. &lt;code&gt;length&lt;/code&gt;는 배열에서 사용되는 요소의 수이며, &lt;code&gt;capacity&lt;/code&gt;는 메모리에 할당된 용량의 크기다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int main()
{
    std::vector&amp;lt;int&amp;gt; array { 0, 1, 2 };
    array.resize(5); // set length to 5

    std::cout &amp;lt;&amp;lt; &quot;The length is: &quot; &amp;lt;&amp;lt; array.size() &amp;lt;&amp;lt; '\n';

    for (auto const &amp;amp;element: array)
        std::cout &amp;lt;&amp;lt; element &amp;lt;&amp;lt; ' ';

    return 0;
};
/* The length is: 5
   0 1 2 0 0      */
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 예제 코드는 &lt;code&gt;resize()&lt;/code&gt; 함수를 사용해서 벡터의 길이를 5로 지정했다. 이는 배열에 처음 5개 요소를 사용한다는 것을 알린다. 그렇다면 이 배열의 용량은 얼마일까? &lt;code&gt;capacity()&lt;/code&gt; 함수를 통해 용량을 알 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int main()
{
    std::vector&amp;lt;int&amp;gt; array { 0, 1, 2 };
    array.resize(5); // set length to 5

    std::cout &amp;lt;&amp;lt; &quot;The length is: &quot; &amp;lt;&amp;lt; array.size() &amp;lt;&amp;lt; '\n';
    std::cout &amp;lt;&amp;lt; &quot;The capacity is: &quot; &amp;lt;&amp;lt; array.capacity() &amp;lt;&amp;lt; '\n';
}

/* The length is: 5
   The capacity is: 5 */
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위에서 resize() 함수는 std::vector의 길이(length)와 용량(capacity)을 모두 변경했다. 용량은 항상 길이보다 크거나 같다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;morelengthvscapacity&quot;&gt;More Length vs. Capacity&lt;/h2&gt;

&lt;p&gt;왜 길이(length)와 용량(capacity)을 구별할까? &lt;strong&gt;std::vector가 메모리를 재할당하기 때문이다.&lt;/strong&gt; 배열의 크기를 조정하는 것은 비용이 크기 때문에 될 수 있으면 피하는 게 좋다. 다음 예제를 보자:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int main()
{
    std::vector&amp;lt;int&amp;gt; array;
    array = { 0, 1, 2, 3, 4 }; // okay, array length = 5
    std::cout &amp;lt;&amp;lt; &quot;length: &quot; &amp;lt;&amp;lt; array.size() &amp;lt;&amp;lt; &quot;  capacity: &quot; &amp;lt;&amp;lt; array.capacity() &amp;lt;&amp;lt; '\n';

    array = { 9, 8, 7 }; // okay, array length is now 3!
    std::cout &amp;lt;&amp;lt; &quot;length: &quot; &amp;lt;&amp;lt; array.size() &amp;lt;&amp;lt; &quot;  capacity: &quot; &amp;lt;&amp;lt; array.capacity() &amp;lt;&amp;lt; '\n';

    return 0;
}

/* length: 5  capacity: 5
   length: 3  capacity: 5 */
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;벡터에 더 작은 배열을 할당했지만, 메모리를 재할당하지 않았다. (용량이 여전히 5다.) 단순히 길이를 변경했으므로 처음 세 요소만이 현재 유효하다는 것을 알 수 있다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;&quot;&gt;용량이 아닌 길이를 기준으로 한다.&lt;/h2&gt;

&lt;p&gt;배열의 첨자 연산자(&lt;code&gt;[]&lt;/code&gt;) 및 &lt;code&gt;at()&lt;/code&gt; 함수의 범위는 용량이 아닌 길이를 기준으로 한다. 앞의 예제에서 길이 3과 용량 5의 배열을 고려할 때, 인덱스 4로 배열 요소에 접근하려고 하면 4가 배열 길이보다 크기 때문에 실패한다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;stdvectorstack&quot;&gt;std::vector로 stack 동작&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;std::vector&lt;/code&gt;는 동적 배열이지만 &lt;code&gt;스택(stack)&lt;/code&gt;처럼 동작할 수 있다. 이를 위해 다음 3가지 함수를 사용한다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;push_back&lt;/code&gt; : 스택의 요소를 푸시한다.&lt;/li&gt;

&lt;li&gt;&lt;code&gt;back()&lt;/code&gt; : 스택의 최상위 값을 반환한다.&lt;/li&gt;

&lt;li&gt;&lt;code&gt;pop_back()&lt;/code&gt; : 스택에서 요소를 팝한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void printStack(const std::vector&amp;lt;int&amp;gt; &amp;amp;stack)
{
    for (const auto &amp;amp;element : stack)
        std::cout &amp;lt;&amp;lt; element &amp;lt;&amp;lt; ' ';
    std::cout &amp;lt;&amp;lt; &quot;(cap &quot; &amp;lt;&amp;lt; stack.capacity() &amp;lt;&amp;lt; &quot; length &quot; &amp;lt;&amp;lt; stack.size() &amp;lt;&amp;lt; &quot;)\n&quot;;
}

int main()
{
    std::vector&amp;lt;int&amp;gt; stack;

    printStack(stack);

    stack.push_back(5); // push_back() pushes an element on the stack
    printStack(stack);

    stack.push_back(3);
    printStack(stack);

    stack.push_back(2);
    printStack(stack);

    std::cout &amp;lt;&amp;lt; &quot;top: &quot; &amp;lt;&amp;lt; stack.back() &amp;lt;&amp;lt; '\n'; // back() returns the last element

    stack.pop_back(); // pop_back() back pops an element off the stack
    printStack(stack);

    stack.pop_back();
    printStack(stack);

    stack.pop_back();
    printStack(stack);

    return 0;
}
/*
(cap 0 length 0)
5 (cap 1 length 1)
5 3 (cap 2 length 2)
5 3 2 (cap 3 length 3)
top: 2
5 3 (cap 3 length 2)
5 (cap 3 length 1)
(cap 3 length 0) */
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;함수가 호출될 때 필요한 경우 벡터의 용량이 자동 조정된다. 위 예제에서는 3번 조정되었다. 벡터의 용량이 조정되는 것은 메모리를 재할당하는 동작이므로 비용이 크다. 그러므로 &lt;strong&gt;벡터에 일정량의 용량을 미리 할당함으로써 성능 향상을 꾀할 수 있다.&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void printStack(const std::vector&amp;lt;int&amp;gt; &amp;amp;stack)
{
    for (const auto &amp;amp;element : stack)
        std::cout &amp;lt;&amp;lt; element &amp;lt;&amp;lt; ' ';
    std::cout &amp;lt;&amp;lt; &quot;(cap &quot; &amp;lt;&amp;lt; stack.capacity() &amp;lt;&amp;lt; &quot; length &quot; &amp;lt;&amp;lt; stack.size() &amp;lt;&amp;lt; &quot;)\n&quot;;
}

int main()
{
    std::vector&amp;lt;int&amp;gt; stack;

    stack.reserve(5); // Set the capacity to (at least) 5

    printStack(stack);

    stack.push_back(5);
    printStack(stack);

    stack.push_back(3);
    printStack(stack);

    stack.push_back(2);
    printStack(stack);

    std::cout &amp;lt;&amp;lt; &quot;top: &quot; &amp;lt;&amp;lt; stack.back() &amp;lt;&amp;lt; '\n';

    stack.pop_back();
    printStack(stack);

    stack.pop_back();
    printStack(stack);

    stack.pop_back();
    printStack(stack);

    return 0;
}
/* 
(cap 5 length 0)
5 (cap 5 length 1)
5 3 (cap 5 length 2)
5 3 2 (cap 5 length 3)
top: 2
5 3 (cap 5 length 2)
5 (cap 5 length 1)
(cap 5 length 0) */
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;용량을 미리 5로 설정함으로써 프로그램이 실행되는 동안 메모리 재할당이 일어나지 않았다.&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/991F694B5C652FAA0C&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F991F694B5C652FAA0C&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;cpp&quot; filename=&quot;cpp2.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 11pt;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: 11pt;&quot;&gt;번역: 이 포스트의 원문은 https://www.learncpp.com/cpp-tutorial/7-10-stdvector-capacity-and-stack-behavior/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <category>C++ 이야기/08. 함수</category>
      <category>C++</category>
      <category>STL</category>
      <category>함수</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/236</guid>
      <comments>https://boycoding.tistory.com/236#entry236comment</comments>
      <pubDate>Thu, 14 Feb 2019 18:07:47 +0900</pubDate>
    </item>
    <item>
      <title>C++ 08.10 - 스택과 힙 (Stack and Heap)</title>
      <link>https://boycoding.tistory.com/235</link>
      <description>&lt;h1 style=&quot;text-align: left; clear: none; float: none; font-size: 30px;&quot; id=&quot;h1h1&quot;&gt;스택과 힙 (Stack and Heap)&lt;/h1&gt;

&lt;p&gt;프로그램이 사용하는 메모리는 일반적으로 &lt;strong&gt;세그먼트(segment)&lt;/strong&gt;라고 하는 몇 가지 다른 영역으로 나뉜다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;코드 세그먼트 : 컴파일된 프로그램이 저장되는 영역, 일반적으로 read-only 속성이다.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;데이터 세그먼트 : 전역 변수 및 정적 변수가 저장되는 영역&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;힙 세그먼트 : 동적으로 할당된 변수가 할당되는 영역&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;스택 세그먼트 : 함수 매개 변수, 지역 변수 및 기타 함수 관련 정보가 저장되는 영역&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;

&lt;h2 id=&quot;heapsegment&quot;&gt;힙 세그먼트 (Heap segment)&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;힙 세그먼트&lt;/strong&gt;는 동적 메모리 할당에 사용되는 메모리를 추적한다. C++에서 &lt;code&gt;new&lt;/code&gt; 연산자를 사용해서 메모리를 할당하면 이 메모리는 응용 프로그램의 힙 세그먼트에 할당된다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int* ptr   = new int; //   ptr은 힙에서 4바이트로 할당된다.
int* array = new int[10]; // array는 힙에서 40바이트로 할당된다.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이 메모리의 주소는 연산자 &lt;code&gt;new&lt;/code&gt;에 의해 포인터에 저장될 수 있다. 주의할 점은 이 메모리 요청이 순차적 메모리 주소를 할당하는 결과가 아닐 수 있다는 것이다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int* ptr1 = new int;
int* ptr2 = new int;
// ptr1과 ptr2은 순차적인 주소가 아닐 수 있다.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;동적으로 할당된 변수가 삭제되면 메모리는 힙으로 &quot;반환&quot;되고, 이후 다시 할당될 수 있다. 포인터를 삭제하면 변수가 삭제되는 것이 아니라 관련 주소의 메모리를 운영 체제에 반환하는 것이다.&lt;/p&gt;&lt;p&gt;힙에는 장단점이 있다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;힙에 메모리를 할당하는 것은 비교적 느리다.&lt;/li&gt;

&lt;li&gt;할당된 메모리는 명시적으로 할당 해제하거나 응용 프로그램이 종료될 때까지 유지된다. (메모리 릭 주의)&lt;/li&gt;

&lt;li&gt;동적으로 할당된 메모리는 포인터를 통해 접근한다: 포인터를 역참조하는 것은 변수에 직접 접근하는 것보다 느리다.&lt;/li&gt;

&lt;li&gt;힙은 큰 메모리 풀이므로 큰 배열, 구조체 또는 클래스를 할당할 수 있다.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;

&lt;h2 id=&quot;stacksegment&quot;&gt;스택 세그먼트 (Stack segment)&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;스택 세그먼트(=콜 스택)&lt;/strong&gt;는 메인() 함수부터 현재 실행 지점까지의 모든 활성 함수를 추적하고 모든 함수 매개 변수와 지역 변수의 할당을 처리한다.&lt;/p&gt;&lt;p&gt;스택은 후입선출(LIFO) 자료구조다. 즉, 가장 늦게 들어간 자료를 가장 먼저 꺼내게 된다. 함수 호출이 끝나고, 이전 함수로 돌아갈 때 이 함수의 바로 이전 함수로 돌아가야 한다. 그래서 컴퓨터는 내부적으로 스택 세그먼트를 스택 자료구조로 구현한다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;콜 스택&lt;/strong&gt;(&lt;strong&gt;call stack&lt;/strong&gt;) 이란 컴퓨터 프로그램에서 현재 실행 중인 서브루틴(함수)에 관한 정보를 저장하는 &lt;strong&gt;스택&lt;/strong&gt; 자료구조이다. -위키백과-&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;응용프로그램이 시작되면 메인() 함수가 운영체제에 의해 호출 스택에 푸시된다. 그 후 프로그램이 실행되기 시작한다. 함수호출이 발생하면 함수가 콜 스택에 푸시된다. 현재 함수가 끝나면 해당 함수는 콜 스택에서 해제된다. 따라서 콜 스택에 푸시된 함수를 살펴보면 현재 실행 지점으로 이동하기 위해 호출된 모든 함수를 볼 수 있다.&lt;/p&gt;&lt;p&gt;콜 스택 자체는 고정된 크기의 메모리 영역이다. 여기서 콜 스택에 넣고 빼는 데이터 자체를 &lt;strong&gt;스택 프레임(stack frame)&lt;/strong&gt;이라고 한다. 스택 프레임은 하나의 함수 호출과 관련된 모든 데이터를 추적한다. 또한 &lt;strong&gt;스택 포인터(Stack Pointer)&lt;/strong&gt;라고 하는 CPU의 작은 조각인 레지스터는 현재 호출 스택의 최상위 위치를 가리킨다.&lt;/p&gt;

&lt;h4 id=&quot;thecallstackinaction&quot;&gt;- The call stack in action&lt;/h4&gt;

&lt;p&gt;콜 스택이 어떻게 작동하는지 좀 더 자세히 살펴보자. 함수를 호출할 때 발생하는 단계는 다음과 같다.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;프로그램에 함수 호출이 발생한다.&lt;/li&gt;

&lt;li&gt;스택 프레임이 생성되고 콜 스택에 푸시된다. 스택 프레임은 다음과 같이 구성된다:&lt;/li&gt;

&lt;li&gt;

&lt;ul&gt;
&lt;li&gt;함수가 종료되면 복귀할 주소&lt;/li&gt;

&lt;li&gt;함수의 모든 매개 변수&lt;/li&gt;

&lt;li&gt;지역 변수&lt;/li&gt;

&lt;li&gt;함수가 반환할 때 복원해야 하는 수정된 레지스터의 복사본&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;

&lt;li&gt;CPU가 함수의 시작점으로 점프한다.&lt;/li&gt;

&lt;li&gt;함수 내부의 명령어를 실행한다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;함수가 종료되면 다음 단계가 수행된다:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;레지스터가 콜 스택에서 복원된다.&lt;/li&gt;

&lt;li&gt;스택 프레임이 콜 스택에서 튀어나온다. 이렇게 하면 모든 지역 변수와 매개 변수에 대한 메모리가 해제된다.&lt;/li&gt;

&lt;li&gt;반환 값이 처리된다.&lt;/li&gt;

&lt;li&gt;CPU는 반환 주소에서 실행을 재개한다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;반환 값은 시스템 아키텍처에 따라 여러 가지 방법으로 처리한다. 반환 값이 스택 프레임의 일부로 포함되기도 하며, CPU 레지스터를 사용하기도 한다.&lt;/p&gt;&lt;p&gt;일반적으로 콜 스택이 어떻게 동작하는지에 대한 모든 세부 사항이 중요하지는 않다. 그러나 함수가 호출될 때와 종료될 때 함수가  콜 스택에서 효과적으로 작동한다는 것을 이해하면 재귀를 이해할 때와 디버깅할 때 유용하다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;stackoverflow&quot;&gt;스택 오버플로 (Stack overflow)&lt;/h2&gt;

&lt;p&gt;스택 세그먼트는 크기가 제한되어 있으므로 제한된 양의 데이터만 저장할 수 있다. window 운영체제에서 기본 스택 세그먼트의 크기는 1MB다. 응용 프로그램이 스택 세그먼트에 너무 많은 정보를 넣으려고 하면 &lt;strong&gt;스택 오버플로(stack overflow)&lt;/strong&gt;가 발생한다. 스택 오버플로(Stack overflow)는 스택 세그먼트의 모든 메모리가 할당되어 꽉 찼을 때 발생하며, 이 경우 추가 할당이 메모리의 다른 섹션으로 넘치기 시작한다.&lt;/p&gt;&lt;p&gt;스택 오버플로는 일반적으로 스택 세그먼트에 너무 많은 변수를 할당하거나 중첩된 함수 호출(A calls function B calls function C calls funct D...)을 너무 많이 한 결과다. 보통 스택 오버플로가 발생하면 프로그램이 다운된다.&lt;/p&gt;&lt;p&gt;다음은 스택 오버플로를 일으킬 수 있는 예제 프로그램이다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int main()
{
    int stack[100000000];
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 프로그램은 스택에 거대한 크기의 배열을 할당하려고 시도한다. 그러나 스택 세그먼트 크기가 배열을 처리할 만큼 충분히 크지 않으므로 배열 할당은 응용 프로그램에서 사용할 수 없는 메모리 부분까지 오버플로 된다. 따라서 프로그램이 다운된다.&lt;/p&gt;&lt;p&gt;다음은 다른 이유로 스택 오버플로를 발생시키는 또 다른 예제 프로그램이다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void foo()
{
    foo();
}

int main()
{
    foo();

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 프로그램은 함수 &lt;em&gt;foo()&lt;/em&gt;가 호출 될 때마다 스택 프레임이 스택 세그먼트에 푸시된다. &lt;em&gt;foo()&lt;/em&gt; 함수는 무한히 호출되기 때문에 결국에는 스택 메모리가 꽉 차서 스택 오버플로가 발생한다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;스택에 메모리를 할당하는 것은 비교적 빠르다.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;스택에 할당된 메모리는 스택 범위에 있을 때만 접근할 수 있다.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;스택에 할당된 모든 메모리는 컴파일 타임에 알려진다. 메모리는 변수를 통해 직접 접근할 수 있다.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;스택은 비교적 크기가 작으므로 스택 공간을 많이 차지하는 지역 변수를 만드는 것은 좋지 않다.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/995AA1465C627F1830&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F995AA1465C627F1830&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;cpp&quot; filename=&quot;cpp2.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;font color=&quot;#8c8c8c&quot;&gt;&lt;span style=&quot;font-size: 11pt; color: rgb(140, 140, 140);&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/font&gt;&lt;span style=&quot;font-size: 11pt; color: rgb(140, 140, 140);&quot;&gt;번역: 이 포스트의 원문은 https://www.learncpp.com/cpp-tutorial/79-the-stack-and-the-heap/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>C++ 이야기/08. 함수</category>
      <category>C++</category>
      <category>함수</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/235</guid>
      <comments>https://boycoding.tistory.com/235#entry235comment</comments>
      <pubDate>Tue, 12 Feb 2019 17:11:30 +0900</pubDate>
    </item>
    <item>
      <title>동시성과 병렬성 훑어보기</title>
      <link>https://boycoding.tistory.com/234</link>
      <description>&lt;h1 style=&quot;text-align: left; clear: none; float: none; font-size: 30px;&quot; id=&quot;h1h1&quot;&gt;동시성과 병렬성 훑어보기&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;동시성(concurrency)&lt;/strong&gt;은 다수의 동시에 벌어지는 일을 갖는 시스템에 관한 일반적인 개념을 말할 때 사용하며, &lt;strong&gt;병렬성(parallelism)&lt;/strong&gt;은 동시성을 사용해서 시스템을 더 빠르게 동작하도록 하는 것을 말한다.&lt;/p&gt;

&lt;h3 id=&quot;-1&quot;&gt;스레드 수준 동시성&lt;/h3&gt;

&lt;p&gt;프로세스 추상화 개념을 이용하면 다수의 프로그램이 동시에 실행되는 시스템을 생각해볼 수 있으며, 이것은 &lt;strong&gt;동시성&lt;/strong&gt;으로 이어진다. &lt;strong&gt;스레드&lt;/strong&gt;를 이용하면 한 개의 프로세스 내에서 실행되는 다수의 제어 흐름을 가질 수 있다.&lt;/p&gt;
&lt;p&gt;1960년대 초반 &lt;em&gt;시간 공유(time sharing)&lt;/em&gt; 기법의 출현으로 동시 실행이 나타났다. 이것은 한 개의 컴퓨터가 실행하는 프로세스를 빠르게 전환하는 방법을 사용한다. 이런 형태의 동시성은 여러 명이 한 개의 웹 서버로부터 페이지를 사용하고자 할 때처럼 다수의 사용자들이 시스템과 동시에 교신할 수 있게 한다. 또한 하나의 PC에서 웹 브라우저와 문서 편집기를 실행하면서 스트리밍 음악을 플레이하는 것처럼 한 명의 사용자가 다수의 태스크를 동시에 연관될 수 있게 해준다. 최근까지도 대부분의 실질적인 계산은 한 개의 프로세서에 의해 이루어지고 있다. 이러한 시스템 구성을 &lt;strong&gt;단일 프로세서 시스템&lt;/strong&gt;이라고 한다.&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 400px; width: 400px; height: 210px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9946CD4A5C625D7A33&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9946CD4A5C625D7A33&quot; width=&quot;400&quot; height=&quot;210&quot; alt=&quot;02_multiprocessors&quot; filename=&quot;02_multiprocessors.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 400px; height: 210px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;어떤 시스템이 여러 개의 프로세서를 가지고 하나의 운영체제 커널의 제어 하에 동작하는 경우를 &lt;strong&gt;멀티 프로세서 시스템&lt;/strong&gt;이라고 한다. 옛날에는 대규모의 계산에나 이용되었지만, 최근에는 &lt;strong&gt;멀티코어 프로세서&lt;/strong&gt;와 &lt;strong&gt;하이퍼스레딩&lt;/strong&gt; 기법의 출현으로 일반적인 환경이 되었다.&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 400px; width: 400px; height: 362px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99DAC74C5C625D942E&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99DAC74C5C625D942E&quot; width=&quot;400&quot; height=&quot;362&quot; alt=&quot;02_i7&quot; filename=&quot;02_i7.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 400px; height: 362px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;멀티코어 프로세서&lt;/strong&gt;는 하나의 칩에 여러 개의 CPU(코어)를 내장하고 있다. 위 그림은 인텔 i7 프로세서 코어의 구성을 보여준다. i7은 네 개의 CPU 코어를 가지고 있으며, 각각 별도의 L1, L2 캐시를 가지고 코어는 메인 메모리 인터페이스뿐만 아니라 상위 수준 캐시를 공유한다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;하이퍼스레딩&lt;/strong&gt;(&lt;strong&gt;Hyper-Threading&lt;/strong&gt; Technology, 이하 HTT)은 인텔이 동시 멀티&lt;strong&gt;스레딩&lt;/strong&gt;을 구현한 기술이다. 물리상 실행 장치 한 개에 가상 실행 장치 두 개를 할당해 성능을 높이려는 기술이다. -위키백과-&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;멀티스레딩&lt;/strong&gt;이라고도 하는 &lt;strong&gt;하이퍼스레딩&lt;/strong&gt;은 하나의 CPU가 여러 개의 제어 흐름을 실행할 수 있게 해주는 기술이다. 하나의 물리적 CPU안에 프로그램 카운터나 레지스터 파일 같은 논리적 하드웨어 사본을 여러 개 가지고 있다. (반면에 부동소수점 연산기는 하나만 있다.)&lt;/p&gt;
&lt;p&gt;기존의 프로세서가 스레드 간의 전환을 하는 데 약 2만 클럭 사이클이 요구되는 반면에 하이퍼스레드 프로세서에서는 매 사이클마다 실행할 스레드를 결정한다. 예를 들어 어느 스레드가 데이터를 캐시에 로딩하기 위해 대기해야 한다면, CPU는 다른 스레드를 실행할 수 있다. 위 인텔 i7 프로세서에서는 각각의 코어가 두 개의 스레드를 실행할 수 있으며, 4코어 시스템이므로 8개의 스레드를 병렬로 실행할 수 있다.&lt;/p&gt;

&lt;hr&gt;

&lt;h3 id=&quot;instructionlevelparallelism&quot;&gt;명령어 수준 병렬성(Instruction-Level Parallelism)&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;명령어 수준 병렬성&lt;/strong&gt;이란 훨씬 낮은 수준에서의 추상화로 여러 개의 명령어를 한 번에 실행하는 것을 말한다. 옛날에는 한 개의 명령어를 실행하는 데 여러 클럭 사이클이 필요했지만, 최근의 프로세서들은 매 클럭마다 여러 개의 명령어를 실행할 수 있다. 사이클당 한 개 이상의 명령어를 실행할 수 있는 프로세서를 &lt;strong&gt;슈퍼스칼라(super-scalar)&lt;/strong&gt;라고 한다. 최근 프로세서들은 대부분 슈퍼스칼라를 지원한다. 응용 프로그래머는 이것을 통해 자신이 작성한 프로그램의 성능을 이해할 수 있게 된다. 이를 통해 작성된 코드가 더 높은 수준의 명령어 수준 병렬성을 갖게 될 것이며, 따라서 더 빨리 동작하게 된다.&lt;/p&gt;

&lt;hr&gt;

&lt;h3 id=&quot;simd&quot;&gt;싱글 명령어 다중 데이터 병렬성 (SIMD)&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;SIMD&lt;/strong&gt;(Single Instruction Multiple Data)는 병렬 프로세서의 한 종류로, 하나의 명령어로 여러 개의 값을 동시에 계산하는 방식이다. -위키백과-&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;최신 프로세서들은 최하위 수준에서 &lt;strong&gt;SIMD 병렬성&lt;/strong&gt;이라는 모드로 한 개의 명령어가 병렬로 다수의 연산을 수행할 수 있는 특수 하드웨어를 가지고 있다. 예를 들어 인텔과 AMD사의 최신 프로세서는 8쌍의 단일 정밀도 부동소수(c언어: float)들 간의 덧셈 연산을 병렬로 실행할 수 있는 명령어를 가지고 있다.&lt;/p&gt;

&lt;hr&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/991938465C625DBC2C&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F991938465C625DBC2C&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;concurrency&quot; filename=&quot;ball.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>컴구 이야기</category>
      <category>컴퓨터시스템</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/234</guid>
      <comments>https://boycoding.tistory.com/234#entry234comment</comments>
      <pubDate>Tue, 12 Feb 2019 14:48:32 +0900</pubDate>
    </item>
    <item>
      <title>C++ 08.09 - 함수 포인터 (function pointer)</title>
      <link>https://boycoding.tistory.com/233</link>
      <description>&lt;h1 style=&quot;text-align: left; clear: none; float: none; font-size: 30px;&quot; id=&quot;h1h1&quot;&gt;함수 포인터 (function pointer)&lt;/h1&gt;

&lt;p&gt;포인터가 다른 변수의 주소를 저장하는 변수라는 것을 배웠다. 이와 유사하게 &lt;strong&gt;함수 포인터(function pointer)&lt;/strong&gt;는 함수를 가리키는 변수다. 즉, 함수의 주소를 저장하는 변수다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int foo()
{
    return 5;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;식별자 &lt;em&gt;foo&lt;/em&gt;는 함수의 이름이다. 그러나 함수의 타입은 무엇일까? 함수는 고유한 l-value 함수 타입이다. 위 예제의 경우 정수(int)를 반환하고 매개 변수를 받지 않는 함수 타입이다. 변수와 마찬가지로 함수는 메모리의 할당된 주소에 있다.&lt;/p&gt;
&lt;p&gt;() 연산자를 통해 함수를 호출하면, 호출되는 함수의 주소로 점프하여 실행한다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int foo() // foo 코드는 메모리 주소 0x002717f0에서 시작한다.
{
    return 5;
}

int main()
{
    foo(); // 주소 0x002717f0로 점프한다.

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;함수 &lt;em&gt;foo&lt;/em&gt;를 &lt;code&gt;std::cout&lt;/code&gt;로 출력하면 0x002717f0가 출력된다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;pointertofunction&quot;&gt;함수에 대한 포인터 (pointer to function)&lt;/h2&gt;

&lt;p&gt;비 상수 함수 포인터(non-const function pointer) 생성하는 문법은 C++에서 볼 수 있는 못생긴&amp;nbsp;문법 중 하나다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;// fcnPtr 는 인수가 없고 정수를 반환하는 함수에 대한 포인터다.
int (*fcnPtr)();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 코드에서 &lt;em&gt;fcnPtr&lt;/em&gt;은 인수가 없고 정수를 반환하는 함수에 대한 포인터다.(즉, 함수 포인터)&amp;nbsp;그러므로 이 타입과 같은 함수를 가리킬 수 있다. 괄호가 필요한 이유는&lt;code&gt;int* fcnPtr()&lt;/code&gt;과 같은 코드는 정수에 대한 포인터를 반환하는 인수가 없는 함수 fcnPtr의 전방 선언으로 해석되기 때문에 우선순위를 위해서다.&lt;/p&gt;
&lt;p&gt;또한, 오류를 발생시키는 흔한 실수는 아래와 같다:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;fcnPtr = goo();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;상수 함수 포인터를 만들기 위해서는 &lt;code&gt;*&lt;/code&gt; 뒤에 &lt;code&gt;const&lt;/code&gt; 키워드를 사용하면 된다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int (*const fcnPtr)();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;만약 int 앞에 const가 오면 함수가 const int를 반환한다는 것을 의미한다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;&quot;&gt;함수 포인터에 함수 할당&lt;/h2&gt;

&lt;p&gt;함수 포인터는 함수로 초기화 할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int foo()
{
    return 5;
}

int goo()
{
    return 6;
}

int main()
{
    int (*fcnPtr)() = foo; // fcnPtr points to function foo
    fcnPtr = goo; // fcnPtr now points to function goo

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;C++는 기본 자료형과 달리 필요할 경우 함수를 함수 포인터로 암묵적으로 변환하므로 주소 연산자 &amp;amp;를 사용할 필요가 없다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;-1&quot;&gt;함수 포인터를 사용해서 함수 호출&lt;/h2&gt;

&lt;p&gt;함수 포인터로 실제 함수를 호출할 수 있다. 호출하는 두 가지 방법이 있는데, 첫 번째는 명시적인 역참조 방법이다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int foo(int x)
{
    return x;
}

int main()
{
    int (*fcnPtr)(int) = foo; // fcnPtr에 foo함수를 할당한다.
    (*fcnPtr)(5); // foo(5)를 fcnPtr로 호출한다.

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;두 번째 방법은 암시적 역참조를 통한 방법이다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;fcnPtr(5); // foo(5)를 fcnPtr로 호출한다.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;일반적인 함수 이름은 함수의 포인터가 되기 때문에 암시적 추론을 통한 역참조 방법은 일반 함수 호출과 똑같이 생겼다. 현대 컴파일러는 대부분 이 방법을 지원한다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;passingfunctionsasargumentstootherfunctions&quot;&gt;다른 함수의 인자로 함수 전달하기 (Passing functions as arguments to other functions)&lt;/h2&gt;

&lt;p&gt;함수 포인터의 가장 유용한 기능은 함수를 다른 함수에 전달하는 것이다. 다른 함수에 대한 인수로써 사용되는 함수를 콜백 함수라고 부르기도 한다.&lt;/p&gt;
&lt;p&gt;배열 정렬을 생각해보자. 정렬을 수행하는 기본적인 함수에서 사용자가 오름차순으로 할지 내림차순으로 할지 정할 수 있는 함수를 예로 한다.&lt;/p&gt;
&lt;p&gt;모든 정렬 알고리즘은 유사한 개념으로 작동한다. 숫자 목록을 통해 반복되고, 숫자의 쌍을 비교하며, 그 비교 결과를 바탕으로 숫자의 순서를 바꾼다. 결과적으로, 비교를 변경함으로써 나머지 분류 코드에 영향을 주지 않고 함수의 정렬 방식을 변경할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void SelectionSort(int* array, int size)
{
    for (int i = 0; i &amp;lt; size; ++i)
    {
        int smallestIndex = i;

        for (int j = i + 1; j &amp;lt; size; ++j)
        {
            if (array[smallestIndex] &amp;gt; array[j]) // 비교 부분
                smallestIndex = j;
        }

        std::swap(array[smallestIndex], array[j]);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위에서 비교 부분을 함수를 이용해서 대체할 수 있다. 비교 함수는 두 개의 정수를 비교한 다음 부울 값을 반환함으로써 교체 여부를 나타낼 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;bool ascending(int x, int y)
{
    return x &amp;gt; y; // 첫 번째 요소(x)가 두 번째 요소(y) 보다 크면 교환한다.
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;다음은 &lt;code&gt;ascending()&lt;/code&gt; 함수를 이용해서 비교를 수행하는 선택 정렬 함수다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void SelectionSort(int* array, int size)
{
    for (int i = 0; i &amp;lt; size; ++i)
    {
        int smallestIndex = i;

        for (int j = i + 1; j &amp;lt; size; ++j)
        {
            if (ascending(array[sammlestIndex], array[j])) // 비교 부분
                smallestIndex = j;
        }

        std::swap(array[smallestIndex], array[j]);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이제 하드코딩된 비교 함수를 사용하는 대신 호출자가 비교 작업을 결정할 수 있도록 함수 포인터를 사용해보자.&lt;/p&gt;
&lt;p&gt;호출자의 비교 함수는 두 정수를 비교하고 부울 값을 반환하므로 이러한 함수에 대한 포인터는 아래와 같을 것이다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;bool (*comparisonFcn)(int, int);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;따라서 호출자가 정렬 함수에 원하는 비교 함수의 포인터를 세 번째 매개 변수로 전달하도록 허용한 다음 호출자의 함수를 사용하여 비교를 수행할 수 있다. 다음은 함수 포인터 매개변수를 사용하여 사용자 정의 비교를 수행하는 선택 정렬이다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void SelectionSort(int* array, int size, bool (*comparisonFcn)(int, int))
{
    for (int i = 0; i &amp;lt; size; ++i)
    {
        int smallestIndex = i;

        for (int j = i + 1; j &amp;lt; size; ++j)
        {
            if (comparisonFcn(array[sammlestIndex], array[j])) // 비교 부분
                smallestIndex = j;
        }

        std::swap(array[smallestIndex], array[j]);
    }
}

bool ascending(int x, int y)
{
    return x &amp;gt; y; // 오름차순
}

bool descending(int x, int y)
{
    return x &amp;lt; y; // 내림차순
}

void printArray(int *array, int size)
{
    for (int index=0; index &amp;lt; size; ++index)
        std::cout &amp;lt;&amp;lt; array[index] &amp;lt;&amp;lt; &quot; &quot;;
    std::cout &amp;lt;&amp;lt; '\n';
}

int main()
{
    int array[9] = { 3, 7, 9, 5, 6, 1, 8, 2, 4 };

    // 내린차순 선택정렬
    selectionSort(array, 9, descending);
    printArray(array, 9);

    // 오름차순 선택정렬
    selectionSort(array, 9, ascending);
    printArray(array, 9);

    return 0;
}

9 8 7 6 5 4 3 2 1
1 2 3 4 5 6 7 8 9
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이제 호출자가 원하는 비교 방식으로 정렬할 수 있다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;-2&quot;&gt;함수 포인터를 더 예쁘게 만들기&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;typedef&lt;/code&gt;를 사용해서 함수 포인터를 일반 변수처럼 보이게 할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;typedef bool (*validateFcn)(int, int);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 코드는 인수로 두 개의 int를 가지고 bool 반환하는 &lt;em&gt;validateFcn&lt;/em&gt;라는 함수 포인터를 정의한다.&lt;/p&gt;
&lt;p&gt;이제 이렇게 하는 대신:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;bool validate(int x, int y, bool (*fcnPtr)(int, int)); // ugly
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이렇게 할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;bool validate(int x, int y, validateFcn pfcn) // clean
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;C++ 11에서는 &lt;code&gt;using&lt;/code&gt;을 사용해서 함수 포인터 타입에 대한 별칭을 만들 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;using validateFcn = bool(*)(int, int); // type alias
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;typedef보다 좀 더 자연스럽게 읽을 수 있다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;c11stdfunction&quot;&gt;C++ 11에서의 std::function&lt;/h2&gt;

&lt;p&gt;C++ 11에서는 표준 라이브러리 &lt;code&gt;&amp;lt;function&amp;gt;&lt;/code&gt; 헤더에 정의되어 있는 &lt;code&gt;std::function&lt;/code&gt;을 이용해서 함수 포인터를 정의할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;functional&amp;gt;
bool validate(int x, int y, std::function&amp;lt;bool(int, int)&amp;gt; fcn); //std::function&amp;lt;반환타입(인수..)&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;보시다시피, 반환 타입과 매개 변수는 꺾쇠(&amp;lt;&amp;gt;)안에 들어 있다. 매개 변수를 꺾쇠 속 괄호 안에 둠으로써 좀 더 명시적으로 읽을 수 있다.  매개 변수가 없다면 괄호 안의 내용을 비워두면 된다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;functional&amp;gt;
#include &amp;lt;iostream&amp;gt;

int foo()
{
    return 5;
}

int goo()
{
    return 6;
}

int main()
{
    std::function&amp;lt;int()&amp;gt; fcnPtr; // int를 반환하고 매개 변수가 없는 함수 포인터 변수 fctPtr 선언
    fcnPtr = goo; // fcnPtr은 함수 goo를 가리킨다.
    std::cout &amp;lt;&amp;lt; fcnPtr(); // 일반적인 함수처럼 함수를 호출할 수 있다.

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;

&lt;p&gt;함수 포인터는 주로 함수를 다른 함수에 전달할 때 유용하다. 함수 포인터를 선언하는 원시적인 방법은 보기 나쁘고 오류를 발생시키기 쉽우므로 &lt;code&gt;typedef&lt;/code&gt;, &lt;code&gt;using&lt;/code&gt; 또는 &lt;code&gt;std::function&lt;/code&gt;을 사용하는 게 좋다.&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/994A5D3B5C6109BC22&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F994A5D3B5C6109BC22&quot; width=&quot;50&quot; height=&quot;50&quot; filename=&quot;cpp2.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 11pt;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: 11pt;&quot;&gt;번역: 이 포스트의 원문은 https://www.learncpp.com/cpp-tutorial/78-function-pointers/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>C++ 이야기/08. 함수</category>
      <category>C++</category>
      <category>함수</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/233</guid>
      <comments>https://boycoding.tistory.com/233#entry233comment</comments>
      <pubDate>Mon, 11 Feb 2019 14:38:54 +0900</pubDate>
    </item>
    <item>
      <title>컴퓨터 시스템 훑어보기</title>
      <link>https://boycoding.tistory.com/232</link>
      <description>&lt;h1 style=&quot;text-align: left; clear: none; float: none; margin-bottom: 15px; font-size: 30px;&quot; id=&quot;h1h1&quot;&gt;컴퓨터 시스템 훑어보기&lt;/h1&gt;

&lt;blockquote&gt;
  &lt;p&gt;간단한 &quot;hello, world&quot; 프로그램의 일생 주기를 추적해서 컴퓨터 시스템에서의 주요 아이디어와 주제들을 소개한다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;1hello&quot;&gt;1. hello 프로그램&lt;/h3&gt;

&lt;p&gt;시스템에 관한 공부의 시작은 아래 hello 프로그램이 프로그래머에 의해 만들어지고, 시스템에서 실행되고, 단순한 메시지를 출력하고, 종료될 때까지의 수명주기를 추적하는 것으로 시작한다. 프로그램의 수명주기를 따라가면서 주요 개념과 용어, 그리고 관련된 구성요소를 간략히 알아보겠다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;c++ language-c++&quot;&gt;#include &amp;lt;stdio.h&amp;gt;

int main()
{
    printf(&quot;hello, world\n&quot;);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 hello 프로그램은 프로그래머가 에디터로 작성한 소스 프로그램(파일)로 생명을 시작하여, hello.c라는 텍스트 파일로 저장된다. 소스 파일은 0 또는 1로 표시되는 &lt;strong&gt;비트&lt;/strong&gt;들의 연속이며, &lt;strong&gt;바이트&lt;/strong&gt;라는 &lt;strong&gt;8비트 단위&lt;/strong&gt;로 구성된다. 각 바이트는 소스 파일의 텍스트 문자를 나타낸다. &lt;/p&gt;&lt;p&gt;대부분 컴퓨터 시스템은 텍스트 문자를 아스키(ASCII) 표준을 사용하여 표시한다. 각 바이트는 특정 문자에 대응되는 정수 값을 가진다. 예를 들어 첫 번째 문자 '#'은 아스키 값 35가 된다. 이렇게 아스키 문자들로만 이루어진 파일을 &lt;strong&gt;텍스트 파일&lt;/strong&gt;이라고 부른다. 다른 모든 파일들은 &lt;strong&gt;바이너리 파일&lt;/strong&gt;이라고 한다.&lt;/p&gt;&lt;p&gt;위 방법처럼 모든 시스템 내부의 정보(디스크 파일, 메모리상의 프로그램, 데이터, 네트워크를 통해 전송되는 데이터)는 비트들로 표시된다. 서로 다른 객체들을 구분하는 유일한 방법은 이들을 바라보는 &lt;strong&gt;컨텍스트&lt;/strong&gt;에 의해서다. 다른 컨텍스트에서는 동일한 일련의 바이트가 정수, 부동소수, 문자열 또는 기계어 명령을 의미할 수 있다.&lt;/p&gt;

&lt;hr&gt;

&lt;h3 id=&quot;2&quot;&gt;2. 컴파일 시스템&lt;/h3&gt;

&lt;p&gt;hello.c를 시스템에서 실행시키려면, 각 C 문장들은 다른 프로그램들에 의해 저급 &lt;em&gt;기계어&lt;/em&gt;  명령어들로 번역되어야 한다. 이 명령어들은 &lt;strong&gt;실행 가능 목적&lt;/strong&gt; 프로그램(파일)이라고 하는 형태로 합쳐져서 바이너리 디스크 파일로 저장된다. &lt;strong&gt;컴파일러 드라이버&lt;/strong&gt;는 소스 파일 hello.c를 잃어서 실행파일인 hello로 번역한다. 4개의 단계를 거쳐서 실행되며, 이 네 단계를 실행하는 프로그램을 &lt;strong&gt;컴파일 시스템&lt;/strong&gt;이라고 부른다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 650px; width: 650px; height: 125px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/993CA8335C5EED3604&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F993CA8335C5EED3604&quot; width=&quot;650&quot; height=&quot;125&quot; alt=&quot;0102_compileSystem&quot; filename=&quot;0102_compileSystem.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 650px; height: 125px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;전처리 단계: &lt;strong&gt;전처리기(Pre-processor)&lt;/strong&gt;는 '#'문자로 시작하는 디렉티브에 따라 수정한다. 예를 들어 &quot;#include &lt;stdio.h&gt;&quot;는 전처리기에게 시스템 헤더파일인 stdio.h를 직접 삽입하라고 지시한다. 그 결과 일반적으로 .i로 끝나는 새로운 텍스트 파일 hello.i가 생성된다.&lt;/stdio.h&gt;&lt;/li&gt;

&lt;li&gt;컴파일 단계: 컴파일러는 텍스트 파일 hello.i를 텍스트 파일 hello.s로 번역하며, 이 파일에는 &lt;strong&gt;어셈블리어 프로그램&lt;/strong&gt;이 저장된다.&lt;/li&gt;

&lt;li&gt;어셈블리 단계: 어셈블러가 hello.s를 기계어 명령어로 번역하고, 이들을 &lt;strong&gt;재배치 가능 목적 프로그램&lt;/strong&gt;의 형태로 묶어서 hello.o라는 목적 파일에 그 결과를 저장한다. 이 파일은 바이너리 파일이므로 텍스트 편집기로 열어보면 쓰레기 같은 데이터로 보인다.&lt;/li&gt;

&lt;li&gt;링크 단계: hello 프로그램은 C 컴파일러에서 제공하는 표준 C 라이브러이에 들어있는 printf 함수를 호출한다. printf 함수는 이미 컴파일된 별도의 목적 파일인 printf.o에 들어 있으며, 이 파일은 hello.o 파일과 어떻게든 결합되어야 한다. 링커 프로그램이 이 통합작업을 수행한다. 그 결과 hello 파일은 &lt;strong&gt;실행 가능 목적 파일&lt;/strong&gt;(즉, 실행 파일)로 메모리에 적재되어 시스템에 의해 실행된다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;지금까지 hello.c 소스 파일은 컴파일 시스템에 의해 hello라는 실행 가능한 목적 파일로 번역되어 디스크에 저장되었다.&lt;/p&gt;

&lt;hr&gt;

&lt;h3 id=&quot;3&quot;&gt;3. 시스템의 하드웨어 조직&lt;/h3&gt;

&lt;p&gt;hello 프로그램을 실행할 때 무슨 일이 일어나는지 설명하기 위해서는 전형적인 시스템에서의 하드웨어 조직을 이해할 필요가 있다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 350px; width: 350px; height: 252px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9908FD405C5EED7509&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9908FD405C5EED7509&quot; width=&quot;350&quot; height=&quot;252&quot; alt=&quot;01_systemHardward&quot; filename=&quot;0103_systemHardware.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 350px; height: 252px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;

&lt;h4 id=&quot;-1&quot;&gt;- 버스&lt;/h4&gt;

&lt;p&gt;시스템 내를 관통하는 전기적 배선군을 &lt;strong&gt;버스&lt;/strong&gt;라고 한다. 버스는 컴포넌트들 간에 바이트 정보들을 전송한다. 일반적으로 &lt;strong&gt;워드(word)&lt;/strong&gt;라고 하는 고정 크기의 바이트 단위로 데이터를 전송하도록 설계된다. 한 개의 워드를 구성하는 바이트 수는 시스템마다 보유하는 기본 시스템 변수다. 오늘날 대분분의 컴퓨터들은 4바이트(32비트) 또는 8바이트(64비트) 워드 크기를 갖는다.&lt;/p&gt;

&lt;h4 id=&quot;-2&quot;&gt;- 입출력 장치&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;입출력 장치&lt;/strong&gt;는 시스템과 외부세계와의 연결을 담당한다. 위 그림에 나와있는 시스템은 네 개의 입출력 장치를 가지고 있다: 입령용 키보드와 마우스, 출력용 디스플레이, 데이터와 프로그램의 장기 저장을 위한 디스크 드라이브, 처음에 실행 가능 파일인 hello 프로그램은 디스크에 저장되어 있다. 각 입출력 장치는 입출력 버스와 &lt;strong&gt;컨트롤러&lt;/strong&gt;나 &lt;strong&gt;어댑터&lt;/strong&gt;를 통해 연결된다. 컨트롤러는 디바이스 자체가 칩셋이거나 시스템의 메인보드에 장착된다. 어댑터는 메인보드 슬롯에 장착되는 카드다. 이들 각각의 목적은 입출력 버스와 입출력 장치들 간에 정보를 주고받도록 해주는 일이다.&lt;/p&gt;

&lt;h4 id=&quot;-3&quot;&gt;- 메인 메모리&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;메인 메모리&lt;/strong&gt;는 프로세서가 프로그램을 실행하는 동안 데이터와 프로그램을 모두 저장하는 임시 저장장치다. 물리적으로 메인 메모리는 &lt;strong&gt;DRAM(Dynamic Random Access Memory)&lt;/strong&gt; 칩들로 구성되어 있다. 논리적으로 메모리는 연속적인 바이트들의 배열로, 각각 0부터 시작해서 고유의 주소(배열의 인덱스)를 가지고 있다. 일반적으로 한 개의 프로그램을 구성하는 각 기계어 명령어는 다양한 바이트 크기를 갖는다.&lt;/p&gt;

&lt;h4 id=&quot;-4&quot;&gt;- 프로세서&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;중앙 처리 장치(CPU)&lt;/strong&gt; 또는 간단히 &lt;strong&gt;프로세서&lt;/strong&gt;는 메인 메모리에 저장된 기계어들을 해독(실행)하는 엔진이다. 프로세서의 중심에는 워드 크기의 저장장치(또는 &lt;strong&gt;레지스터&lt;/strong&gt;)인 &lt;strong&gt;프로그램 카운터(PC)&lt;/strong&gt;가 있다. 어느 순간 PC는 메인 메모리의 기계어 명령어를 가리킨다.&lt;/p&gt;&lt;p&gt;시스템에 전원이 공급되는 순간부터 전원이 끊어질 때까지 프로세서는 PC가 가리키는 곳의 명령어를 반복적으로 실행하고 PC값이 다음 명령어의 위치를 가리키도록 업데이트한다. 명령어들은 규칙적인 순서로 실행되고, 한 개의 명령을 실행하는 것은 여러 단계를 수행함으로써 이루어진다. 프로세서는 PC가 가리키는 메모리로부터 명령어를 잃어오고, 이 명령어에서 비트들을 해석하여 명령어가 지정하는 간단한 동작을 실행하고, PC를 다음 명령어 위치로 업데이트한다. 이 새로운 위치는 방금 수행한 명령어와 메모리 상에서 연속적일 수 있고, 그렇지 않을 수도 있다.&lt;/p&gt;&lt;p&gt;위와 같이 몇 개의 단순한 동작만이 있을 뿐이며, 이들은 메인 메모리, 레지스터 파일, 수식/논리 처리기(ALU) 주위를 순환한다. &lt;strong&gt;레지스터 파일&lt;/strong&gt;은 각각 고유의 이름을 갖는 워드 크기의 레지스터 집합으로 구성되어 있다. &lt;strong&gt;ALU&lt;/strong&gt;는 새 데이터와 주소값을 계산한다. 다음은 명령어의 요청에 의해 CPU가 실행하는 단순한 작업의 예이다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;적재(Load): 메인 메모리에서 레지스터에 한 바이트 또는 워드를 이전 값에 덮어쓰는 방식으로 복사한다.&lt;/li&gt;

&lt;li&gt;저장(Store): 레지스터에서 메인 메모리로 한 바이트 또는 워드를 이전 값을 덮어쓰는 방식으로 복사한다.&lt;/li&gt;

&lt;li&gt;작업(Operate): 두 레지스터의 값을 ALU로 복사하고 두 개의 워드로 수식연산을 수행한 뒤, 결과를 덮어쓰는 방식으로 레지스터에 저장한다.&lt;/li&gt;

&lt;li&gt;점프(Jump): 명령어 자신으로부터 한 개의 워드를 추출하고, 이것을 PC에 덮어쓰는 방식으로 복사한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;

&lt;h3 id=&quot;4hello&quot;&gt;4. hello 프로그램의 실행&lt;/h3&gt;

&lt;p&gt;처음에 리눅스 쉘 프로그램은 자신의 명령어를 실행하면서 사용자가 명령을 입력하기를 기다린다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;shell language-shell&quot;&gt;linux&amp;gt; ./hello
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&quot;./hello&quot;를 입력하면 각각의 문자를 레지스터에 읽어 들인 후, 아래 그림과 같이 메모리에 저장한다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 300px; width: 300px; height: 228px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/991A6B365C5EEDF732&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F991A6B365C5EEDF732&quot; width=&quot;300&quot; height=&quot;228&quot; alt=&quot;0104_1&quot; filename=&quot;0104_1.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 300px; height: 228px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;키보드에서 엔터를 누르면 쉘은 명령 입력을 끝마쳤다는 것을 알게 된다. 그러면 쉘은 명령 입력을 끝마쳤다는 것을 알게 된다. 그러면 아래 그림처럼 쉘은 파일 내의 코드와 데이터를 복사하는 일련의 명령어를 실행하여 실행파일 hello를 디스크에서 메인 메모리로 로딩한다. 데이터 부분은 최종적으로 출력되는 문자 스트링인 &quot;hello, world\n&quot;을 포함한다. &lt;strong&gt;직접 메모리 접근(DMA)&lt;/strong&gt;이라고 알려진 기법을 이용해서 데이터는 프로세서를 거치지 않고 디스크에서 메인 메모리로 직접 이동한다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 300px; width: 300px; height: 203px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99F7C5335C5EEE1829&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99F7C5335C5EEE1829&quot; width=&quot;300&quot; height=&quot;203&quot; alt=&quot;0104_2&quot; filename=&quot;0104_2.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 300px; height: 203px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;일단 hello 목적 파일의 코드와 데이터가 메모리에 적재된 후, 아래 그림과 같이 프로세서는 hello 프로그램의 main 루틴의 기계어 명령어를 실행하기 시작한다. 이 명령어들은 &quot;hello, world\n&quot; 문자열을 메모리로부터 레지스터 파일로 복사하고, 거기로부터 디스플레이 장치로 전송하여 화면에 글자들이 표시된다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 300px; width: 300px; height: 205px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99D4B1395C5EEE3F07&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99D4B1395C5EEE3F07&quot; width=&quot;300&quot; height=&quot;205&quot; alt=&quot;0104_3&quot; filename=&quot;0104_3.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 300px; height: 205px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;h3 id=&quot;5&quot;&gt;5. 캐시&lt;/h3&gt;

&lt;p&gt;위 내용으로부터 알게 된 것은 시스템이 정보를 한 곳에서 다른 곳으로 이동시키는 일에 많은 시간을 보낸다는 것이다. hello 프로그램의 기계어 명령어들은 본래 하드디스크에 저장되어 있었다. 프로그램이 로딩될 때 이들은 메인 메모리로 복사된다. 프로세서가 프로그램을 실행할 때 명령어들은 메인 메모리에서 프로세서로 복사된다. 마찬가지로 &quot;hello, world\n&quot; 데이터 스트링도 본래는 디스크에 저장되어 있었지만, 메인 메모리로 복사되었다가 디스플레이 장치로 복사된다. 프로그래머의 관점에서 보면, 이러한 여러 복사과정들이 실제 작업을 느리게 하는 &lt;strong&gt;오버헤드&lt;/strong&gt;다. 그래서 시스템 설계자들의 주요 목적은 이러한 복사과정들을 가능한 한 빠르게 동작하도록 하는 것이다.&lt;/p&gt;&lt;p&gt;일반적인 시스템 디스크 드라이브는 메인 메모리보다 1,000배 크기가 더 크지만, 프로세서가 디스크에서 1 워드의 데이터를 읽어들이는 데 걸리는 시간은 메모리에서보다 천만 배 더 오래 걸릴 수 있다. 마찬가지로 일반적인 레지스터 파일은 수백 바이트의 정보를 저장하는 반면, 메인 메모리의 경우는 십억 개의 바이트를 저장한다. 그러나 프로세서는 레지스터 파일의 데이터를 읽는 데 메모리보다 거의 100배 더 빨리 읽을 수 있다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;캐시 메모리: 프로세서가 단기간에 필요할 가능성이 높은 정보를 임시로 저장하는 메모리&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;프로세서-메모리 간 격차에 대응하기 위해 시스템 설계자는 보다 작고 빠른 &lt;strong&gt;캐시 메모리(캐시)&lt;/strong&gt;라고 부르는 저장장치를 고안하여 프로세서가 단기간에 필요로 할 가능성이 높은 정보를 저장할 목적으로 사용한다. 아래 그림은 일반적인 시스템에서의 캐시 메모리를 보여준다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 450px; width: 450px; height: 179px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99D9EA375C5EEE9209&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99D9EA375C5EEE9209&quot; width=&quot;450&quot; height=&quot;179&quot; alt=&quot;0105_cache&quot; filename=&quot;0105_cache.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 450px; height: 179px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;프로세서 칩 내에 들어있는 &lt;strong&gt;L1 캐시&lt;/strong&gt;는 대략 수천 바이트의 데이터를 저장할 수 있으며, 거의 레지스터 파일만큼 빠른 속도로 엑세스 할 수 있다. 이보더 좀 더 큰 &lt;strong&gt;L2 캐시&lt;/strong&gt;는 수백 킬로바이트에서 수 메가 바이트의 용량을 가지며 프로세서와 전용 버스를 통해 연결된다. L1, L2 캐시는 &lt;strong&gt;SRAM(Static Random Access Memory)&lt;/strong&gt;이라는 하드웨어 기술을 이용해 구현한다.&lt;/p&gt;&lt;p&gt;위 내용의 가장 중요한 교훈 중 하나는 캐시 메모리를 이해하는 응용 프로그래머는 캐시를 활용하여 자신의 프로그램 성능을 개선할 수 있다는 것이다.&lt;/p&gt;

&lt;hr&gt;

&lt;h3 id=&quot;6&quot;&gt;6. 저장장치들의 계층구조&lt;/h3&gt;

&lt;p&gt;모든 컴퓨터 시스템의 저장장치들은 아래 그림과 같이 &lt;strong&gt;메모리 계층구조&lt;/strong&gt;로 구성되어 있다. 계층의 꼭대기에서부터 맨 밑바닥까지 이동할수록 저장장치들은 더 느리고, 더 크고, 바이트당 가격이 싸진다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 450px; width: 450px; height: 270px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99E2BD355C5EEED12F&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99E2BD355C5EEED12F&quot; width=&quot;450&quot; height=&quot;270&quot; alt=&quot;0106&quot; filename=&quot;0106_tour.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 450px; height: 270px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;메모리 계층구조의 주요 아이디어는 한 레벨의 저장장치가 다음 하위레벨 저장장치의 캐시 역할을 한다는 것이다. L1과 L2 캐시는 각각 L2와 L3의 캐시다. L3 캐시는 메인 메모리의 캐시이고, 메인 메모리는 디스크의 캐시 역할을 한다. 일부 분산 파일시스템을 가지는 네트워크 시스템에서 로컬 디스크는 다른 시스템의 디스크에 저장된 데이터의 캐시 역할을 수행한다.&lt;/p&gt;&lt;p&gt;프로그래머들이 성능을 개선하기 위해서 다른 종료의 캐시들을 활용할 수 있듯이 전체 메모리에 계층구조에 대한 지식을 활용할 수 있다.&lt;/p&gt;

&lt;hr&gt;

&lt;h3 id=&quot;7&quot;&gt;7. 운영체제&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;운영체제: 시스템 하드웨어를 관리할 뿐 아니라 응용 소프트웨어를 실행하기 위하여 하드웨어 추상화 플랫폼과 공통 시스템 서비스를 제공하는 시스템 소프트웨어 -위키백과-&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;쉘 프로그램이 hello 프로그램을 로드하고 실행했을 때와 메시지를 출력할 때 프로그램은 키보드나 디스플레이, 디스크나 메인 메모리를 직접 엑세스하지 않고 &lt;strong&gt;운영체제(Operation System)&lt;/strong&gt;가 제공하는 서비스를 활용한다. 운영체제는 아래 그림처럼 하드웨어와 소프트웨어 사이에 위치한 소프트웨어 계층으로 생각할 수 있다. 응용프로그램이 하드웨어를 제어하려면 언제나 운영체제를 통해서 해야 한다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 400px; width: 400px; height: 85px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/993A49385C5EEF0A0A&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F993A49385C5EEF0A0A&quot; width=&quot;400&quot; height=&quot;85&quot; alt=&quot;0107_1&quot; filename=&quot;0107_1.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 400px; height: 85px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;운영체제는 두 가지 주요 목적을 가지고 있다.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;제멋대로 동작하는 응용프로그램들이 하드웨어를 잘못 사용하는 것을 막는다.&lt;/li&gt;

&lt;li&gt;응용프로그램들이 단순하고 균일한 메커니즘을 사용하여 복잡하고 매우 다른 저수준 하드웨어 장치들을 조작할 수 있도록 한다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 400px; width: 400px; height: 166px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99B61C4F5C5EEF2316&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99B61C4F5C5EEF2316&quot; width=&quot;400&quot; height=&quot;166&quot; alt=&quot;0107_2&quot; filename=&quot;0107_2.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 400px; height: 166px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;운영체제는 이 두가지를 위 그림처럼 근본적인 추상화를 통해 달성하고 있다. &lt;strong&gt;프로세스&lt;/strong&gt;, &lt;strong&gt;가상메모리&lt;/strong&gt;, &lt;strong&gt;파일&lt;/strong&gt;. 파일은 입출력장치의 추상화고, 가상메모리는 메인 메모리와 디스크 입출력 장치의 추상화, 그리고 프로세스는 프로세서, 메인 메모리, 입출력장치 모두의 추상화 결과다.&lt;/p&gt;

&lt;h4 id=&quot;-5&quot;&gt;- 프로세스&lt;/h4&gt;

&lt;blockquote&gt;
  &lt;p&gt;프로세스는 컴퓨터에서 연속적으로 실행되고 있는 컴퓨터 프로그램을 말한다. -위키백과-&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;hello 같은 프로그램이 최신 시스템에서 실행될 때 운영체제는 시스템에서 이 한 개의 프로그램만 실행되는 것 같은 착각에 빠지도록 해준다. 한 프로그램이 프로세서, 메인 메모리, 입출력장치를 모두 독차지하고 있는 것처럼 보인다. 프로세서는 프로그램 내의 명령어들을 다른 방해 없이 순차적으로 실행하는 것처럼 보이고, 프로그램의 코드와 데이터가 시스템 메모리의 유일한 객체인 것처럼 보인다. 이러한 환상은 프로세스라고 하는 개념에 의해서 만들어진다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;프로세스&lt;/strong&gt;는 실행 중인 프로그램에 대한 운영체제의 추상화다. 다수의 프로세스들은 동일한 시스템에서 동시에 실행될 수 있다. &lt;strong&gt;동시에(concurrently)&lt;/strong&gt;라는 말은 한 프로세스의 명령어들이 다른 프로세스의 명령어와 섞인다는 것을 의미한다. 대부분 시스템에서 프로세스를 실행할 CPU의 숫자보다 더 많은 프로세스들이 존재한다. 이전에는 한 번에 한개의 프로그램만 실행할 수 있었지만, 요즘은 멀티코어 프로세서로 여러 개의 프로그램을 동시에 실행할 수 있다. 어느쪽이건 프로세서가 프로세스들을 바꿔주는 방식으로 한 개의 CPU가 다수의 프로세스를 동시에 실행하는 것처럼 보이게 해준다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;문맥 전환&lt;/strong&gt;(Context Switch)이란 하나의 프로세스가 CPU를 사용 중인 상태에서 다른 프로세스가 CPU를 사용하도록 하기 위해, 이전의 프로세스의 상태(문맥)를 보관하고 새로운 프로세스의 상태를 적재하는 작업을 말한다. 한 프로세스의 문맥은 그 프로세스의&amp;nbsp;프로세스 제어 블록에 기록되어 있다. -위키백과-&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;운영체제는 &lt;strong&gt;문맥 전환&lt;/strong&gt;이라는 방법을 사용해서 이러한 교차실행을 수행한다. 여기서는 &lt;strong&gt;단일 프로세서&lt;/strong&gt;시스템만 고려하겠다. 운영체제는 프로세스가 실행하는데 필요한 모든 상태정보의 변화를 추적한다. 이 &lt;strong&gt;컨텍스트(context)&lt;/strong&gt;라고 부르는 상태정보는PC, 레지스터 파일, 메인 메모리의 현재 값을 포함하고 있다. 운영체제는 현재 프로세스에서 다른 프로세스로 제어를 옮기려고 할 때 현재 프로세스의 컨텍스트를 저장하고 새 프로세스의 컨텍스트를 복원시키는 &lt;strong&gt;문맥 전환&lt;/strong&gt;을 실행하여 제어권을 새 프로세스로 넘겨준다. 새 프로세서는 이전에 중단했던 바로 그 위치부터 다시 실행된다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 550px; width: 550px; height: 189px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99F13B485C5EEF6109&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99F13B485C5EEF6109&quot; width=&quot;550&quot; height=&quot;189&quot; alt=&quot;0107_3&quot; filename=&quot;0107_3.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 550px; height: 189px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;위 그림의 예제01에는 두개의 동시성 프로세스가 존재한다.(쉘 프로세스와 hello 프로세스) 처음에는 쉘 프로세스가 혼자서 동작하고 있다가 명령줄에서 입력을 기다린다. hello 프로그램을 실행하라는 명령을 받으면, 쉘은 &lt;strong&gt;시스템 콜&lt;/strong&gt;이라는 특수 함수를 호출하여 운영체제로 제어권을 넘겨준다. 운영체제는 쉘의 컨텍스트를 저장하고 새로운 hello 프로세스와 컨텍스트를 생성한 뒤 제어권을 새 hello 프로세스로 넘겨준다. hello 프로세스가 종료되면 쉘은 컨텍스트를 복구시키고 제어권을 넘겨주면서 다음 명령 줄 입력을 기다린다.&lt;/p&gt;&lt;p&gt;하나의 프로세스에서 다른 프로세스로의 전환은 운영체제 &lt;strong&gt;커널(kernel)&lt;/strong&gt;에 의해 관리된다. 커널은 운영체제 코드의  일부분으로 메로리에 상주한다. 응용 프로그램이 운영체제에 어떤 작업을 요청하면, 컴퓨터는 파일 읽기나 쓰기와 같은 특정 &lt;strong&gt;시스템 콜(system call)&lt;/strong&gt;을 실행해서 커널에 제어를 넘겨준다. 그러면 커널은 요청된 작업을 수행하고 응용프로그램으로 리턴한다. 커널은 별도의 프로세스가 아니라 모든 프로세스를 관리하기 위해 시스템이 이용하는 코드와 자료구조의 집합이다.&lt;/p&gt;

&lt;h4 id=&quot;-6&quot;&gt;- 쓰레드&lt;/h4&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;스레드&lt;/strong&gt;(thread)는 어떠한 프로그램 내에서, 특히&amp;nbsp;프로세스에서 실행되는 흐름의 단위를 말한다. 일반적으로 한 프로그램은 하나의 스레드를 가지고 있지만, 프로그램 환경에 따라 둘 이상의 스레드를 동시에 실행할 수 있다. 이러한 실행 방식을&amp;nbsp;멀티스레드(multithread)라고 한다. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;프로세스가 마치 한 개의 제어흐름을 갖는 것으로 생각할 수 있지만, 최근 시스템에서는 프로세스가 실제로 &lt;strong&gt;쓰레드(Thread)&lt;/strong&gt;라고 하는 다수의 실행 유닛으로 구성되어 있다. 각각의 쓰레드는 해당 프로세스의 컨텍스트에서 실행되며 동일한 코드와 전역 데이터를 공유한다. 쓰레드는 다수의 프로세스보다 데이터의 공유가 더 쉽다는 점과 프로세스보다 더 효율적이기 때문에 프로그래밍 모델에서 중요성이 크다. 쓰레드에 관한 내용은 다음에 좀 더 알아보겠다.&lt;/p&gt;

&lt;h4 id=&quot;-7&quot;&gt;- 가상메모리&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;가상메모리&lt;/strong&gt;는 각 프로세스들이 메인 메모리 전체를 독점적으로 사용하고 있는 것 같은 환상을 제공하는 추상화다. 각 프로세스는 아래 그림처럼 &lt;strong&gt;가상주소 공간&lt;/strong&gt;이라고 하는 균일한 메모리의 모습을 갖게된다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 500px; width: 500px; height: 392px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99ED56465C5EEF8017&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99ED56465C5EEF8017&quot; width=&quot;500&quot; height=&quot;392&quot; alt=&quot;0107_virtualMemory&quot; filename=&quot;0107_virtualMemory.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 500px; height: 392px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;리눅스에서, 주소곤강의 최상위 영역은 모든 프로세스들이 공통으로 사용하는 운영체제의 코드와 데이터를 위한 공간이다. 주소공간의 하위 여역은 사용자 프로세스의 코드와 데이터를 저장한다. (위 그림에서 위쪽으로 갈수록 주소가 증가한다.)&lt;/p&gt;&lt;p&gt;각 프로세스들에게 보여지는 가장주소 공간은 몇 개의 정의된 영역으로 구성되어 있다. 낮은 주소부터 위로 올라가면서 간단히 살펴보자.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;프로그램 코드와 데이터&lt;/strong&gt; : 코드는 모든 프로세스들이 같은 고정 주소에서 시작하며, 다음에 C 전역변수에 대응되는 데이터 위치들이 따라온다. 코드와 데이터 영역은 실행 가능 목적 파일인 hello로부터 직접 초기화된다.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;힙(Heap)&amp;nbsp;&lt;/b&gt;: 코드와 데이터 영역 다음으로 런타입&lt;b style=&quot;font-style: italic;&quot;&gt;&amp;nbsp;&lt;/b&gt;&lt;b&gt;힙&lt;/b&gt;이 따라온다. 크기가 고정되어 있는 코드, 데이터 영역과 달리, 힙은 프로세스가 실행되면서 C 표준함수인 malloc이나 free를 호출하면서 런타에 동적으로 크기가 늘었다 줄었다 한다.&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;공유 라이브러리&lt;/strong&gt; : 주소공간의 중간 부근에 C 표준 라이브러리나 수학 라이브러리와 같은 공유 라이브러리와 같은 공유 라리브러리의 코드와 데이터를 저장하는 영역이 있다.&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;스택(Stack)&lt;/strong&gt; : 사용자 가상메모리 공간의 맨 위에 컴파일러가 함수 호출을 구현하기 위해 사용하는 사용자 &lt;strong&gt;스택&lt;/strong&gt;이 위치한다. 힙과 마찬가지로 사용자 스택은 프로그램이 실행되는 동안에 동적으로 늘었다 줄어들었다 한다. 특히, 함수를 호출할 때마다 스택이 커지며, 함수에서 리턴될 때는 줄어든다.&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;커널 가상메모리&lt;/strong&gt; : 주소공간의 맨 윗부분은 커널을 위해 예약되어 있다. 응용프로그램들은 이 영역의 내용을 읽거나 쓰는 것이 금지되어 있으며, 마찬가지로 커널 코드 내에 정의도니 함수를 직접 호출되는 것도 금지되어 이다. 대신, 이런 작업을 수행하기 위해서는 커널을 호출해야 한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;가상메모리가 작동하기 위해서는 프로세서가 만들어내는 모든 주소를 하드웨어로 번역하는 등의 하드웨어와 운영체제 소프트웨어 간의 복잡한 상호작용이 필요하다. 기본적인 아이디어는 프로세스의 가상메모리 내용을 디스크에 저장하고 메인 메모리를 디스크의 캐시로 사용하는 것이다.&lt;/p&gt;

&lt;h4 id=&quot;-8&quot;&gt;- 파일&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;파일&lt;/strong&gt;은 더도 덜도 말고 그저 연속된 바이트들이다. 디스크, 키보드, 디스플레이, 네트워크까지 포함하는 모든 입출력장치는 파일로 모델링한다. 시스템의 모든 입출력은 &lt;strong&gt;유닉스 I/O&lt;/strong&gt;라는 시스템 콜들을 이용하여 파일을 읽고 쓰는 형태로 이루어진다. 이 파일 개념은 매우 강력해서 응용프로그램에 시스템에 들어있는 다양한 입출력장치들의 통일된 관점을 제공한다. 예를 들어 동일한 프로그램이 다른 디스크 기술을 사용하는 다른 시스템에서도 실행될 수 있다.&lt;/p&gt;

&lt;hr&gt;

&lt;h3 id=&quot;8&quot;&gt;8. 네트워크&lt;/h3&gt;

&lt;p&gt;시스템은 네트워크를 사용하여 다른 시스템과 통신한다. 이러한 관점에서 볼 때, 네트워크는 아래 그림처럼 단지 또 다른 입출력장치로 볼 수 있다.시스템이 메인 메모리로부터 네트워크 어댑ㅓ로 일련의 바이트를 복사할 때, 데이터는 로컬디스크 드라이브 대신에 네트워크를 통해서 다른 컴퓨터로 이동된다. 마찬가지로 시스템은 다른 컴퓨터로부터 받은 데이터를 읽어서 메인 메모리에 복사할 수 있다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 400px; width: 400px; height: 310px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99BC4F435C5EEFA510&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99BC4F435C5EEFA510&quot; width=&quot;400&quot; height=&quot;310&quot; alt=&quot;0108_1&quot; filename=&quot;0108_1.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 400px; height: 310px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;hello 예제로 돌아가서, telnet 응용을 사용하여 hello 프로그램을 다른 곳에 위치한 컴퓨터에서 실행할 수 있다. 다음은 telnet 클라이언트를 사용하여 로컬 컴퓨터를 원격 컴퓨터의 telnet 서버와 연결한 후, 네트워크를 거쳐 &quot;hello, word\n&quot; 문자열을 출력하기 까지의 과정을 보여준다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 550px; width: 550px; height: 114px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/996F18465C5EEFC540&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F996F18465C5EEFC540&quot; width=&quot;550&quot; height=&quot;114&quot; alt=&quot;0108_2&quot; filename=&quot;0108_2.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 550px; height: 114px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;위 그림처럼 이러한 유형의 클라이언트와 서버 간의 데이터 교환은 모든 네트워크 응용의 전형적인 사례이다.&lt;/p&gt;

&lt;hr&gt;

&lt;h3 id=&quot;-9&quot;&gt;요약&lt;/h3&gt;

&lt;p&gt;컴퓨터 시스템은 응용프로그램을 실행하기 위해 함께 동작하는 하드웨어와 시스템 소프트웨어로 구성된다. 컴퓨터 내의 정보는 상황에 따라 다르게 해석되는 비트들의 그룹으로 표시된다. 프로그램은 ASCII 문자로 시작해서 컴파일러와 링커에 의해 바이너리 실행파일들로 번역되는 방식으로, 다른 프로그램들에 의해 다른 형태로 번역된다.&lt;/p&gt;&lt;p&gt;프로세서는 메인 메모리에 저장된 바이너리 인스트럭션을 읽고 해석한다. 컴퓨터가 대부분 시간을 메모리, 입출력장치, CPU 레지스터 간에 데이터를 복사하는 데 쓰고 있으므로 시스템의 저장장치들은 계층구조를 형성하여 최상위 부터 CPU 레지스터, 하드웨어 캐시 메모리, DRAM 메인 메모리, 디스크 저장장치 등이 순차적으로 위치한다. 계층 구조가의 상부일 수록 비트당 단가가 더 비싸고, 더 빠르다.&lt;/p&gt;&lt;p&gt;계층구조 상부의 저장장치들은 하부의 장치들을 위한 캐시 역할을 수행한다. 프로그래머들은 이러한 메모리 계층구조를 이해하고 활용해서 자신이 작성한 C 프로그램의 성능을 최적화할 수 있다.&lt;/p&gt;&lt;p&gt;운영체제 커널은 응용프로그램과 하드웨어 사이에서 중간자의 역할을 수행한다. 운영체제는 세 가지 근본적인 추상화를 제공한다. (1) 파일을 입출력장치의 추상화, (2) 가상메모리는 메인 메모리와 디스크의 추상화, (3) 프로세스는 프로세서, 메인 메모리, 입출력 장치의 추상화다.&lt;/p&gt;&lt;p&gt;마지막으로 네트워크는 컴퓨터 시스템이 서로 통신할 수 있는 방법을 제공한다. 특정 시스템의 관점으로 볼 때, 네트워크는 단지 또 하나의 입출력장치다.&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 64px; width: 64px; height: 64px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/992AE44F5C5EF14D15&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F992AE44F5C5EF14D15&quot; width=&quot;64&quot; height=&quot;64&quot; alt=&quot;pc&quot; filename=&quot;monitor.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 64px; height: 64px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>컴구 이야기</category>
      <category>컴퓨터시스템</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/232</guid>
      <comments>https://boycoding.tistory.com/232#entry232comment</comments>
      <pubDate>Sun, 10 Feb 2019 00:25:52 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 가운데 글자 가져오기, 짝수 홀수 판별 알고리즘</title>
      <link>https://boycoding.tistory.com/231</link>
      <description>&lt;h1 style=&quot;text-align: left; clear: none; float: none; margin-bottom: 15px; font-size: 30px;&quot; id=&quot;h1h1&quot;&gt;가운데 글자 가져오기&lt;/h1&gt;

&lt;blockquote&gt;
  &lt;p&gt;단어 s의 가운데 글자를 반환하는 함수, solution을 만들어 보세요. 단어의 길이가 짝수라면 가운데 두글자를 반환하면 됩니다.&lt;/p&gt;
  
  &lt;h6 id=&quot;&quot;&gt;재한사항&lt;/h6&gt;
  
  &lt;ul&gt;
  &lt;li&gt;s는 길이가 1 이상, 100이하인 스트링입니다&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;단어의 길이가 짝수인지 홀수인지 판별한 후 가운데 글자를 반환하면 된다.&lt;/p&gt;

&lt;h2 id=&quot;-2&quot;&gt;짝수 홀수 판별 알고리즘&lt;/h2&gt;

&lt;p&gt;정수가 짝수인지 홀수인지 판별하는 법은 쉽다. 2로 나눠서 나머지가 0이면 짝수고, 아니면 홀수다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;// 1. 조건문
if (number % 2 == 0) // 짝수
{

}
else                 // 홀수
{

}

// 2. 함수
bool IsEvenNumber(int number)
{
    return number % 2 == 0; // 2로 나누어 떨어지면 짝수다.
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;비트 연산으로 판별하는 방법도 있다. 홀수일 경우 이진법에서 제일 마지막 수는 1이다. 비트 AND 연산자(&lt;code&gt;&amp;amp;&lt;/code&gt;)를 이용해서 알 수 있다. 비트 연산에 대한 더 자세한 내용을 알고 싶다면 아래 포스트들을 참고하면 된다.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/163&quot; target=&quot;_blank&quot;&gt;2018/06/27 - C++ 03.07 - 비트 단위 연산자 (Bitwise operators)&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/164&quot; target=&quot;_blank&quot;&gt;2018/06/27 - C++ 03.08 - 비트 플래그와 비트 마스크 (Bit flags and bit masks)&lt;/a&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;bool IsEvenNumber(int number)
{
    // 1 &amp;amp; 1: 0001 &amp;amp; 0001 = 1
    // 2 &amp;amp; 1: 0010 &amp;amp; 0001 = 0
    // 3 &amp;amp; 1: 0011 &amp;amp; 0001 = 1
    // 4 &amp;amp; 1: 0100 &amp;amp; 0001 = 0
    return number &amp;amp; 1 == 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;

&lt;h2 id=&quot;-3&quot;&gt;문자열 자르기&lt;/h2&gt;

&lt;p&gt;C++의 string에는 문자열 일부를 반환하는 &lt;code&gt;substr&lt;/code&gt; 메서드가 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;string substr (size_t pos = 0, size_t len = npos) const;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;문자열의 &lt;code&gt;pos&lt;/code&gt;부터 &lt;code&gt;len&lt;/code&gt; 길이 만큼의 문자열을 반환한다. 만약 &lt;code&gt;len&lt;/code&gt;의 값이 문자열의 길이보다 길거나, &lt;code&gt;string::npos&lt;/code&gt;를 전달하면 문자열의 끝까지만 반환한다. 이 메서드를 이용해서 '가운데 글자 가져오기' 문제를 해결해보자.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;

using namespace std;

string solution(string s)
{
    int length = s.length();

    if (length &amp;amp; 1 == 1) // 홀수
    {
        return s.substr(length / 2, 1);
    }
    else 
    {
        return s.substr(length / 2 - 1, 2);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 100px; width: 100px; height: 100px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99C96A3E5C22F48905&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99C96A3E5C22F48905&quot; width=&quot;100&quot; height=&quot;100&quot; alt=&quot;text&quot; filename=&quot;text.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 100px; height: 100px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/225&quot; target=&quot;_blank&quot;&gt;2018/12/19 - [프로그래머스] 소수의 합, 소수 판별 알고리즘&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/226&quot; target=&quot;_blank&quot;&gt;2018/12/21 - [프로그래머스] 완주하지 못한 선수, map과 unordered_map의 차이&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/227&quot; target=&quot;_blank&quot;&gt;2018/12/23 - [프로그래머스] N으로 표현&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/228&quot; target=&quot;_blank&quot;&gt;2018/12/23 - [프로그래머스] 모의고사&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/229&quot; target=&quot;_blank&quot;&gt;2018/12/23 - [프로그래머스] K번째수, vector.assign()&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/230&quot; target=&quot;_blank&quot;&gt;2018/12/25 - [프로그래머스] 체육복, 벡터 삭제 시 주의점 (iterator, erase)&lt;/a&gt;&lt;/p&gt;</description>
      <category>프로그래머스/Level 1</category>
      <category>Level 1</category>
      <category>프로그래머스</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/231</guid>
      <comments>https://boycoding.tistory.com/231#entry231comment</comments>
      <pubDate>Wed, 26 Dec 2018 12:25:42 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 체육복, 벡터 삭제 시 주의점 (iterator, erase)</title>
      <link>https://boycoding.tistory.com/230</link>
      <description>&lt;h1 style=&quot;text-align: left; clear: none; float: none; margin-bottom: 15px; font-size: 30px;&quot; id=&quot;h1h1&quot;&gt;체육복&lt;/h1&gt;

&lt;blockquote&gt;
  &lt;p&gt;오늘은 체육수업이 있는 날입니다. 그런데 점심시간에 도둑이 들어 몇몇 학생의 체육복이 도난을 당했습니다. 다행히 일부 학생들이 여벌의 체육복을 가져왔습니다. 학생들의 번호는 체격 순으로 매겨져 있기 때문에 바로 앞번호의 학생이나 바로 뒷번호의 학생에게만 체육복을 빌려주려고 합니다.&lt;/p&gt;
  
  &lt;p&gt;예를 들어, 4번 학생은 3번 학생이나 5번 학생에게만 체육복을 빌려줄 수 있습니다. 당연히 체육복을 2벌 가져온 학생의 체육복이 도난을 당했다면, 여벌의 체육복을 빌려줄 수 없습니다.&lt;/p&gt;
  
  &lt;p&gt;체육복이 없으면 체육수업을 들을 수 없기 때문에 체육복을 적절히 빌려 최대한 많은 학생이 체육수업을 듣고 싶습니다.&lt;/p&gt;
  
  &lt;p&gt;전체 학생의 수 n, 체육복을 도난당한 학생들의 번호가 담긴 배열 lost, 여벌의 체육복을 가져온 학생들의 번호가 담긴 배열 reserve가 매개변수로 주어질 때, 체육수업을 들을 수 있는 학생의 최댓값을 return 하도록 solution 함수를 작성해주세요.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;이 문제는 문제 설명이 문제를 더 어렵게 만든다. 핵심만 말하자면  &lt;code&gt;lost&lt;/code&gt; 배열의 값을 &lt;code&gt;reserve&lt;/code&gt; 배열의 값과 잘 비교해서 지워나가면 된다. 비교해서 지워 나간 뒤  &lt;code&gt;lost&lt;/code&gt; 배열 남아있는 학생이 체육복이 없는 학생이므로 체육수업을 들을 수 있는 학생의 수는 &lt;code&gt;n - lost.size()&lt;/code&gt;가 된다.&lt;/p&gt;&lt;p&gt;체육수업을 들을 수 있는 학생의 최댓값을 구하라고 해서 괜히 어려워 보이기도 한다. n번째 학생에게 체육복을 빌려줄 수 있는 학생은 n-1, n, n+1 학생이다. 지금 당장 노트를 펴서 연필로 간단히 숫자를 나열하기만 해도 n번째 학생에게는 n-1학생이 체육복을 빌려주는 게 제일 좋다는 것을 알 수 있다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot; vectoreraseiterator&quot; =&quot;&quot;=&quot;&quot;&gt;vector::erase(), iterator 와 주의점&lt;/h2&gt;

&lt;p&gt;벡터에는 특정 위치의 요소를 제거하고, 제거된 요소 수만큼 크기가 줄어드는 효율적인 &lt;code&gt;erase()&lt;/code&gt; 메서드가 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;iterator erase (const_iterator position);
iterator erase (const_iterator first, const_iterator last);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;첫 번째 메서드의 경우 &lt;code&gt;position&lt;/code&gt; 위치의 원소를 제거하고, 두 번째 메서드는  &lt;code&gt;first&lt;/code&gt;부터 &lt;code&gt;last&lt;/code&gt; 이전까지의 모든 원소를 제거한다. [&lt;code&gt;first&lt;/code&gt;, &lt;code&gt;last&lt;/code&gt;) 즉, 해당 위치의 원소를 제거하고 그 뒤에 있는 원소들을 남은 자리만큼 앞으로 이동시킨다. 이 특징은 반복문에서 iterator가 원하는 인덱스를 건너띄어 엉뚱한 곳을 참조할 수 있으므로 주의해야 한다.&lt;/p&gt;&lt;p&gt;반환 값인 &lt;code&gt;iterator&lt;/code&gt;는 &lt;code&gt;erase()&lt;/code&gt; 함수 호출에 의해 지워지는 요소의 다음 위치다. 이 반환 값을 이용해서 다음과 같이 벡터 A에서 값이 2인 요소를 모두 지우는 반복문을 프로그래밍할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;std::vector A {1, 2, 2, 3, 4, 5};

// 벡터 A에서 값이 2인 요소를 모두 지우는 반복문
for (auto it = A.begin(); it != A.end(); )
{
    if (*it == 2)
    {
        // A.erase(it);
        // ++it -&amp;gt; 잘못된 방법! (다음 위치를 건너띄게 된다.)

        it = A.erase(it);
    }
    else
    {
        ++it;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;

&lt;p&gt;아래는 '체육복' 문제의 풀이다. &lt;em&gt;isErase&lt;/em&gt; 플래그를 이용해서 &lt;em&gt;++it&lt;/em&gt; 사용에 조건을 두었다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int solution(int n, vector&amp;lt;int&amp;gt; lost, vector&amp;lt;int&amp;gt; reserve) 
{    
    int num, number;
    bool isErase;
    for (auto it = lost.begin(); it != lost.end();)
    {
        num = *it;
        isErase = false;
        for (auto itt = reserve.begin(); itt != reserve.end(); ++itt)
        {
            number = *itt;
            if (num - 1 == number || num == number || num + 1 == number)
            {
                it = lost.erase(it);
                reserve.erase(itt);
                isErase = true;
                break;
            }
        }

        if (isErase == false) ++it;
    }

    return n - lost.size();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 100px; width: 100px; height: 100px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/992B1D375C2105C82D&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F992B1D375C2105C82D&quot; width=&quot;100&quot; height=&quot;100&quot; alt=&quot;man&quot; filename=&quot;man.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 100px; height: 100px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/225&quot; target=&quot;_blank&quot;&gt;2018/12/19 - [프로그래머스] 소수의 합, 소수 판별 알고리즘&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/226&quot; target=&quot;_blank&quot;&gt;2018/12/21 - [프로그래머스] 완주하지 못한 선수&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/227&quot; target=&quot;_blank&quot;&gt;2018/12/23 - [프로그래머스] N으로 표현&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/228&quot; target=&quot;_blank&quot;&gt;2018/12/23 - [프로그래머스] 모의고사&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/229&quot; target=&quot;_blank&quot;&gt;2018/12/23 - [프로그래머스] K번째수&lt;/a&gt;&lt;/p&gt;</description>
      <category>프로그래머스/Level 1</category>
      <category>Level 1</category>
      <category>프로그래머스</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/230</guid>
      <comments>https://boycoding.tistory.com/230#entry230comment</comments>
      <pubDate>Tue, 25 Dec 2018 01:15:47 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] K번째수, vector.assign()</title>
      <link>https://boycoding.tistory.com/229</link>
      <description>&lt;h1 style=&quot;text-align: left; clear: none; float: none; margin-bottom: 15px; font-size: 30px;&quot; id=&quot;h1h1&quot;&gt;K번째수&lt;/h1&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;배열 array의 i번째 숫자부터 j번째 숫자까지 자르고 정렬했을 때, k번째에 있는 수를 구하려 합니다.

예를 들어 array가 [1, 5, 2, 6, 3, 7, 4], i = 2, j = 5, k = 3이라면

array의 2번째부터 5번째까지 자르면 [5, 2, 6, 3]입니다.
1에서 나온 배열을 정렬하면 [2, 3, 5, 6]입니다.
2에서 나온 배열의 3번째 숫자는 5입니다.
배열 array, [i, j, k]를 원소로 가진 2차원 배열 commands가 매개변수로 주어질 때, commands의 모든 원소에 대해 앞서 설명한 연산을 적용했을 때 나온 결과를 배열에 담아 return 하도록 solution 함수를 작성해주세요.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;쉽게 눈치챌 수 있는 함정이 있다. 저기서 &lt;em&gt;i&lt;/em&gt;와 &lt;em&gt;j&lt;/em&gt;는 0부터 시작하는 일반적인 프로그래밍 언어의 배열 인덱스가 아니다. 배열의 인덱스 0을 1로 표현하고 있다. 그러므로 정확히는 &lt;em&gt;i-1&lt;/em&gt; 번째부터 &lt;em&gt;j&lt;/em&gt; 번째 인덱스 까지다. 반환하는 &lt;em&gt;k&lt;/em&gt; 번째 역시 배열에서는 &lt;em&gt;k-1&lt;/em&gt;번째 인덱스가 된다. &lt;/p&gt;

&lt;hr&gt;&lt;h2 id=&quot;vectorassign&quot;&gt;vector::assign()&lt;/h2&gt;

&lt;p&gt;반복문을 이용해서 &lt;em&gt;i&lt;/em&gt;부터 &lt;em&gt;j&lt;/em&gt;까지의 값을 가져올 수 있겠지만 &lt;code&gt;C++ STL&lt;/code&gt;에서는 좀 더 편한 메서드를 제공한다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;template &amp;lt;class InputIterator&amp;gt;
void assign ( InputIterator first, InputIterator last );
void assign ( size_type n, const T&amp;amp; u );
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;첫 번째 메서드의 경우 &lt;code&gt;first&lt;/code&gt;부터 &lt;code&gt;last 이전&lt;/code&gt;까지의 모든 원소의 내용이 들어가게 된다. 두 번째 메서드는 원소 &lt;code&gt;u&lt;/code&gt;를 &lt;code&gt;n&lt;/code&gt;개 가지는 벡터를 만든다.&lt;/p&gt;&lt;p&gt;&lt;code&gt;vector.assign()&lt;/code&gt; 메서드를 이용해서 &lt;em&gt;i-1&lt;/em&gt;부터 &lt;em&gt;j&lt;/em&gt;까지 쉽게 할당할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;vector&amp;lt;int&amp;gt; a { 1, 2, 3, 4, 5, 6, 7, 8, 9};
vector&amp;lt;int&amp;gt; b;
int i = 2, j = 5, k = 3;
b.assign(a.begin() + i - 1, a.begin() + j); // {2, 3, 4, 5, 6}
return b[k-1];
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 내용을 적용해서 반복문으로 풀은 내용은 아래와 같다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;vector&amp;lt;int&amp;gt; solution(vector&amp;lt;int&amp;gt; array, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; commands) 
{
    vector&amp;lt;int&amp;gt; answer;

    vector&amp;lt;int&amp;gt; temps;
    for (vector&amp;lt;int&amp;gt;&amp;amp; arr : commands)
    {
        temps.clear();

        temps.assign(array.begin() + arr[0] - 1, array.begin() + arr[1]);
        std::sort(temps.begin(), temps.end());

        answer.push_back(temps[arr[2] - 1]);
    }

    return answer;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 80px; width: 80px; height: 80px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99D0E8395C1E835A07&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99D0E8395C1E835A07&quot; width=&quot;80&quot; height=&quot;80&quot; filename=&quot;letter-k.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 80px; height: 80px;&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/225&quot; target=&quot;_blank&quot;&gt;2018/12/19 - [프로그래머스] 소수의 합, 소수 판별 알고리즘&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/226&quot; target=&quot;_blank&quot;&gt;2018/12/21 - [프로그래머스] 완주하지 못한 선수&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/227&quot; target=&quot;_blank&quot;&gt;2018/12/23 - [프로그래머스] N으로 표현&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/228&quot; target=&quot;_blank&quot;&gt;2018/12/23 - [프로그래머스] 모의고사&lt;/a&gt;&lt;/p&gt;</description>
      <category>프로그래머스/Level 1</category>
      <category>Level 1</category>
      <category>프로그래머스</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/229</guid>
      <comments>https://boycoding.tistory.com/229#entry229comment</comments>
      <pubDate>Sun, 23 Dec 2018 03:34:53 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 모의고사</title>
      <link>https://boycoding.tistory.com/228</link>
      <description>&lt;h1 style=&quot;text-align: left; clear: none; float: none; margin-bottom: 15px; font-size: 30px;&quot; id=&quot;h1h1&quot;&gt;모의고사&lt;/h1&gt;

&lt;blockquote&gt;
  &lt;p&gt;수포자는 수학을 포기한 사람의 준말입니다. 수포자 삼인방은 모의고사에 수학 문제를 전부 찍으려 합니다. 수포자는 1번 문제부터 마지막 문제까지 다음과 같이 찍습니다.&lt;/p&gt;
  
  &lt;p&gt;1번 수포자가 찍는 방식: 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ...
  2번 수포자가 찍는 방식: 2, 1, 2, 3, 2, 4, 2, 5, 2, 1, 2, 3, 2, 4, 2, 5, ...
  3번 수포자가 찍는 방식: 3, 3, 1, 1, 2, 2, 4, 4, 5, 5, 3, 3, 1, 1, 2, 2, 4, 4, 5, 5, ...&lt;/p&gt;
  
  &lt;p&gt;1번 문제부터 마지막 문제까지의 정답이 순서대로 들은 배열 answers가 주어졌을 때, 가장 많은 문제를 맞힌 사람이 누구인지 배열에 담아 return 하도록 solution 함수를 작성해주세요.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;반복문과 조건문 및 나머지 연산만 이해했으면 풀 수 있는 매우 쉬운 문제다.&lt;/p&gt;&lt;p&gt;프로그래밍 언어에서 나머지 연산의 결과값은 0 ~ N-1이다. 이 성질을 이용해서 반복되는 구간의 인덱스를 구할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;vector&amp;gt;
#include &amp;lt;cmath&amp;gt;
using namespace std;


vector&amp;lt;int&amp;gt; solution(vector&amp;lt;int&amp;gt; answers)
{
    vector&amp;lt;int&amp;gt; answer;

    vector&amp;lt;int&amp;gt; answer1 {1, 2, 3, 4, 5};
    vector&amp;lt;int&amp;gt; answer2 {2, 1, 2, 3, 2, 4, 2, 5};
    vector&amp;lt;int&amp;gt; answer3 {3, 3, 1, 1, 2, 2, 4, 4, 5, 5};

    int count1 = 0;
    int count2 = 0;
    int count3 = 0;

    int size = answers.size();
    for(int i = 0; i &amp;lt; size; ++i)
    {
        int i1 = i % 5;  // 0 ~ 4
        int i2 = i % 8;  // 0 ~ 7
        int i3 = i % 10; // 0 ~ 9

        if(answers[i] == answer1[i1]) count1++;
        if(answers[i] == answer2[i2]) count2++;
        if(answers[i] == answer3[i3]) count3++;
    }

    int maxCount = max(count1, max(count2, count3));

    if(maxCount == count1) answer.push_back(1);
    if(maxCount == count2) answer.push_back(2);
    if(maxCount == count3) answer.push_back(3);

    return answer;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 100px; width: 100px; height: 100px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99A3C7445C1E778704&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99A3C7445C1E778704&quot; width=&quot;100&quot; height=&quot;100&quot; filename=&quot;exam.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 100px; height: 100px;&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/225&quot; target=&quot;_blank&quot;&gt;2018/12/19 - [프로그래머스] 소수의 합, 소수 판별 알고리즘&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/226&quot; target=&quot;_blank&quot;&gt;2018/12/21 - [프로그래머스] 완주하지 못한 선수&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/227&quot; target=&quot;_blank&quot;&gt;2018/12/23 - [프로그래머스] N으로 표현&lt;/a&gt;&lt;/p&gt;</description>
      <category>프로그래머스/Level 1</category>
      <category>Level 1</category>
      <category>프로그래머스</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/228</guid>
      <comments>https://boycoding.tistory.com/228#entry228comment</comments>
      <pubDate>Sun, 23 Dec 2018 02:43:27 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] N으로 표현</title>
      <link>https://boycoding.tistory.com/227</link>
      <description>&lt;h1 style=&quot;text-align: left; clear: none; float: none; margin-bottom: 15px; font-size: 30px;&quot; id=&quot;h1h1&quot;&gt;N으로 표현&lt;/h1&gt;

&lt;blockquote&gt;
  &lt;p&gt;아래와 같이 5와 사칙연산만으로 12를 표현할 수 있습니다.&lt;/p&gt;
  
  &lt;p&gt;12 = 5 + 5 + (5 / 5) + (5 / 5)
  12 = 55 / 5 + 5 / 5
  12 = (55 + 5) / 5&lt;/p&gt;
  
  &lt;p&gt;5를 사용한 횟수는 각각 6,5,4 입니다. 그리고 이중 가장 작은 경우는 4입니다.
  이처럼 숫자 N과 number가 주어질 때, N과 사칙연산만 사용해서 표현 할 수 있는 방법 중 N 사용횟수의 최솟값을 return 하도록 solution 함수를 작성하세요.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;프로그래머스 알고리즘 1단계에서 꽤 어려운 문제인가보다. 이 문제를 푸는 시점에&amp;nbsp;통과한 사람이 채 100명이 되지 않는다. 99번째로 통과했다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 450px; width: 450px; height: 140px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/990B273E5C1E6EF312&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F990B273E5C1E6EF312&quot; width=&quot;450&quot; height=&quot;140&quot; filename=&quot;n1.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 450px; height: 140px;&quot;/&gt;&lt;/span&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;&quot;&gt;완전 탐색, 무식하게 풀어보자. (+ 재귀)&lt;/h2&gt;

&lt;p&gt;'무식하게 푼다(brute-force)'라는 말은 가능한 경우의 수를 일일이 나열하면서 답을 찾는 방법을 의미한다. 이렇게 가능한 방법을 전부&amp;nbsp;만들어 보는 알고리즘들을 가리켜 흔히 '완전 탐색'이라고 부른다.&lt;/p&gt;&lt;p&gt;이 문제를 풀 수 있는 가장 쉬운 방법은 N, NN, NNN, ... , NNNNNNNN과 사칙 연산 +, - *, /를 이용한 경우의 수 중에 가장 N을 적게 사용한 경우를 return 하면 된다.&lt;/p&gt;&lt;p&gt;완전 탐색을 구현할 때 유용하게 사용되는 개념으로 '재귀 함수(recursive function)'가 있다. 재귀 함수란 자신이 수행할 작업을 유사한 형태의 여러 조각으로 쪼갠 두 그중 한 조각을 수행하고, 나머지를 자기 자신을 호출해 실행하는 함수를 말한다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cmath&amp;gt;

using namespace std;

vector&amp;lt;int&amp;gt; list;
int count = 0;
int minCount = 9;

// 완전 탐색 알고리즘 (Brute-Force Search)
void BFS(int N, int number)
{
    if(count &amp;gt;= minCount) return;
    if(list.size() &amp;gt;= minCount) return;
    int lastNumber = (list.size() == 0) ? 0 : list.back();

    if(lastNumber == number)
    {
        minCount = min(count, minCount);
        return;
    }

    int n = 0;
    int addCount = 0;

    for(int c = 1; c &amp;lt;= 10000000; c *= 10) 
    {
        addCount++;
        if(count + addCount &amp;gt;= minCount) continue;

        n += (N * c);

        count += addCount;

        list.push_back(lastNumber + n);
        BFS(N, number);
        list.pop_back();

        if(lastNumber - n != 0)
        {
            list.push_back(lastNumber - n);
            BFS(N, number);
            list.pop_back();
        }

        list.push_back(lastNumber * n);
        BFS(N, number);
        list.pop_back();

        if(lastNumber / n != 0)
        {
            list.push_back(lastNumber / n);
            BFS(N, number);
            list.pop_back(); 
        }

        count -= addCount;
    }

    return;
}

int solution(int N, int number) 
{    
    BFS(N, number);

    return minCount &amp;lt; 9 ? minCount : -1;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 코드는 N부터 NNNNNNNNN까지 반복문을 돌며, 벡터에 +, -, * , / 연산의 결과값을 push, pop 하며 &lt;em&gt;BFS&lt;/em&gt; 함수를 재귀 호출한다. 벡터의 마지막 값이 &lt;em&gt;number&lt;/em&gt;이면 재귀를 끝낸다. 완전 탐색은 기본적으로 모든 경우의 수를 검사한다. 하지만 위 코드는 최소 사용횟수(&lt;code&gt;minCount&lt;/code&gt;)를 비교하는 탈출 조건을 추가함으로써 연산량을 줄였다.&lt;/p&gt;&lt;p&gt;이 문제는 또 특이하게 효율성 검사를 하지 않는다. 위 코드의 결과는 아래와 같다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 600px; width: 600px; height: 228px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/994AB5365C1E6F4B0A&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F994AB5365C1E6F4B0A&quot; width=&quot;600&quot; height=&quot;228&quot; alt=&quot;nq1&quot; filename=&quot;nq1.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 600px; height: 228px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 100px; width: 100px; height: 100px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9921E2475C1E6FCE37&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9921E2475C1E6FCE37&quot; width=&quot;100&quot; height=&quot;100&quot; filename=&quot;letter-n.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 100px; height: 100px;&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/225&quot; target=&quot;_blank&quot;&gt;2018/12/19 - [프로그래머스] 소수의 합, 소수 판별 알고리즘&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/226&quot; target=&quot;_blank&quot;&gt;2018/12/21 - [프로그래머스] 완주하지 못한 선수&lt;/a&gt;&lt;/p&gt;</description>
      <category>프로그래머스/Level 1</category>
      <category>Level 1</category>
      <category>완전탐색</category>
      <category>재귀</category>
      <category>프로그래머스</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/227</guid>
      <comments>https://boycoding.tistory.com/227#entry227comment</comments>
      <pubDate>Sun, 23 Dec 2018 02:13:24 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 완주하지 못한 선수, map과 unordered_map의 차이</title>
      <link>https://boycoding.tistory.com/226</link>
      <description>&lt;h1 style=&quot;text-align: left; clear: none; float: none; margin-bottom: 15px; font-size: 30px;&quot; id=&quot;h1h1&quot;&gt;해시: 완주하지 못한 선수&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.&lt;/p&gt;&lt;p&gt;마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;-&quot;&gt;이중 반복문을 사용하는 간단한 문제?&lt;/h2&gt;
&lt;p&gt;마라톤 선수가 어쩌고 말을 꼬아놨지만 간단한 문제다. 배열 A와 배열 B를 비교해서 남은 선수 한 명의 이름을 return 하면 된다. &lt;/p&gt;&lt;p&gt;간단하게 2중 반복문으로 구현하면 된다. &lt;code&gt;std::vector&lt;/code&gt;에서 &lt;code&gt;std::find&lt;/code&gt; 함수는 주어진 범위 내에서 순차적인 검색을 하므로 시간복잡도는 O(n)이다. 결국 O(n) * O(n) = O(n^2) 시간복잡도를 가진다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;lang-cpp&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; solution(vector&amp;lt;&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;&amp;gt; participant, vector&amp;lt;&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;&amp;gt; completion) 
{
    &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;name&lt;/span&gt; : completion)
    {
        auto &lt;span class=&quot;hljs-keyword&quot;&gt;it&lt;/span&gt; = find(participant.begin(), participant.&lt;span class=&quot;hljs-keyword&quot;&gt;end&lt;/span&gt;(), &lt;span class=&quot;hljs-built_in&quot;&gt;name&lt;/span&gt;);
        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;hljs-keyword&quot;&gt;it&lt;/span&gt; != participant.&lt;span class=&quot;hljs-keyword&quot;&gt;end&lt;/span&gt;())
        {
            participant.erase(&lt;span class=&quot;hljs-keyword&quot;&gt;it&lt;/span&gt;);
        }
    }
&lt;span class=&quot;hljs-built_in&quot;&gt;
    return&lt;/span&gt; participant[&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;당연히 시간 초과로 효율성 테스트를 통과하지 못했다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;map-unordered_map&quot;&gt;map, unordered_map&lt;/h2&gt;
&lt;p&gt;&amp;lt;이름, 갯수&amp;gt; 형태로 자료구조를 만들어 갯수를 비교해 남은 선수 한 명의 이름을 구할 수 있다.&lt;/p&gt;&lt;p&gt; C++ STL에는 &lt;code&gt;std::map&lt;/code&gt;과 &lt;code&gt;std::unordered_map&lt;/code&gt; 컨테이너가 있고, 둘 다 &lt;em&gt;key&lt;/em&gt;로 &lt;em&gt;value&lt;/em&gt;에 접근할 수 있다(&lt;key, value=&quot;&quot;&gt;). &lt;code&gt;map&lt;/code&gt;은 &lt;em&gt;Red-Black Tree&lt;/em&gt;를 사용해 키의 순서를 유지하므로 탐색 속도에 시간복잡도 O(log n)을 가진다. 반면 &lt;code&gt;unordered_map&lt;/code&gt;은 &lt;em&gt;Hash Table&lt;/em&gt;을 사용해 키의 순서를 유지하지 않는다. key 분포에 따라 탐색 속도에 O(1) 이상의 시간복잡도를 가진다.&lt;/key,&gt;&lt;/p&gt;&lt;p&gt;'완주하지 못한 선수' 문제에서 &amp;lt;이름, 갯수&amp;gt; 형태의 자료구조는 이름이 정렬되어 있을 필요가 없으므로 &lt;em&gt;unordered_map&lt;/em&gt;이 더 적합하고, 성능 또한 더 빠르다.&lt;/p&gt;&lt;p&gt;아래 문제 풀이는 O(n) + O(n) + O(n) = O(n) 시간복잡도를 가진다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;lang-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;solution&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-built_in&quot;&gt;vector&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;&amp;gt; participant, &lt;span class=&quot;hljs-built_in&quot;&gt;vector&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;&amp;gt; completion)&lt;/span&gt; 
&lt;/span&gt;{
    &lt;span class=&quot;hljs-built_in&quot;&gt;unordered_map&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt;&amp;gt; participants;

    &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; name : participant)
    {
        ++participants[name];
    }

    &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; name : completion)
    {
        --participants[name];
    }

    &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;hljs-keyword&quot;&gt;auto&lt;/span&gt; pair : participants)
    {
        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (pair.second &amp;gt; &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;)
        {
            &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; pair.first;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-&quot;&gt;정렬 후 비교&lt;/h2&gt;
&lt;p&gt;위에까지 직접 구현해 정확성과 효율성 테스트를 통과할 수 있었다. 코드를 제출하고 다른 사람들의 풀이를 통해 다른 방법도 알 수 있었다.&lt;/p&gt;&lt;p&gt;문제 제한사항에서 두 배열의 크기는 1 차이가 난다고 했으므로 두 배열을 정렬한 뒤 같은 인덱스에서 선수 이름이 다르다면 그 선수가 통과하지 못한 선수가 된다.&lt;/p&gt;&lt;p&gt;C++ STL에서 &lt;code&gt;std::sort&lt;/code&gt; 알고리즘은 O(nlogn) 시간복잡도를 보장한다. O(nlogn)  + O(nlogn)  + O(n) =  O(nlogn) 시간복잡도를 가진다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;lang-cpp&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;string&lt;/span&gt; solution(vector&amp;lt;&lt;span class=&quot;hljs-keyword&quot;&gt;string&lt;/span&gt;&amp;gt; participant, vector&amp;lt;&lt;span class=&quot;hljs-keyword&quot;&gt;string&lt;/span&gt;&amp;gt; completion) 
{
    sort(participant.&lt;span class=&quot;hljs-built_in&quot;&gt;begin&lt;/span&gt;(), participant.&lt;span class=&quot;hljs-built_in&quot;&gt;end&lt;/span&gt;());
    sort(completion.&lt;span class=&quot;hljs-built_in&quot;&gt;begin&lt;/span&gt;(), completion.&lt;span class=&quot;hljs-built_in&quot;&gt;end&lt;/span&gt;());

    &lt;span class=&quot;hljs-built_in&quot;&gt;for&lt;/span&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; i = &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;; i &amp;lt; completion.&lt;span class=&quot;hljs-built_in&quot;&gt;size&lt;/span&gt;(); ++i)
    {
        &lt;span class=&quot;hljs-built_in&quot;&gt;if&lt;/span&gt;(participant[i] != completion[i])
        {
            &lt;span class=&quot;hljs-built_in&quot;&gt;return&lt;/span&gt; participant[i];
        }
    }

    &lt;span class=&quot;hljs-built_in&quot;&gt;return&lt;/span&gt; participant[participant.&lt;span class=&quot;hljs-built_in&quot;&gt;size&lt;/span&gt;() - &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 문제풀이는 &lt;code&gt;unordered_map&lt;/code&gt;을 사용한 문제 풀이보다는 느리지만, 구현이 좀 더 편하다.&lt;/p&gt;
&lt;hr&gt;
&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 100px; width: 100px; height: 100px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/993DB83D5C1CCC3521&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F993DB83D5C1CCC3521&quot; width=&quot;100&quot; height=&quot;100&quot; alt=&quot;runner&quot; filename=&quot;runner.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 100px; height: 100px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://boycoding.tistory.com/225&quot; target=&quot;_blank&quot;&gt;2018/12/19 - [프로그래머스] 소수의 합, 소수 판별 알고리즘&lt;/a&gt;&lt;/p&gt;</description>
      <category>프로그래머스/Level 1</category>
      <category>Level 1</category>
      <category>프로그래머스</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/226</guid>
      <comments>https://boycoding.tistory.com/226#entry226comment</comments>
      <pubDate>Fri, 21 Dec 2018 20:20:37 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 소수의 합, 소수 판별 알고리즘</title>
      <link>https://boycoding.tistory.com/225</link>
      <description>&lt;h1 style=&quot;text-align: left; clear: none; float: none; margin-bottom: 15px; font-size: 30px;&quot; id=&quot;h1h1&quot;&gt;소수의 합&lt;/h1&gt;

&lt;blockquote&gt;
  &lt;p&gt;2부터 N까지의 모든 소수의 합을 구하세요.
  N이 7이라면 {2,3,5,7} = 17을 출력 하시면 됩니다.
  N의 범위는 2이상 10,000,000이하 입니다.
  효율성 테스트의 모든 시간 제한은 1초입니다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;step1&quot;&gt;Step 1. 소수의 정의와 소수 판별하기&lt;/h2&gt;

&lt;p&gt;소수란 1과 자기 자신으로밖에 나누어지지 않는 1 이외의 정수다. 예를 들어  { 2, 3, 5, 7, 11, 13….}과 같은 정수는 1 이외의 정수와 나누어지지 않는다. 위 문제에서 N은 양의 정수이므로 나누어 지지 않는다는 것은 나머지가 1 이상을 의미한다. &lt;/p&gt;
&lt;p&gt;대게 프로그래밍 언어는 사칙연산 이외에도 나머지 연산을 제공한다. 나머지 연산자의 결과값은 나누고 난 뒤의 나머지다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int a = 5 % 1; /* 0 */  int d = 5 % 4; /* 1 */

int b = 5 % 2; /* 1 */  int e = 5 % 5; /* 0 */

int c = 5 % 3; /* 2 */
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;소수인 양의 정수 5는 나눴을 때 1과 자기 자신 5를 제외하고는 나머지가 모두 1 이상이다. 즉 1과 n만을 약수로 가지므로 2부터 n-1까지의 수로는 나누어 지면 안 된다. 반대로 말하면 소수가 아닌 정수는 1과 자기 자신을 제외하고도 나머지가 0인 n이 있다는 것을 의미한다.&lt;/p&gt;
&lt;p&gt;또한. 소수는 2를 제외하고 모두 홀수라는 규칙이 있다. 반복문을 이용해 구현하면 아래와 같다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;bool IsPrimeNumber(int number)
{
    if (number == 2) return true;

    if (number == 1) return false;
    if (number % 2 == 0) return false; // 2를 제외한 소수는 항상 홀수

    // 소수는 홀수 이므로 짝수로 나누는 과정을 생략한다.
    for (int n = 3; n &amp;lt; number; n+=2)
    {
        if (number % n == 0) // 나누어지므로 소수가 아니다.
        {
            return false;
        }
    }

    return true;
}&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;&lt;h2 id=&quot;step2naxb&quot;&gt;Step 2. N = A X B&lt;/h2&gt;

&lt;p&gt;어떤 수 n이 소수가 아닌 경우 n = a * b 관계이므로 n을 a나 b로 나눠 보면 된다. 아래 그림을 보면 a와 b 중 더 큰 수는 무조건 n의 제곱근의 값보다 크다. (더 작은 수는 n의 제곱근 이하의 값을 가진다.)&lt;/p&gt;
&lt;p&gt;즉, 어떤 정수 n의 제곱근까지의 수 중 한 개의 수에 대해서라도 나누어떨어지면 소수가 아니다. &lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 500px; width: 500px; height: 172px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99A5E9465C193C5E06&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99A5E9465C193C5E06&quot; width=&quot;500&quot; height=&quot;172&quot; alt=&quot;n=axb&quot; filename=&quot;primeroot.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 500px; height: 172px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;위 규칙을 이용해서 &lt;em&gt;IsPrime&lt;/em&gt; 함수의 반복문을 수정해보자.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int rootNumber = sqrt(number);
for (int n = 3; n  &amp;lt;= rootNumber; n+=2)
{
    if (number % n == 0)
    {
        return false;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;알고리즘의 속도는 대게 반복문이 지배한다. 시간 복잡도가 n에서 sqrt(n)으로 향상되었다.&lt;/p&gt;

&lt;hr&gt;&lt;h2 id=&quot;step3&quot;&gt;Step 3. 캐시, 메모이제이션 : 소수 재사용&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;9 = { 1, 3, 9 }&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;15 = { 1, 3, 5, 15}&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;21 = { 1, 3, 7, 21}&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;25 = { 1, 5, 25 }&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;어떤 수 n이 소수가 아닌 경우 n보다 작은 소수로 나누어진다. 즉, n보다 작은 소수들로만 나누어 보면 된다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int rootNumber = sqrt(number);
for (int n = 3; n  &amp;lt;= rootNumber; n+=2)
{
    if (IsPrimeNumber(n) &amp;amp;&amp;amp; (number % n == 0)) // 소수만 나누어 본다.
    {
        return false;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;하지만 위 방법은 나누는 수가 소수인지 매번 판별하므로 지금까지에 비해 매우 느리다.&lt;/p&gt;
&lt;p&gt;판별된 소수를 재사용함으로써 속도의 향상을 꾀할 수 있다. 그러기 위해서는 소수를 메모리에 저장해 둘 필요가 있다. 이때 이미 계산된 값을 저장해 두는 메모리의 장소를 캐시(cache)라 하고. 캐시에 저장된 값을 재활용하는 기법을 메모이제이션(memoization)이라고 부른다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;bool IsPrimeNumber(int number)
{
    static vector&amp;lt;int&amp;gt; primes {2, 3, 5, 7, 11, 13}; // 소수가 들어있는 벡터 (캐시)

    if (number == 2) return true;

    if (number == 1) return false;
    if (number % 2 == 0) return false;

    int rootNumber = sqrt(number);

    int i, n = 0;
    while (n &amp;lt;= rootNumber) // 벡터에 들어있는 소수들 중 n의 제곱근 보다 작은 소수와 먼저 나누어 본다.
    {                      // (메모이제이션)
        n = primes[i++];
        if(number % n == 0) return false;
    }

    for (; n  &amp;lt;= rootNumber; n+=2)
    {
        if (number % n == 0)
        {
            return false;
        }
    }

    primes.push_back(number); // 벡터(캐시)에 없는 새로운 소수 추가
    return true;
}

long long solution(int N) {
    long long answer = 2;

    for (int i = 3; i &amp;lt;= N; i+=2)
    {
        if(IsPrimeNumber(i) == true)
        {
            answer += i;
        }   
    }

    return answer;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Step 3의 알고리즘은 '소수 판별하기'가 아닌 '정수 N까지 소수의 합'에만 적용할 수 있는 알고리즘이다.&lt;/p&gt;
&lt;p&gt;나는 여기 내용까지 직접 생각하고 구현해서 프로그래머스의 정확성과 효율성 테스트를 통과할 수 있었다. 제출 후 다른 사람들의 코드를 검토한 결과 '소수의 합'을 위한 좀 더 좋은 방법이 있다는 걸 알게 되었다.&lt;/p&gt;

&lt;hr&gt;&lt;h2 id=&quot;step4&quot;&gt;Step 4. 에라토네스의 체&lt;/h2&gt;

&lt;blockquote style=&quot;margin-bottom: 10px;&quot;&gt;
  &lt;p&gt;고대 그리스의 수학자 에라토스테네스가 만들어 낸 소수를 찾는 방법. 이 방법은 마치 체로 치듯이 수를 걸러낸다고 하여 '에라토스테네스의 체'라고 부른다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 300px; width: 300px; height: 249px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99BEFF495C193C9306&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99BEFF495C193C9306&quot; width=&quot;300&quot; height=&quot;249&quot; alt=&quot;에라토네스의 체2&quot; filename=&quot;에라토네스의체.gif&quot; filemime=&quot;image/gif&quot; style=&quot;width: 300px; height: 249px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 600px; width: 600px; height: 237px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99BEC5495C193C9106&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99BEC5495C193C9106&quot; width=&quot;600&quot; height=&quot;237&quot; alt=&quot;에라토네스의 체1&quot; filename=&quot;에라토네스의체.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 600px; height: 237px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;위 내용은 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%97%90%EB%9D%BC%ED%86%A0%EC%8A%A4%ED%85%8C%EB%84%A4%EC%8A%A4%EC%9D%98_%EC%B2%B4&quot; target=&quot;blank&quot;&gt;위키백과: 에라토네스의 체&lt;/a&gt;의 일부분이다.&lt;/p&gt;
&lt;p&gt;한마디로 제일 작은 소수 2부터 시작하여 체에서 배수를 모두 거두는 방식이다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;long long solution(int N) {
    long long answer = 0;
    vector&amp;lt;bool&amp;gt; sieve(N+1); // 에라토네스의 체 (default: false)

    for (int i = 2; i &amp;lt;= N; ++i)
    {
        if (sieve[i] == false)
        {
            answer += i;
        }

        for (int j = i; j &amp;lt;= N; j+=i) // 체에서 배수를 모두 거둔다.
        {
            sieve[j] = true; 
        }
    }

    return answer;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Step 3과 마찬가지로 Step 4의 아리토네스의 체 알고리즘은 '소수 판별하기'가 아닌 '정수 N까지 소수의 합'에 더 적합한&amp;nbsp;알고리즘이다.&lt;/p&gt;&lt;hr&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 100px; text-align: center; width: 100px; height: 83px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99A7C5485C19405907&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99A7C5485C19405907&quot; width=&quot;100&quot; height=&quot;83&quot; alt=&quot;prime&quot; filename=&quot;prime.jpg&quot; filemime=&quot;image/jpeg&quot; style=&quot;text-align: center; width: 100px; height: 83px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래머스/Level 1</category>
      <category>Level 1</category>
      <category>프로그래머스</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/225</guid>
      <comments>https://boycoding.tistory.com/225#entry225comment</comments>
      <pubDate>Wed, 19 Dec 2018 03:50:04 +0900</pubDate>
    </item>
    <item>
      <title>문자열을 더해서 만드는 3가지 방법: String Performance</title>
      <link>https://boycoding.tistory.com/224</link>
      <description>&lt;h1 id=&quot;stringformatvsconcatenationvsstringbuilder&quot;&gt;String.Format() vs. Concatenation vs. String Builder&lt;/h1&gt;

&lt;p&gt;C# 에서 문자열을 더해서 만드는 3가지 방법&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;String 클래스에 내장된 &lt;code&gt;string.Format&lt;/code&gt; 메서드&lt;/li&gt;

&lt;li&gt;언어 자체 기능인 Concatenation &lt;code&gt;(&quot;a&quot; + &quot;b&quot;)&lt;/code&gt;&lt;/li&gt;

&lt;li&gt;&lt;code&gt;System.Text.StringBuilder&lt;/code&gt; 클래스&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;위 세가지 중 무엇이 가장 빠를까?&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;studyexample&quot;&gt;Study &amp;amp; Example&lt;/h2&gt;

&lt;h3 id=&quot;stringformat&quot;&gt;string.Format&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;c# language-c#&quot;&gt;// Placeholders are of the form: {index}
string.Format(&quot;{0} + {1} = {2}&quot;, 2, 3, 5); // returns &quot;2 + 3 = 5&quot;

// Mixed types are OK, too
string.Format(&quot;{0} {1}, {2}&quot;, &quot;August&quot;, 5, 1972); // returns &quot;August 5, 1972&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;concatenation&quot;&gt;Concatenation&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;c# language-c#&quot;&gt;// Strings can be concatenated by the + operator
&quot;Hello,&quot; + &quot; &quot; + &quot;world!&quot;; // evaluates to: &quot;Hello, world!&quot;

// Mixed types are OK, too
&quot;August &quot; + 5 + &quot;, &quot; + 1972; // evaluates to: &quot;August 5, 1972&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;systemtextstringbuilder&quot;&gt;System.Text.StringBuilder&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;c# language-c#&quot;&gt;var builder = new StringBuilder(); // initially empty
var builder = new StringBuilder(1024); // empty, but with 1024 bytes of capacity
var builder = new StringBuilder(&quot;hello&quot;); // initially &quot;hello&quot;

// Append various types
builder.Append(&quot;string&quot;);
builder.Append('c');
builder.Append(123);
builder.Append(3.1415);

// Get the result string
var str = builder.ToString();
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;

&lt;h2 id=&quot;test&quot;&gt;Test&lt;/h2&gt;

&lt;p&gt;위 각각의 방법으로 1000개의 단일 문자열로 만드는 테스트를 100000번씩 반복하여 테스트를 진행해봤다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;c# language-c#&quot;&gt;using UnityEngine;
using System;
using System.Text;
using System.Diagnostics;


public static class StopWatchExtensions
{
    public static long RunTest(this Stopwatch stopWatch, int iterationCount, Action testFunction)
    {
        stopWatch.Reset();
        stopWatch.Start();

        for (int i = 0; i &amp;lt; iterationCount; ++i)
        {
            testFunction();
        }

        return stopWatch.ElapsedMilliseconds;
    }
}

public class StringTest : MonoBehaviour
{
    private StringBuilder builder = new StringBuilder(1024);
    private string report;

    void Start()
    {
        var iterationCount = 100000;
        var formatString = &quot;&quot;;

        for (int i = 0; i &amp;lt; 1000; ++i)
        {
            formatString += &quot;{&quot; + i + &quot;}&quot;;
        }

        var stopWatch = new Stopwatch();

        var stringFormatTime = stopWatch.RunTest(iterationCount, ()=&amp;gt; StringFormat(formatString));

        var concatenationTime = stopWatch.RunTest(iterationCount, ()=&amp;gt; Concatenation());

        var stringBuilderTime = stopWatch.RunTest(iterationCount, ()=&amp;gt; StringBuilder());

        report = 
            &quot;Test: result\n&quot;   +
            &quot;String.Format: &quot;  + stringFormatTime  + &quot;\n&quot; +
            &quot;Concatenation: &quot;  + concatenationTime + &quot;\n&quot; +
            &quot;StringBuilder: &quot;  + stringBuilderTime;
    }

    void OnGUI()
    {
        GUI.TextArea(new Rect(0, 0, Screen.width, Screen.height), report);
    }

    private string StringFormat(string formatString)
    {
        var s = &quot;A&quot;;

        return string.Format(
            formatString,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s,
            s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s
        );
    }

    private string Concatenation()
    {
        var s = &quot;A&quot;;

        return
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s +
            s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s;
    }

    private string StringBuilder()
    {
        var s = &quot;A&quot;;

        var builder = new StringBuilder();

        for (int i = 0; i &amp;lt; 1000; ++i)
        {
            builder.Append(s);
        }

        return builder.ToString();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
  &lt;p&gt;Test: result&lt;/p&gt;
  
  &lt;p&gt;String.Format: 7843&lt;/p&gt;
  
  &lt;p&gt;Concatenation: 2099&lt;/p&gt;
  
  &lt;p&gt;StringBuilder:  2260&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr&gt;

&lt;h1 id=&quot;result&quot;&gt;Result&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;string.Format은 위 테스트에서 다른 방법들보다 3배정도 시간이 더 걸리며 가장 느리다.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Concatenation과 StringBuilder는 성능차이가 크게나지 않는다.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;하지만 StringBuilder에 추가적인 최적화방법이 있다. 위 테스트에서 StringBuilder의 인스턴스는 비어있는 상태로 시작했지만, 전체 문자열을 유지할 수 있을만큼 큰 초기용량이 주어지면 성능은 더 향상될 것이다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;c# language-c#&quot;&gt;var builder = new StringBuilder(1024);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;마지막으로 매 반복문에서 StringBuilder의 인스턴스를 만드는대신, 하나의 StringBuilder 인스턴스의 길이를 초기화하는 방식으로 사용함으로써 성능향상을 할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;c# language-c#&quot;&gt;private StringBuilder m_builder = new StringBuilder(1024);

private string StringBuilder()
{
    var s = &quot;A&quot;;

    m_bulder.Length = 0;

    for (int i = 0; i &amp;lt; 1000; ++i)
    {
        m_builder.Append(s);
    }

    return m_builder.ToString();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
  &lt;p&gt;Test: result&lt;/p&gt;
  
  &lt;p&gt;String.Format: 7959&lt;/p&gt;
  
  &lt;p&gt;Concatenation: 2074&lt;/p&gt;
  
  &lt;p&gt;StringBuilder:  1793&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr&gt;&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p&gt;&lt;span style=&quot;color: rgb(140, 140, 140);&quot;&gt;&lt;/span&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/995D6A345BAF70580F&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F995D6A345BAF70580F&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;string&quot; filename=&quot;bass.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;color: rgb(140, 140, 140);&quot;&gt;번역: https://jacksondunstan.com/articles/3015&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <category>유니티 &amp;amp; C# 이야기</category>
      <category>C#</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/224</guid>
      <comments>https://boycoding.tistory.com/224#entry224comment</comments>
      <pubDate>Sat, 29 Sep 2018 21:32:08 +0900</pubDate>
    </item>
    <item>
      <title>List에 삽입할 개수를 미리 안다면 Capacity 프로퍼티를 설정하자.</title>
      <link>https://boycoding.tistory.com/223</link>
      <description>&lt;h1 id=&quot;listcapacity&quot;&gt;List.Capacity&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.capacity?view=netframework-4.7.2&quot;&gt;Capacity&lt;/a&gt; is the number of elements that the &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1?view=netframework-4.7.2&quot;&gt;List&lt;/a&gt; can store before resizing is required, whereas &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.count?view=netframework-4.7.2&quot;&gt;Count&lt;/a&gt; is the number of elements that are actually in the &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1?view=netframework-4.7.2&quot;&gt;List&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.capacity?view=netframework-4.7.2&quot;&gt;Capacity&lt;/a&gt; is always greater than or equal to &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.count?view=netframework-4.7.2&quot;&gt;Count&lt;/a&gt;. If &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.count?view=netframework-4.7.2&quot;&gt;Count&lt;/a&gt; exceeds &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.capacity?view=netframework-4.7.2&quot;&gt;Capacity&lt;/a&gt; while adding elements, the capacity is increased by automatically reallocating the internal array before copying the old elements and adding the new elements.&lt;/p&gt;&lt;p&gt;If the capacity is significantly larger than the count and you want to reduce the memory used by the &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1?view=netframework-4.7.2&quot;&gt;List&lt;/a&gt;, you can decrease capacity by calling the &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.trimexcess?view=netframework-4.7.2&quot;&gt;TrimExcess&lt;/a&gt; method or by setting the &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.capacity?view=netframework-4.7.2&quot;&gt;Capacity&lt;/a&gt; property explicitly to a lower value. When the value of &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.capacity?view=netframework-4.7.2&quot;&gt;Capacity&lt;/a&gt;is set explicitly, the internal array is also reallocated to accommodate the specified capacity, and all the elements are copied.&lt;/p&gt;&lt;p&gt;Retrieving the value of this property is an O(1) operation; setting the property is an O(&lt;em&gt;n&lt;/em&gt;) operation, where &lt;em&gt;n&lt;/em&gt; is the new capacity.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.capacity?redirectedfrom=MSDN&amp;amp;view=netframework-4.7.2#System_Collections_Generic_List_1_Capacity&quot;&gt;&lt;span style=&quot;color: rgb(140, 140, 140); font-size: 10pt;&quot;&gt;https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.capacity?redirectedfrom=MSDN&amp;amp;view=netframework-4.7.2#System_Collections_Generic_List_1_Capacity&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;List&lt;t&gt;의 내부 구현은 LinkedList가 아닌 ArrayList&lt;/t&gt;&lt;/p&gt;&lt;p&gt;capacity 만큼 메모리를 미리 잡고있는다. &lt;/p&gt;&lt;p&gt;요약 &amp;amp; 생각해야할 점&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;capacity를 넘어가면 재할당 후 복사하므로 비용이 크다. (반복문에서 빈번하게 일어날 경우...)&lt;/li&gt;

&lt;li&gt;capacity가 증가될 시 count에 따라 기하 급수적으로 커질 수 있다.&lt;/li&gt;

&lt;li&gt;capacity * T.Size 만큼 메모리를 잡고 있으므로 낭비가 크다. (게임에서는 중요!)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;List&lt;t&gt;에 삽입할 개수를 미리 안다면 Capacity를 미리 설정하여 메모리를 확보하는 습관을 가지자.&lt;/t&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;ex)&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;c# language-c#&quot;&gt;monsters.Capacity = monsterDatas.Count; 

foreach(var data in monsterDatas)
{
    Monster monsterObj = GameObject.Instantiate(m_monsterPrefab) as Monster;
    /// ...
    /// ...

    monsters.Add(monsterObj);
}
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 128px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/997138415BA7410522&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F997138415BA7410522&quot; width=&quot;128&quot; height=&quot;128&quot; alt=&quot;capacity&quot; filename=&quot;liter.png&quot; filemime=&quot;image/jpeg&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>유니티 &amp;amp; C# 이야기</category>
      <category>C#</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/223</guid>
      <comments>https://boycoding.tistory.com/223#entry223comment</comments>
      <pubDate>Sun, 23 Sep 2018 20:07:31 +0900</pubDate>
    </item>
    <item>
      <title>C++ 08.08 - 디폴트 매개 변수 (default parameter)</title>
      <link>https://boycoding.tistory.com/222</link>
      <description>&lt;h1 id=&quot;0808defaultparameter&quot;&gt;디폴트 매개 변수 (default parameter)&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;디폴트 매개 변수(default parameter)&lt;/em&gt;는 기본값이 제공된 함수 매개 변수다.&lt;/strong&gt; 사용자가 이 매개 변수의 값을 제공하지 않으면 기본값(default value)이 사용된다. 반대로 매개 변수의 값을 제공하면 사용자 제공 값이 기본값 대신 사용된다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void printValues(int x, int y=10)
{
    std::cout &amp;lt;&amp;lt; &quot;x: &quot; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; '\n';
    std::cout &amp;lt;&amp;lt; &quot;y: &quot; &amp;lt;&amp;lt; y &amp;lt;&amp;lt; '\n';
}

int main()
{
    printValues(1);    // y will use default parameter of 10
    printValues(3, 4); // y will use user-supplied value 4
}
/*
x: 1
y: 10
x: 3
y: 4
*/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;첫 번째 함수 호출(&lt;code&gt;printValues(1)&lt;/code&gt;)에서 호출자는 &lt;em&gt;y&lt;/em&gt;에 대한 인수를 제공하지 않으므로 함수는 기본값 &lt;em&gt;10&lt;/em&gt;을 사용한다. 두 번째 호출(&lt;code&gt;printValues(3, 4)&lt;/code&gt;)에서 호출자는 &lt;em&gt;y&lt;/em&gt; 값을 제공했으므로 사용자 제공 값이 사용되었다.&lt;/p&gt;&lt;p&gt;디폴트 매개 변수(default parameter)가 있는 함수는 사용자가 무시할 수도 있고 그렇지 않을 수도 있으므로 훌륭한 옵션이다. 예를 들어, 다음 예제는 default parameter를 사용하는 몇 가지 함수 원형(prototype)이다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void openLogFile(std::string filename=&quot;default.log&quot;);
int rollDie(int sides=6);
void printStringInColor(std::string str, Color color=COLOR_BLACK); // Color is an enum
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;

&lt;h2 id=&quot;multipledefaultparameters&quot;&gt;Multiple default parameters&lt;/h2&gt;

&lt;p&gt;함수는 디폴트 매개 변수(default parameter)를 여러 개 가질 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void printValues(int x=10, int y=20, int z=30)
{
    std::cout &amp;lt;&amp;lt; &quot;Values: &quot; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; y &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; z &amp;lt;&amp;lt; '\n';
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;함수를 호출해보자.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;printValues(1, 2, 3);
printValues(1, 2);
printValues(1);
printValues();
/*
Values: 1 2 3
Values: 1 2 30
Values: 1 20 30
Values: 10 20 30
*/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;매개 변수 &lt;em&gt;x&lt;/em&gt;와 &lt;em&gt;y&lt;/em&gt;에 대한 인수를 제공하지 않고는 매개 변수 &lt;em&gt;z&lt;/em&gt;에 인수를 제공할 수 없다. C++가 &lt;code&gt;printValues(, , 3)&lt;/code&gt;과 같은 호출 구문을 지원하지 않기 때문이다. 이는 다음과 같은 규칙 때문이다.&lt;/p&gt;&lt;p&gt;&lt;em&gt;모든 default parameter는 오른쪽부터 지정해야 한다. 그래서 다음은 허용되지 않는다.&lt;/em&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void printValue(int x=10, int y); // not allowed
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;

&lt;h2 id=&quot;defaultparameterscanonlybedeclaredonce&quot;&gt;Default parameters can only be declared once&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;선언된 후에는 디폴트 매개 변수(default parameter)를 다시 선언할 수 없다.&lt;/strong&gt; 즉, 전방 선언과 함수 정의가 있는 함수의 경우, default parameter는 전방 선언이나 함수 정의 둘 중 하나에서만 선언할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void printValues(int x, int y=10);

void printValues(int x, int y=10) // error: redefinition of default parameter
{
    std::cout &amp;lt;&amp;lt; &quot;x: &quot; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; '\n';
    std::cout &amp;lt;&amp;lt; &quot;y: &quot; &amp;lt;&amp;lt; y &amp;lt;&amp;lt; '\n';
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;가장 좋은 방법은 전방 선언이 다른 헤더 파일에 있는 경우 함수 정의가 아닌 전방 선언에서 default parameter를 선언하는 것이다.&lt;/p&gt;

&lt;h6 id=&quot;fooh&quot;&gt;foo.h:&lt;/h6&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#ifndef FOO_H
#define FOO_H
void printValues(int x, int y=10);
#endif
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;maincpp&quot;&gt;main.cpp:&lt;/h6&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &quot;foo.h&quot;
#include &amp;lt;iostream&amp;gt;

void printValues(int x, int y)
{
    std::cout &amp;lt;&amp;lt; &quot;x: &quot; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; '\n';
    std::cout &amp;lt;&amp;lt; &quot;y: &quot; &amp;lt;&amp;lt; y &amp;lt;&amp;lt; '\n';
}

int main()
{
    printValues(5);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위의 예제 main.cpp의 #include &quot;foo.h&quot;는 default parameter를 정의하는 전방 선언을 하고 있기 때문에 &lt;code&gt;printValues()&lt;/code&gt; 함수를 가지고 있으므로 main.cpp의 &lt;code&gt;printValues()&lt;/code&gt; 함수 정의에서는 default parameter를 사용할 수 없다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;defaultparametersandfunctionoverloading&quot;&gt;Default parameters and function overloading&lt;/h2&gt;

&lt;p&gt;디폴트 파라미터(default parameter)가 있는 함수를 오버로딩(overloading)할 수 있다. 예를 들어, 다음 예제를 보자.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void print(std::string string);
void print(char ch=' ');
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;사용자가 &lt;code&gt;print()&lt;/code&gt;를 호출하면 &lt;code&gt;print(' ')&lt;/code&gt;가 호출되어 스페이스를 출력한다.&lt;/p&gt;&lt;p&gt;그러나 다음은 허용하지 않는다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void printValues(int x);
void printValues(int x, int y=20);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;호출한 사람이 &lt;code&gt;printValues(10)&lt;/code&gt;을 호출하면 컴파일러는 사용자가 &lt;code&gt;printValues(int)&lt;/code&gt;인지 &lt;code&gt;printValues(int, 20)&lt;/code&gt;지 여부를 명확히 할 수 없다.&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99FFE73B5B82672538&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99FFE73B5B82672538&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;cpp&quot; filename=&quot;cpp2.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 11pt; color: rgb(140, 140, 140);&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: 11pt; color: rgb(140, 140, 140);&quot;&gt;번역: 이 포스트의 원문은 http://www.learncpp.com/cpp-tutorial/77-default-parameters/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <category>C++ 이야기/08. 함수</category>
      <category>C++</category>
      <category>C++ 함수</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/222</guid>
      <comments>https://boycoding.tistory.com/222#entry222comment</comments>
      <pubDate>Sun, 26 Aug 2018 17:39:56 +0900</pubDate>
    </item>
    <item>
      <title>C++ 08.07 - 함수 오버로딩 (Function overloading)</title>
      <link>https://boycoding.tistory.com/221</link>
      <description>&lt;h1 id=&quot;0807functionoverloading&quot;&gt;함수 오버로딩 (Function overloading)&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;함수 오버로딩(function overloading)&lt;/strong&gt;은 다른 매개 변수를 가진 같은 이름의 여러 함수를 만들 수 있는 C++의 기능이다. 다음 함수를 보자.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int add(int x, int y)
{
    return x + y;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이 간단한 함수는 두 개의 정수를 더한다. 그러나 두 개의 부동 소수점 숫자를 더해야 하는 경우는 어떻게 해야 할까? 부동 소수점 매개 변수가 정수로 변환될 때 소수값을 잃게 되므로 위 함수는 적합하지 않다.&lt;/p&gt;&lt;p&gt;이 문제를 해결하는 한 가지 방법은 약간 다름 이름을 여러 함수를 정의하는 것이다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int addInteger(int x, int y)
{
    return x + y;
}

double addDouble(double x, double y)
{
    return x + y;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;일관된 이름을 정의하고, 함수의 모든 이름을 기억한 다음 올바른 이름의 함수를 불러오는 방법이다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;함수 오버로딩(function overloading)&lt;/strong&gt;은 더 나은 솔루션을 제공한다. 함수 오버로딩을 사용하면 &lt;code&gt;double&lt;/code&gt; 매개 변수를 취하는 또 다른 &lt;code&gt;add()&lt;/code&gt; 함수를 선언할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;double add(double x, double y)
{
    return x + y;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이제 &lt;code&gt;add()&lt;/code&gt;의 두 가지 버전이 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int add(int x, int y);          // integer version
double add(double x, double y); // floating point version
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이것이 네이밍 충돌을 일으킬 것으로 예상할 수도 있지만, 그렇지 않다. 컴파일러는 함수 호출에 사용된 인수를 기반으로 호출할 &lt;code&gt;add()&lt;/code&gt; 버전을 결정할 수 있다. 두 개의 int를 제공하면 C++은 &lt;code&gt;add(int, int)&lt;/code&gt;를 호출한다. 두 개의 &lt;code&gt;double&lt;/code&gt;을 제공하면 C++은 &lt;code&gt;add(double, double)&lt;/code&gt;을 호출한다. 사실 각 &lt;code&gt;add()&lt;/code&gt; 함수가 고유한 매개 변수를 가지고 있는 한 원하는 만큼 많은 오버로드된 &lt;code&gt;add()&lt;/code&gt; 함수를 정의할 수 있다.&lt;/p&gt;&lt;p&gt;따라서 매개 변수의 수가 다른 add() 함수를 정의할 수도 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int add(int x, int y, int z)
{
    return x + y + z;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이 &lt;code&gt;add()&lt;/code&gt; 함수는 2개 대신 3개의 매개 변수를 갖지만 다른 버전은 &lt;code&gt;add()&lt;/code&gt;와 매개 변수가 다르므로 유효한 함수다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;functionreturntypesarenotconsideredforuniqueness&quot;&gt;Function return types are not considered for uniqueness&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;함수의 반환 타입은 함수 오버로딩에서 고려되지 않는다.&lt;/strong&gt; (함수는 인수에만 기반하여 호출된다. 반환 값이 포함된 경우 함수의 어떤 버전이 호출되었는지 쉽게 알 수 있는 구문 방식이 없다)&lt;/p&gt;&lt;p&gt;난수를 반환하는 함수를 작성하려고 하지만 int를 반환하는 버전과 double을 반환하는 다른 버전이 필요한 경우를 생각해보자. 아래와 같은 유혹에 빠질 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int getRandomValue();
double getRandomValue();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;그러나 컴파일러는 오류를 발생시킨다. 위 두 함수는 동일한 매개 변수(:없음)를 가지므로 결과적으로 두 번째 &lt;code&gt;getRandomValue()&lt;/code&gt;는 첫 번째 함수를 잘못된 재선언으로 처리된다.&lt;/p&gt;&lt;p&gt;이 문제를 해결하는 가장 좋은 방법은 함수에 서로 다른 이름을 지정하는 것이다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int getRandomInt();
double getRandomDouble();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;또 다른 방법은 함수가 &lt;em&gt;void&lt;/em&gt;를 반환하도록 하고 반환 값을 호출자에게 &lt;em&gt;out&lt;/em&gt; 참조 매개 변수로 전달하도록 하는 것이다. &lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void getRandomValue(int&amp;amp; out);
void getRandomValue(double&amp;amp; out);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이러한 함수에는 다른 매개 변수가 있으므로 고유한 것으로 간주된다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;typedefsarenotdistinct&quot;&gt;Typedefs are not distinct&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;typedef&lt;/code&gt; 선언은 새로운 타입을 만드는 것이 아니므로 아래 &lt;code&gt;Print()&lt;/code&gt; 함수의 두 선언은 동일하게 간주된다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;typedef char* string;
void print(string value);
void print(char* value); // Compile Error
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;

&lt;h2 id=&quot;howfunctioncallsarematchedwithoverloadedfunctions&quot;&gt;How function calls are matched with overloaded functions&lt;/h2&gt;

&lt;p&gt;오버로드(overload)된 함수를 호출하면 다음 세 가지 결과 중 하나가 발생한다.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;일치하는 함수가 있다. (호출이 특정 오버로드된 함수로 해석된다.)&lt;/li&gt;

&lt;li&gt;일치하는 함수가 없다. (오버로드된 함수 중에 인수가 일치하는 함수가 없다.)&lt;/li&gt;

&lt;li&gt;모호한 함수가 있다. (하나 이상의 오버로드된 함수와 인수가 일치한다.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;오버로드된 함수가 호출되면 C++은 다음 프로세스를 통해 호출할 함수의 버전을 결정한다.&lt;/p&gt;&lt;p&gt;1) 먼저 C++은 정확하게 일치하는 함수를 찾으려고 한다. 이것은 실제 인수가 오버로드된 함수 중 하나의 매개 변수 타입과 정확히 일치하는 경우다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void print(char* value);
void print(int value);

print(0); // exact match with print(int)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;0&lt;/em&gt;은 기술적으로 &lt;code&gt;print(char*)&lt;/code&gt;와 일치할 수 있지만 &lt;code&gt;print(int)&lt;/code&gt;와 정확하게 일치한다.&lt;/p&gt;&lt;p&gt;2) 정확히 일치하는 항목이 없으면 C++은 승격(promotion)을 통해 일치하는 함수를 찾으려고 한다. 이것은 '05.01 - 암시적 형 변환'에서 다룬 적이 있다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;char&lt;/code&gt;, &lt;code&gt;unsigned char&lt;/code&gt; 및 &lt;code&gt;short&lt;/code&gt;가 &lt;code&gt;int&lt;/code&gt;로 승격된다.&lt;/li&gt;

&lt;li&gt;&lt;code&gt;unsigned short는 int&lt;/code&gt;의 크기에 따라 &lt;code&gt;int&lt;/code&gt; 또는 &lt;code&gt;unsigned int&lt;/code&gt;로 승격된다.&lt;/li&gt;

&lt;li&gt;&lt;code&gt;float&lt;/code&gt;은 &lt;code&gt;double&lt;/code&gt;로 승격된다.&lt;/li&gt;

&lt;li&gt;열거형(&lt;code&gt;enum&lt;/code&gt;)은 &lt;code&gt;int&lt;/code&gt;로 승격된다.&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void print(char* value);
void print(int value);

print('a'); // promoted to match print(int)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 프로그램의 경우, &lt;code&gt;print(char)&lt;/code&gt;가 없으므로 &lt;code&gt;char&lt;/code&gt; &lt;em&gt;'a'&lt;/em&gt;는 &lt;code&gt;int&lt;/code&gt;로 승격되고 &lt;code&gt;print(int)&lt;/code&gt;와 일치한다.&lt;/p&gt;&lt;p&gt;3) 승격(promotion)이 불가능한 경우 C++은 표준 변환을 통해 일치하는 항목을 찾으려고 한다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;숫자 타입은 다른 숫자 타입으로 변환된다. (Ex. &lt;code&gt;int&lt;/code&gt;-&amp;gt;&lt;code&gt;float&lt;/code&gt;)&lt;/li&gt;

&lt;li&gt;열거형(&lt;code&gt;enum&lt;/code&gt;)은 위에서 말한 숫자 공식과 같다. (Ex. &lt;code&gt;enum&lt;/code&gt;-&amp;gt;&lt;code&gt;int&lt;/code&gt;-&amp;gt;&lt;code&gt;float&lt;/code&gt;)&lt;/li&gt;

&lt;li&gt;0은 포인터 타입 및 숫자 타입과 일치한다.(Ex. &lt;code&gt;0&lt;/code&gt;-&amp;gt;&lt;code&gt;char*&lt;/code&gt; or &lt;code&gt;0&lt;/code&gt;-&amp;gt;&lt;code&gt;float&lt;/code&gt;)&lt;/li&gt;

&lt;li&gt;포인터는 void 포인터와 일치한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;struct Employee; // defined somewhere else
void print(float value);
void print(Employee value);

print('a'); // 'a' converted to match print(float)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이 경우 &lt;code&gt;print(char)&lt;/code&gt; 및 &lt;code&gt;print(int)&lt;/code&gt;가 없으므로 &lt;em&gt;'a'&lt;/em&gt;는 &lt;code&gt;float&lt;/code&gt;으로 변환되어 &lt;code&gt;print(float)&lt;/code&gt;와 일치한다.&lt;/p&gt;&lt;p&gt;4) 마지막으로, C++은 사용자 정의 변환을 통해 일치하는 함수를 찾는다. 아직 클래스를 배우지는 않았지만, 클래스는 해당 클래스의 객체와 암시적으로 적용될 수 있는 다른 타입으로 변환을 정의할 수 있다. 예를 들어 클래스 &lt;em&gt;X&lt;/em&gt;의 사용자 정의 변환을 &lt;code&gt;int&lt;/code&gt;로 정의할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;class X; // with user-defined conversion to int

void print(float value);
void print(int value);

X value; // declare a variable named value of type class X
print(value); // value will be converted to an int and matched to print(int)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;value&lt;/em&gt;는 클래스 &lt;em&gt;X&lt;/em&gt; 타입이지만 사용자가 정의한 &lt;code&gt;int&lt;/code&gt;로 변환하므로 &lt;code&gt;print(value)&lt;/code&gt;는 &lt;code&gt;print(int)&lt;/code&gt;로 결정된다.&lt;/p&gt;&lt;p&gt;나중에 클래스를 공부할 때 클래스의 사용자 정의 변환을 수행하는 방법을 자세히 설명할 예정이다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;ambiguousmatches&quot;&gt;Ambiguous matches&lt;/h2&gt;

&lt;p&gt;모든 오버로드된 함수가 고유한 매개 변수를 가져야 하는 경우 호출이 둘 이상과 일치하면 어떻게 될까?&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void print(unsigned int value);
void print(float value);

print('a');
print(0);
print(3.14159);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위에서 &lt;code&gt;print('a')&lt;/code&gt;의 경우 C++은 정확하게 일치하는 함수를 찾을 수 없다. &lt;em&gt;'a'&lt;/em&gt;를 &lt;code&gt;int&lt;/code&gt;로 승격시키려고 하지만 &lt;code&gt;print(int)&lt;/code&gt;도 없다. 표준 변환을 하면 &lt;em&gt;'a'&lt;/em&gt;는 &lt;code&gt;unsigned int&lt;/code&gt;와 &lt;code&gt;float&lt;/code&gt;으로 변환할 수 있다. 이 두 표준 변환으로 인해 모호한 일치가 발생한다.&lt;/p&gt;&lt;p&gt;&lt;code&gt;print(0)&lt;/code&gt;도 비슷하다. &lt;em&gt;0&lt;/em&gt;은 &lt;code&gt;int&lt;/code&gt;이며, &lt;code&gt;print(int)&lt;/code&gt;는 없다. 결국, 표준 변환으로 인해 모호한 일치가 발생한다.&lt;/p&gt;&lt;p&gt;&lt;code&gt;print(3.14159)&lt;/code&gt;는 조금 놀랍다. 모든 리터럴 부동 소수점 값은 &lt;em&gt;f&lt;/em&gt; 접미사가 없으면 &lt;code&gt;double&lt;/code&gt;이 된다. &lt;em&gt;3.14159&lt;/em&gt;는 &lt;code&gt;double&lt;/code&gt;이며 &lt;code&gt;print(double)&lt;/code&gt;은 없다. 따라서 표준 변환을 통해 모호한 일치가 발생한다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;모호한 일치(ambiguous match)&lt;/strong&gt;는 컴파일 타임 오류로 간주된다. 따라서 프로그램을 컴파일하기 전에 모호한 일치를 명확히 해야 한다. 모호한 일치를 해결할 수 있는 몇 가지 방법이 있다.&lt;/p&gt;&lt;p&gt;1) 호출하려고 하는 타입의 매개 변수를 가지는 새로운 오버로드된 함수를 정의한다. 그러면 C++은 정확히 일치하는 함수를 찾을 수 있다.&lt;/p&gt;&lt;p&gt;2) 모호한 인수를 호출할 함수의 타입으로 명시적으로 변환한다. 예를들어, 다음과 같이하면 된다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int x = 0;
print(static_cast&amp;lt;unsigned int&amp;gt;(x)); // will call print(unsigned int)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;3) 인수가 리터럴인 경우 리터럴 접미사를 사용해서 리터럴이 올바른 타입으로 해석되도록 한다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;print(0u); // will call print(unsigned int) since 'u' suffix is unsigned int
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;

&lt;h2 id=&quot;matchingforfunctionswithmultiplearguments&quot;&gt;Matching for functions with multiple arguments&lt;/h2&gt;

&lt;p&gt;인수가 여러 개인 경우 C++은 일치하는 규칙을 차례로 각 인수에 적용한다. &lt;/p&gt;&lt;p&gt;그러한 함수가 발견된 경우, 그 함수는 가장 일치하는 함수다. 그러나 그러한 함수를 찾을 수 없는 경우, 호출은 모호함(또는 불일치)으로 간주된다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;iostream&amp;gt;

void fcn(char c, int x)
{
    std::cout &amp;lt;&amp;lt; 'a';
}

void fcn(char c, double x)
{
    std::cout &amp;lt;&amp;lt; 'b';
}

void fcn(char c, float x)
{
    std::cout &amp;lt;&amp;lt; 'c';
}

int main()
{
    fcn('x', 4);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위의 프로그램에서 모든 함수는 첫 번째 인수와 정확히 일치한다. 그리고 &lt;code&gt;fcn(char, int)&lt;/code&gt;는 두 번째 매개 변수와 정확히 일치하지만 다른 함수는 변환이 필요하다. 그래서 &lt;code&gt;fcn(char, int)&lt;/code&gt;가 가장 일치하는 함수다.&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;Rule: 프로그램을 단순화 하려면 함수 오버로딩을 사용하자.&lt;/p&gt;&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/991113355B790BEB07&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F991113355B790BEB07&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;cpp&quot; filename=&quot;cpp2.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 11pt; color: rgb(140, 140, 140);&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: 11pt; color: rgb(140, 140, 140);&quot;&gt;번역: 이 포스트의 원문은 http://www.learncpp.com/cpp-tutorial/76-function-overloading/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <category>C++ 이야기/08. 함수</category>
      <category>C++</category>
      <category>C++ 함수</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/221</guid>
      <comments>https://boycoding.tistory.com/221#entry221comment</comments>
      <pubDate>Sun, 19 Aug 2018 15:21:02 +0900</pubDate>
    </item>
    <item>
      <title>C++ 08.06 - 인라인 함수 (inline function)</title>
      <link>https://boycoding.tistory.com/220</link>
      <description>&lt;h1 id=&quot;0806inlinefunction&quot;&gt;인라인 함수 (inline function)&lt;/h1&gt;

&lt;p&gt;함수를 사용하면 다음과 같은 많은 이점을 얻을 수 있다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;함수 내부의 코드를 재사용할 수 있다.&lt;/li&gt;

&lt;li&gt;인스턴트 코드보다 함수에서 코드를 변경하거나 업데이트하기가 더 쉽다.&lt;/li&gt;

&lt;li&gt;함수 이름을 통해 코드가 무엇을 의미하는지 이해하기 더 쉽다.&lt;/li&gt;

&lt;li&gt;함수는 함수 호출 인수가 함수 매개 변수와 일치하는지 확인하기 위해 타입 검사를 한다. (매크로는 안한다.)&lt;/li&gt;

&lt;li&gt;함수는 프로그램을 디버그하기 쉽게 만든다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;그러나 함수는 함수가 호출될 때마다 발생하는 일정량의 성능 오버헤드가 있다는 단점이 있다. 이는 CPU가 다른 레지스터와 함께 실행 중인 현재 명령어의 주소를 저장해야 하므로(나중에 반환할 위치를 알 수 있도록) 모든 함수 매개 변수를 생성해야 한다. 할당된 값을 사용하면 프로그램이 새 위치로 분기된다. 내부에서 작성된 코드(인스턴트 코드)가 훨씬 더 빠르다.&lt;/p&gt;&lt;p&gt;크거나 복잡한 태스크를 수행하는 함수의 경우 함수 호출의 오버헤드는 함수가 실행되는 데 걸리는 시간과 비교할 때 중요하지 않다. 그러나 일반적으로 사용하는 작은 함수의 경우, 함수 호출에 필요한 시간이 실제로 함수 코드를 실행하는 데 필요한 시간보다 훨씬 많은 경우가 있다. 이로 인해 상당한 성능 저하가 발생할 수 있다.&lt;/p&gt;&lt;p&gt;C++은 &lt;strong&gt;인라인 함수(inline function)&lt;/strong&gt;라는 내부에서 작성된 코드의 속도와 함수의 장점을 결합하는 방법을 제공한다. &lt;code&gt;inline&lt;/code&gt; 키워드는 컴파일러에서 함수를 인라인 함수로 처리하도록 요청한다. 컴파일러가 코드를 컴파일하면 모든 인라인 함수가 인-플레이스(in-place) 확장된다. 즉, 함수 호출이 함수 자체의 내용 복사본으로 대체되어 함수 오버헤드가 제거된다! 단점은 인라인 함수가 모든 함수 호출에 대해 적절한 위치에서 확장되므로 인라인 함수가 길거나 인라인 함수를 여러 번 호출하는 경우 컴파일된 코드를 약간 더 크게 만들 수 있다는 것이다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int min(int x, int y)
{
    return x &amp;gt; y ? y : x;
}

int main()
{
    std::cout &amp;lt;&amp;lt; min(5, 6) &amp;lt;&amp;lt; '\n';
    std::cout &amp;lt;&amp;lt; min(3, 2) &amp;lt;&amp;lt; '\n';
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 프로그램은 함수 &lt;code&gt;min()&lt;/code&gt;을 두 번 호출하여 함수 호출 오버헤드 패널티를 두 번 발생시킨다. &lt;code&gt;min()&lt;/code&gt; 함수는 짦은 함수이므로 인라인화 하기 좋다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;inline int min(int x, int y)
{
    return x &amp;gt; y ? y : x;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이제 프로그램이 main()을 컴파일하면 main() 다음과 같이 작성된 것처럼 기계 코드를 생성한다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int main()
{
    std::cout &amp;lt;&amp;lt; (5 &amp;gt; 6 ? 6 : 5) &amp;lt;&amp;lt; '\n';
    std::cout &amp;lt;&amp;lt; (3 &amp;gt; 2 ? 2 : 3) &amp;lt;&amp;lt; '\n';
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;결론적으로 더 빠르게 실행된다.&lt;/p&gt;&lt;p&gt;코드를 부풀릴 가능성이 있으므로 함수를 인라인화 하는 것은 내부 루프가 없는 짧은 함수에 가장 적합하다. 또한 &lt;code&gt;inline&lt;/code&gt; 키워드는 권장 사항일 뿐이다. 컴파일러는 인라인에 대한 요청을 자유롭게 무시할 수 있다. (긴 함수를 인라인화 하려고 하면 무시할 가능성이 있다.)&lt;/p&gt;&lt;p&gt;마지막으로, 현대 컴파일러는 자동으로 함수를 인라인화 하는 데 매우 뛰어나다. 함수를 인라인으로 표시하지 않더라도 컴파일러는 성능이 향상될 것으로 생각하는 함수를 인라인화 한다. 따라서 대부분의 경우 &lt;code&gt;inline&lt;/code&gt; 키워드를 사용할 필요가 없다.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Rule: 인라인 함수를 알고 있어야 하지만 최신 컴파일러는 함수를 적절하게 인라인화 하므로 &lt;code&gt;inline&lt;/code&gt; 키워드를 사용할 필요가 없다.&lt;/em&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9987F7485B75743F16&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9987F7485B75743F16&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;cpp&quot; filename=&quot;cpp2.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 11pt; color: rgb(140, 140, 140);&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: 11pt; color: rgb(140, 140, 140);&quot;&gt;번역: 이 포스트의 원문은 http://www.learncpp.com/cpp-tutorial/75-inline-functions/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <category>C++ 이야기/08. 함수</category>
      <category>C++</category>
      <category>C++ 함수</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/220</guid>
      <comments>https://boycoding.tistory.com/220#entry220comment</comments>
      <pubDate>Thu, 16 Aug 2018 21:56:09 +0900</pubDate>
    </item>
    <item>
      <title>C++ 08.05 - 값, 참조 및 주소로 값 반환 (Returning values by value, reference and address)</title>
      <link>https://boycoding.tistory.com/219</link>
      <description>&lt;h1 id=&quot;0805returningvaluesbyvaluereferenceandaddress&quot;&gt;값, 참조 및 주소로 값 반환 (Returning values by value, reference and address)&lt;/h1&gt;

&lt;p&gt;앞의 세 가지 포스트에서 값, 참조 및 주소를 사용해서 함수에 인수를 전달하는 방법을 알아봤다. 이 포스트에서는 앞에서 언급한 세 가지 방법 모두를 사용해서 호출자에게 값을 반환하는 방법을 알아볼 예정이다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;값으로 반환(return by value)&lt;/li&gt;

&lt;li&gt;주소로 반환(return by address)&lt;/li&gt;

&lt;li&gt;참조로 반환(return by reference)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;값, 참조 또는 주소를 사용해서 함수에서 호출자로 값을 반환하는 것은 함수의 매개 변수에 전달하는 것과 거의 똑같은 방식으로 작동한다. 각 방법에 대한 동일한 장단점이 모두 있다. 이 둘의 주요 차이점은 단순히 데이터 흐름과 방향이 바뀌었다는 것이다. 그러나 함수의 지역 변수가 범위를 벗어나 함수가 반환될 때 파괴되므로 각 반환 유형에 대한 영향을 고려해야 한다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;returnbyvalue&quot;&gt;Return by value&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;값으로 반환(return by value)&lt;/strong&gt;은 가장 간단하고 안전하다. 값이 반환되면 복사본이 호출자에게 반환된다. 값으로 전달처럼 리터럴, 변수 및 표현식(Ex. &lt;em&gt;5&lt;/em&gt;, &lt;em&gt;x&lt;/em&gt;, &lt;em&gt;x+1&lt;/em&gt;)을 반환할 수 있다.&lt;/p&gt;&lt;p&gt;값으로 반환의 또 다른 장점은 범위 지정 문제를 걱정할 필요 없다는 것이다. 함수 내에서 선언된 지역 변수를 반환할 수 있다. 함수가 반환되기 전에 지역 변수가 평가되고, 값의 복사본이 호출자에게 반환되기 때문에 함수의 지역 변수가 함수의 끝에서 범위를 벗어날 때 아무런 문제가 없다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int doubleValue(int x)
{
    int value = x * 2;

    return value; // A copy of value will be returned here
} // value goes out of scope here
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;값으로 반환은 함수 내에서 선언된 (지역)변수를 반환하거나 값으로 전달된 매개 변수를 반환할 때 가장 적절하다. 그러나 값으로 전달과 마찬가지로 구조체 및 클래스의 경우 값으로 반환은 느리다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;값으로 반환을 사용해야 하는 경우:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;함수 내에서 선언된 (지역)변수를 반환할 때&lt;/li&gt;

&lt;li&gt;값으로 전달된 매개 변수를 반환할 때&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;값으로 반환을 사용해야 하지 않아야 하는 경우:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;배열이나 포인터를 반환할 때 (주소로 반환 사용)&lt;/li&gt;

&lt;li&gt;구조체 또는 클래스를 반환할 때 (참조로 반환 사용)&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;

&lt;h2 id=&quot;returnbyaddress&quot;&gt;Return by address&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;주소로 반환(return by address)&lt;/strong&gt;은 호출자에게 변수의 주소를 반환한다.&lt;/p&gt;&lt;p&gt;그러나 함수 안에서 선언된 지역 변수의 주소를 반환하려고 하면 프로그램에서 정의되지 않은 동작이 발생한다. 다음 예제를 살펴보자.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int* doubleValue(int x)
{
    int value = x * 2;

    return &amp;amp;value; // return value by address here
} // value destroyed here
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위에서 볼 수 있듯이, 주소가 호출자에게 반환된 직후 &lt;em&gt;value&lt;/em&gt;가 소멸한다. 최종적으로 할당되지 않은 메모리(dangling pointer)이기 때문에, 호출자가 이것을 사용하면 문제가 발생할 수 있다. 이것은 초보 프로그래머가 자주 범하는 실수다. 반환하는 주소가 유효한 변수가 되도록 하는 것이 중요하다.&lt;/p&gt;&lt;p&gt;주소로 반환은 동적 할당된 메모리를 호출자에게 반환하는 데도 사용한다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int* allocateArray(int size)
{
    return new int[size];
}

int main()
{
    int* array = allocateArray(25);

    // do stuff with array

    delete[] array;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;주소로 반환을 사용해야 하는 경우:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;동적 할당된 메모리를 반환할 때&lt;/li&gt;

&lt;li&gt;주소로 전달된 매개 변수를 반환할 때&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;주소로 반환을 사용해야 하지 않아야 하는 경우:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;함수 내에서 선언된 (지역)변수를 반환할 때 (값으로 반환 사용)&lt;/li&gt;

&lt;li&gt;참조로 전달된 구조체 또는 클래스를 반환할 때 (참조로 반환 사용)&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;

&lt;h2 id=&quot;returnbyreference&quot;&gt;Return by reference&lt;/h2&gt;

&lt;p&gt;변수가 &lt;strong&gt;참조로 반환(return by reference)&lt;/strong&gt;되면 변수에 대한 참조가 호출자에게 반환된다. 그런 다음 호출자는 이 참조를 사용해서 변수를 계속 수정할 수 있다. 참조로 반환은 빠르므로 구조체와 클래스를 반환할 때 유용하다.&lt;/p&gt;&lt;p&gt;그러나 주소로 반환처럼 참조로 함수 지역 변수를 반환하면 안 된다. 다음 예제를 보자.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int&amp;amp; doubleValue(int x)
{
    int value = x * 2;

    return value; // return a reference to value here
} // value is destroyed here
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;위 프로그램은 함수가 반환될 때 소멸할 &lt;em&gt;value&lt;/em&gt;에 대한 참조를 반환한다. 이것은 호출자가 쓰레기에 대한 참조를 받는다는 것을 의미한다. 다행히도, 컴파일러는 이것을 시도하면 경고나 에러를 뱉을 것이다.&lt;/p&gt;&lt;p&gt;참조로 반환은 호출자에게 참조로 전달된 매개 변수를 반환하는 데도 사용한다. 다음 예제에서는 참조로 함수에 전달된 배열의 요소를 참조로 반환한다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;array&amp;gt;
#include &amp;lt;iostream&amp;gt;

// Returns a reference to the index element of array
int&amp;amp; getElement(std::array&amp;lt;int, 25&amp;gt;&amp;amp; array, int index)
{
    // we know that array[index] will not be destroyed when we return to the caller (since the caller passed in the array in the first place!)
    // so it's okay to return it by reference
    return array[index];
}

int main()
{
    std::array&amp;lt;int, 25&amp;gt; array;

    // Set the element of array with index 10 to the value 5
    getElement(array, 10) = 5;

    std::cout &amp;lt;&amp;lt; array[10] &amp;lt;&amp;lt; '\n';

    return 0;
}

// 5
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;getElement(array, 10)&lt;/em&gt;을 호출하면 인덱스 10인 &lt;em&gt;array&lt;/em&gt;에 대한 참조를 반환한다. &lt;em&gt;main()&lt;/em&gt; 함수에서는 이 참조를 사용해서 해당 요소에 값 &lt;em&gt;5&lt;/em&gt;를 할당한다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;참조로 반환을 사용해야 하는 경우:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;참조 매개 변수를 반환할 때&lt;/li&gt;

&lt;li&gt;함수에 전달된 배열의 요소를 반환할 때&lt;/li&gt;

&lt;li&gt;함수의 끝에서 소멸하지 않는 구조체나 클래스를 반환할 때&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;참조로 반환을 사용해야 하지 않아야 하는 경우:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;함수 내에서 선언된 (지역)변수를 반환할 때 (값으로 반환 사용)&lt;/li&gt;

&lt;li&gt;기본 배열이나 포인터 값을 반환할 때 (주소로 반환 사용)&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9941FA4A5B66CC6C32&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9941FA4A5B66CC6C32&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;cpp&quot; filename=&quot;cpp2.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;color: rgb(140, 140, 140); font-size: 11pt;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: rgb(140, 140, 140); font-size: 11pt;&quot;&gt;번역: 이 포스트의 원문은 http://www.learncpp.com/cpp-tutorial/74a-returning-values-by-value-reference-and-address/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <category>C++ 이야기/08. 함수</category>
      <category>C++</category>
      <category>C++ 함수</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/219</guid>
      <comments>https://boycoding.tistory.com/219#entry219comment</comments>
      <pubDate>Sun, 5 Aug 2018 19:08:28 +0900</pubDate>
    </item>
    <item>
      <title>C++ 08.04 - 주소로 전달 (Pass by address)</title>
      <link>https://boycoding.tistory.com/218</link>
      <description>&lt;h1 id=&quot;0804passbyaddress&quot;&gt;주소로 전달 (Pass by address)&lt;/h1&gt;

&lt;p&gt;함수에 변수를 전달할 수 있는 또 다른 방법이 있는데, 주소를 사용하는 것이다. 인수가 주소이기 때문에 함수 매개 변수는 포인터다. 함수는 가리키는 값에 접근하거나 변경하기 위해 포인터를 역참조할 수 있다.&lt;/p&gt;&lt;p&gt;다음은 주소로 전달된 매개 변수를 사용하는 함수의 예제다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;iostream&amp;gt;

void foo(int* ptr)
{
    *ptr = 6;
}

int main()
{
    int value = 5;

    std::cout &amp;lt;&amp;lt; &quot;value = &quot; &amp;lt;&amp;lt; value &amp;lt;&amp;lt; '\n';
    foo(&amp;amp;value);
    std::cout &amp;lt;&amp;lt; &quot;value = &quot; &amp;lt;&amp;lt; value &amp;lt;&amp;lt; '\n';
    return 0;
}
/*
value = 5
value = 6
*/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;보시다시피, 함수 &lt;em&gt;foo()&lt;/em&gt;는 포인터 매개 변수 ptr을 통해 인수의 값을 변경한다.&lt;/p&gt;&lt;p&gt;주소로 전달(Pass by address)은 주로 배열과 함께 사용한다. 예를 들어, 다음 함수는 배열의 모든 값을 출력한다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void printArray(int* array, int length)
{
    for (int index=0; index &amp;lt; length; ++index)
        std::cout &amp;lt;&amp;lt; array[index] &amp;lt;&amp;lt; ' ';
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;다음은 위 함수를 호출하는 예제 프로그램이다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;int main()
{
    int array[6] = { 6, 5, 4, 3, 2, 1 }; // remember, arrays decay into pointers
    printArray(array, 6); // so array evaluates to a pointer to the first element of the array here, no &amp;amp; needed
}

// 6 5 4 3 2 1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;배열이 함수에 전달될 때 포인터로 변환되므로 길이를 별도의 매개 변수로 전달해야 한다는 것을 기억하자.&lt;/p&gt;&lt;p&gt;주소를 통해 전달된 매개 변수를 역참조하기 전에 null 포인터인지 항상 확인하는 게 좋다. null 포인터를 역참조하면 프로그램이 중단된다. 다음은 null 포인터 검사를 수행하는 &lt;em&gt;printArray()&lt;/em&gt; 함수다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void printArray(int* array, int length)
{
    // if user passed in a null pointer for array, bail out early!
    if (!array)
        return;

    for (int index=0; index &amp;lt; length; ++index)
        cout &amp;lt;&amp;lt; array[index] &amp;lt;&amp;lt; ' ';
}

int main()
{
    int array[6] = { 6, 5, 4, 3, 2, 1 };
    printArray(array, 6);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;

&lt;h2 id=&quot;constpassingbyconstaddress&quot;&gt;const 주소로 전달 (Passing by const address)&lt;/h2&gt;

&lt;p&gt;위 예제에서 &lt;em&gt;printArray()&lt;/em&gt;는 함수 내에서 인수를 수정하지 않기 때문에 배열 매개 변수를 const로 만드는 것이 좋다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void printArray(const int* array, int length)
{
    // if user passed in a null pointer for array, bail out early!
    if (!array)
        return;

    for (int index=0; index &amp;lt; length; ++index)
        std::cout &amp;lt;&amp;lt; array[index] &amp;lt;&amp;lt; ' ';
}

int main()
{
    int array[6] = { 6, 5, 4, 3, 2, 1 };
    printArray(array, 6);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이것은 &lt;em&gt;printArray()&lt;/em&gt;는 전달된 배열 인수를 수정하지 않는다는 것을 한눈에 알 수 있게 해준다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;addressesareactuallypassedbyvalue&quot;&gt;주소는 실제로 값에 의해 전달된다. (Addresses are actually passed by value)&lt;/h2&gt;

&lt;p&gt;함수에 포인터를 전달하면 포인터의 값(포인터가 가리키는 주소)이 인수에서 함수의 매개 변수로 복사된다. 다시 말해서, 값으로 전달되었다! 함수 매개 변수의 값을 변경하면 복사본만 변경된다. 따라서 원래 포인터 인수는 변경되지 않는다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;iostream&amp;gt;

void setToNull(int* tempPtr)
{
    // we're making tempPtr point at something else, not changing the value that tempPtr points to.
    tempPtr = nullptr; // use 0 instead if not C++11
}

int main()
{ 
    // First we set ptr to the address of five, which means *ptr = 5
    int five = 5;
    int* ptr = &amp;amp;five;

    // This will print 5
    std::cout &amp;lt;&amp;lt; *ptr &amp;lt;&amp;lt; std::endl;

    // tempPtr will receive a copy of ptr
    setToNull(ptr);

    // ptr is still set to the address of five!

    // This will print 5
    if (ptr)
        std::cout &amp;lt;&amp;lt; *ptr;
    else
        std::cout &amp;lt;&amp;lt; &quot; ptr is null&quot;;

    return 0;
}
// 5
// 5
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;매개 변수 tempPtr은 ptr이 보유하고 있는 주소의 복사본을 전달받는다. tempPtr을 다른 값(Ex. nullptr)을 가리키도록 변경하더라도 ptr이 가리키는 값은 변경되지 않는다.&lt;/p&gt;&lt;p&gt;주소 자체가 값에 의한 전달이 되더라도 여전히 그 주소를 역참조하여 인수의 값을 변경할 수 있다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;인수를 주소로 전달할 때 함수 매개 변수는 인수에서 주소의 복사본을 받는다. 이 시점에서 함수 매개 변수와 인수는 모두 같은 값을 가리킨다.&lt;/li&gt;

&lt;li&gt;함수 매개 변수가 가리키는 값을 변경하기 위해 역참조 하면 함수 매개 변수와 인수가 모두 같은 값을 가리키기 때문에 인수가 가리키는 값에 영향을 준다.&lt;/li&gt;

&lt;li&gt;함수 매개 변수가 다른 주소로 지정되면 함수 매개 변수가 복사본이기 때문에 인수에 영향을 미치지 않으므로 복사본을 변경해도 원본에 영향을 미치지 않는다. 함수 매개 변수의 주소를 변경한 후에는 함수 매개 변수와 인수가 다른 값을 가리키므로 역참조하더라도 더는 인수가 가리키는 값에 영향을 미치지 않는다.&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;iostream&amp;gt;

void setToSix(int* tempPtr)
{
    *tempPtr = 6; // we're changing the value that tempPtr (and ptr) points to
}

int main()
{ 
    // First we set ptr to the address of five, which means *ptr = 5
    int five = 5;
    int* ptr = &amp;amp;five;

    // This will print 5
    std::cout &amp;lt;&amp;lt; *ptr &amp;lt;&amp;lt; std::endl;

    // tempPtr will receive a copy of ptr
    setToSix(ptr);

    // tempPtr changed the value being pointed to to 6, so ptr is now pointing to the value 6

    // This will print 6
    if (ptr)
        std::cout &amp;lt;&amp;lt; *ptr;
    else
        std::cout &amp;lt;&amp;lt; &quot; ptr is null&quot;;

    return 0;
}
// 5
// 6
&lt;/code&gt;&lt;/pre&gt;

&lt;hr&gt;

&lt;h2 id=&quot;passingaddressesbyreference&quot;&gt;참조로 주소 전달하기 (Passing addresses by reference)&lt;/h2&gt;

&lt;p&gt;그러면 &quot;함수 내에서 인수가 가리키는 주소를 변경하려면 어떻게 해야 할까?&quot; 라는 의문이 들 수 있다. 간단하게 참조로 주소를 전달해서 해결할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;iostream&amp;gt;

// tempPtr is now a reference to a pointer, so any changes made to tempPtr will change the argument as well!
void setToNull(int*&amp;amp; tempPtr)
{
    tempPtr = nullptr; // use 0 instead if not C++11
}

int main()
{ 
    // First we set ptr to the address of five, which means *ptr = 5
    int five = 5;
    int* ptr = &amp;amp;five;

    // This will print 5
    std::cout &amp;lt;&amp;lt; *ptr &amp;lt;&amp;lt; std::endl;

    // tempPtr is set as a reference to ptr
    setToNull(ptr);

    // ptr has now been changed to nullptr!

    if (ptr)
        std::cout &amp;lt;&amp;lt; *ptr;
    else
        std::cout &amp;lt;&amp;lt; &quot; ptr is null&quot;;

    return 0;
}

// 5
// ptr is null
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;setToNull(ptr)&lt;/em&gt; 호출이 &lt;em&gt;ptr&lt;/em&gt;의 값을 &lt;em&gt;&amp;amp;five&lt;/em&gt;에서 nullptr로 실제로 변경했음을 보여준다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;prosandconsofpassbyaddress&quot;&gt;주소로 전달의 장단점 (Pros and cons of pass by address)&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;주소로 전달의 장점:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;주소로 전달은 함수가 인수 값을 변경할 수 있으므로 유용하다.&lt;/li&gt;

&lt;li&gt;인수의 복사본이 만들어지지 않으므로 구조체 또는 클래스와 함께 사용하는 경우에 빠르다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;주소로 전달의 단점:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;리터럴과 표현식은 주소가 없으므로 사용할 수 없다.&lt;/li&gt;

&lt;li&gt;모든 값은 null인지 확인해야 한다. null 값을 역참조하려고 하면 충돌이 발생한다.&lt;/li&gt;

&lt;li&gt;포인터 역참조는 값에 직접 접근하는 것보다 더 느리다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;주소로 전달을 사용해야 하는 경우:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;내장 배열을 전달할 때&lt;/li&gt;

&lt;li&gt;포인터를 전달할 때&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;주소로 전달을 사용하지 않아야 하는 경우:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;구조체 또는 클래스를 전달할 때 (참조로 전달 사용)&lt;/li&gt;

&lt;li&gt;기본 자료형의 값을 전달할 때 (값으로 전달 사용)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;주소로 전달과 참조로 전달은 거의 같은 장단점이 있다. 그러나 참조로 전달하는 것이 주소로 전달하는 것보다 더 안전하므로 대부분의 경우 참조로 전달하는 게 좋다.&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/996B9D3C5B65E17B05&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F996B9D3C5B65E17B05&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;cpp&quot; filename=&quot;cpp2.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 11pt; color: rgb(140, 140, 140);&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: 11pt; color: rgb(140, 140, 140);&quot;&gt;번역: 이 포스트의 원문은 http://www.learncpp.com/cpp-tutorial/74-passing-arguments-by-address/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <category>C++ 이야기/08. 함수</category>
      <category>C++</category>
      <category>C++ 함수</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/218</guid>
      <comments>https://boycoding.tistory.com/218#entry218comment</comments>
      <pubDate>Sun, 5 Aug 2018 02:25:51 +0900</pubDate>
    </item>
    <item>
      <title>C++ 08.03 - 참조로 전달 (Pass by reference)</title>
      <link>https://boycoding.tistory.com/217</link>
      <description>&lt;h1 id=&quot;0803passbyreference&quot;&gt;08.03 - 참조로 전달 (Pass by reference)&lt;/h1&gt;

&lt;p&gt;값으로 전달은 두 가지 한계가 있다. 첫째, 큰 구조체 또는 클래스를 함수에 전달할 때 값으로 전달은 인수의 복사본을 함수 매개 변수로 만든다. 이 경우 복사하는데 큰 비용이 들어 성능이 저하될 수 있다. 둘째, 값으로 인수를 전달할 경우 함수에서 호출자에게 값을 반환하는 유일한 방법은 함수의 반환 값을 사용하는 것이다. 이 방법도 좋지만, 함수에서 인수를 수정하는 것이 더 명확하고 효율적일 수 있다. 참조를 통해 위 두 가지 문제를 해결할 수 있다.&lt;/p&gt;
&lt;p&gt;변수를 참조로 전달하려면 함수 매개 변수를 일반 변수가 아닌 참조로 선언해야 한다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void addOne(int&amp;amp; y) // y is a reference variable
{
    y = y + 1;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;함수가 호출되면 &lt;em&gt;y&lt;/em&gt;는 인수에 대한 참조가 된다. 변수에 대한 참조는 변수 자체와 똑같이 취급되므로 참조에 대한 모든 변경 사항은 인수에도 적용된다.&lt;/p&gt;
&lt;p&gt;다음 예제는 위 내용을 보여준다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void foo(int&amp;amp; value)
{
    value = 6;
}

int main()
{
    int value = 5;

    cout &amp;lt;&amp;lt; &quot;value = &quot; &amp;lt;&amp;lt; value &amp;lt;&amp;lt; '\n';
    foo(value);
    cout &amp;lt;&amp;lt; &quot;value = &quot; &amp;lt;&amp;lt; value &amp;lt;&amp;lt; '\n';
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;프로그램에서 &lt;em&gt;foo()&lt;/em&gt; 함수의 매개 변수가 일반 변수 대신 참조가 된다는 점을 제외하고는 이전 포스트 '값으로 전달' 예제에서 사용한 것과 똑같다. &lt;em&gt;foo(x)&lt;/em&gt;를 호출하면 &lt;em&gt;y&lt;/em&gt;는 &lt;em&gt;x&lt;/em&gt;에 대한 참조가 된다. 그래서 다음과 같이 출력한다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;value = 5
value = 6
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;보시다시피 함수는 인수의 값을 &lt;em&gt;5&lt;/em&gt;에서 &lt;em&gt;6&lt;/em&gt;으로 변경했다!&lt;/p&gt;
&lt;p&gt;여기 또 다른 예제가 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void addOne(int&amp;amp; y) // y is a reference variable
{
    y = y + 1;
} // y is destroyed here

int main()
{
    int x = 5;
    std::cout &amp;lt;&amp;lt; &quot;x = &quot; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; '\n';
    addOne(x);
    std::cout &amp;lt;&amp;lt; &quot;x = &quot; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; '\n';
    return 0;
}

/*
x = 5
x = 6
*/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;인수 x의 값은 함수에 의해 변경되었다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;constpassbyconstreference&quot;&gt;const 참조로 전달 (Pass by const reference)&lt;/h2&gt;

&lt;p&gt;참조를 사용하면 함수가 인수의 값을 변경할 수 있으므로 인수가 읽기 전용일 때는 사용하기에 바람직하지 않다. 함수가 인수의 값을 변경해서는 안 되지만 값으로 전달하지 않으려면 &lt;code&gt;const&lt;/code&gt; 참조를 전달하는 것이 가장 좋다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;const 참조는 변수가 참조를 통해 변경되는 것을 허용하지 않는 참조&lt;/strong&gt;다. 그러므로 const 참조를 매개 변수로 사용하면 함수가 인수를 변경하지 않는다는 것을 호출자에게 보장한다!&lt;/p&gt;
&lt;p&gt;다음 함수는 컴파일러 오류를 생성한다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void foo(const int&amp;amp; x) // x is a const reference
{
    x = 6;  // compile error: const 참조는 값을 변경할 수 없다.
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;const 참조를 사용하는 것은 다음과 같은 이유로 유용하다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;컴파일러는 변경해서 안 되는 값을 변경하지 않도록 한다. (위의 예제처럼 시도할 경우 오류가 발생한다.)&lt;/li&gt;

&lt;li&gt;함수가 인수의 값을 변경하지 않는다는 것을 프로그래머에게 알려준다. (디버깅에 도움이 될 수 있다.)&lt;/li&gt;

&lt;li&gt;const가 아닌 참조 매개 변수에는 const 인수를 전달할 수 없다. const 매개 변수를 사용하면 non-const 및 const 인수를 함수에 전달할 수 있다.&lt;/li&gt;

&lt;li&gt;const 참조는 l-value, const l-value 및 r-value를 포함한 모든 유형의 인수를 허용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;규칙: 참조로 인수를 전달할 때 인수 값을 변경해야 하는 경우가 아니면 항상 const 참조를 사용하자.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr&gt;

&lt;h2 id=&quot;referencestopointers&quot;&gt;포인터에 대한 참조 (References to pointers)&lt;/h2&gt;

&lt;p&gt;포인터를 참조로 전달하고 함수가 포인터의 주소를 완전히 변경하도록 할 수 있다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;iostream&amp;gt;

void foo(int*&amp;amp; ptr) // pass pointer by reference
{
    ptr = nullptr; // 실제 ptr 인수를 변경할 수 있다.
}

int main()
{
    int x = 5;
    int *ptr = &amp;amp;x;
    std::cout &amp;lt;&amp;lt; &quot;ptr is: &quot; &amp;lt;&amp;lt; (ptr ? &quot;non-null&quot; : &quot;null&quot;) &amp;lt;&amp;lt; '\n'; // prints non-null
    foo(ptr);
    std::cout &amp;lt;&amp;lt; &quot;ptr is: &quot; &amp;lt;&amp;lt; (ptr ? &quot;non-null&quot; : &quot;null&quot;) &amp;lt;&amp;lt; '\n'; // prints null

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;참조를 이용해서 C 스타일 배열을 주소별로 전달할 수 있다. 함수가 배열을 변경하거나 고정 배열의 배열 정보에 접근해야 하는 경우 유용하다. 그러나 이 작업을 수행하려면 매개 변수에서 배열 크기를 명시적으로 정의해야 한다.&lt;/p&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;iostream&amp;gt;

// Note: You need to specify the array size in the function declaration
void printElements(int (&amp;amp;arr)[4])
{
  int length{ sizeof(arr) / sizeof(arr[0]) }; // we can now do this since the array won't decay

  for (int i{ 0 }; i &amp;lt; length; ++i)
  {
    std::cout &amp;lt;&amp;lt; arr[i] &amp;lt;&amp;lt; std::endl;
  }
}

int main()
{
    int arr[]{ 99, 20, 14, 80 };

    printElements(arr);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/blockquote&gt;

&lt;hr&gt;

&lt;h2 id=&quot;prosandconsofpassbyreference&quot;&gt;참조로 전달의 장단점 (Pros and cons of pass by reference)&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;참조로 전달의 장점:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;참조를 사용하면 함수가 인수의 값을 변경할 수 있다. 또한 const 참조를 사용해서 함수가 인수를 변경하지 않는다는 것을 보장할 수 있다.&lt;/li&gt;

&lt;li&gt;인수의 복사본이 만들어지지 않으므로 큰 구조체나 클래스와 함께 사용하는 경우에도 전달이 빠르다.&lt;/li&gt;

&lt;li&gt;참조는 함수에서 매개 변수를 통해 여러 값을 반환할 수 있다.&lt;/li&gt;

&lt;li&gt;참조는 초기화되어야 하므로 null 값에 대해 걱정할 필요가 없다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;참조로 전달의 단점:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;non-const 참조는 const 값 또는 r-value(리터럴 또는 표현식)으로 초기화할 수 없으므로 참조 매개 변수에 대한 인수는 일반 변수여야 한다.&lt;/li&gt;

&lt;li&gt;인수가 변경될 수 있는지는 함수 호출에서 알 수 없다. 값으로 전달이나 참조로 전달 모두 인수에서는 동일하게 보인다. 함수 선언을 보고 인수가 값으로 전달인지 참조로 전달인지 알 수 있다. 이것은 프로그래머가 함수에서 인수의 값을 변경한다는 것을 인식하지 못하는 상황을 초래할 수 있다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;참조로 전달을 사용해야 하는 경우:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;구조체 또는 클래스를 전달할 때 (읽기 전용인 경우 const 사용)&lt;/li&gt;

&lt;li&gt;인수를 수정하는 함수가 필요할 때&lt;/li&gt;

&lt;li&gt;고정 배열의 유형 정보에 접근해야 할 때&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;참조로 전달을 사용하지 않아야 하는 경우:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;수정할 필요가 없는 기본 자료형을 전달할 때 (값으로 전달 사용)&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9936ED355B65CBCB0E&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9936ED355B65CBCB0E&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;cpp&quot; filename=&quot;cpp2.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: rgb(140, 140, 140); font-size: 11pt;&quot;&gt;번역: 이 포스트의 원문은 http://www.learncpp.com/cpp-tutorial/73-passing-arguments-by-reference/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <category>C++ 이야기/08. 함수</category>
      <category>C++</category>
      <category>C++ 함수</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/217</guid>
      <comments>https://boycoding.tistory.com/217#entry217comment</comments>
      <pubDate>Sat, 4 Aug 2018 22:38:31 +0900</pubDate>
    </item>
    <item>
      <title>C++ 08.02 - 값으로 전달 (Pass by value)</title>
      <link>https://boycoding.tistory.com/216</link>
      <description>&lt;h1 id=&quot;0802passbyvalue&quot;&gt;값으로 전달 (Pass by value)&lt;/h1&gt;

&lt;p&gt;C++에서 포인터가 아닌 인수는 값으로 전달된다. 인수가 값으로 전달되면 인수의 값은 해당 함수 매개 변수의 값으로 복사된다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;iostream&amp;gt;

void foo(int y)
{
    std::cout &amp;lt;&amp;lt; &quot;y = &quot; &amp;lt;&amp;lt; y &amp;lt;&amp;lt; '\n';
}

int main()
{
    foo(5);   // first call

    int x = 6;
    foo(x);   // second call
    foo(x+1); // third call

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;첫 번째 &lt;em&gt;foo(5)&lt;/em&gt; 호출에서 인수는 리터럴 &lt;em&gt;5&lt;/em&gt;다. &lt;em&gt;foo(5)&lt;/em&gt;가 호출되면 변수 &lt;em&gt;y&lt;/em&gt;가 만들어지고 값 &lt;em&gt;5&lt;/em&gt;가 &lt;em&gt;y&lt;/em&gt;로 복사된다. 변수 &lt;em&gt;y&lt;/em&gt;는 &lt;em&gt;foo()&lt;/em&gt; 함수가 종료되면 소멸한다.&lt;/p&gt;
&lt;p&gt;두 번째 &lt;em&gt;foo(x)&lt;/em&gt; 호출에서 인수는 변수 &lt;em&gt;x&lt;/em&gt;다. &lt;em&gt;x&lt;/em&gt;는 값 &lt;em&gt;6&lt;/em&gt;으로 평가된다. 두 번째 호출에서 변수 &lt;em&gt;y&lt;/em&gt;가 다시 만들어지고 값 &lt;em&gt;6&lt;/em&gt;이 복사된다. 마찬가지로 &lt;em&gt;foo()&lt;/em&gt; 함수가 종료되면 소멸한다.&lt;/p&gt;
&lt;p&gt;세 번째 &lt;em&gt;foo(x +1)&lt;/em&gt; 호출에서 인수는 값 &lt;em&gt;7&lt;/em&gt;로 평가되며, 변수 &lt;em&gt;y&lt;/em&gt;로 복사되고 &lt;em&gt;foo()&lt;/em&gt; 함수가 끝나면 소멸한다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// 따라서 위 프로그램은 다음과 같이 출력한다.
y = 5
y = 6
y = 7
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;인수의 복사본이 함수로 전달되므로 원래 인수는 함수 안에서 수정할 수 없다.&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;#include &amp;lt;iostream&amp;gt;

void foo(int y)
{
    std::cout &amp;lt;&amp;lt; &quot;y = &quot; &amp;lt;&amp;lt; y &amp;lt;&amp;lt; '\n';

    y = 6;

    std::cout &amp;lt;&amp;lt; &quot;y = &quot; &amp;lt;&amp;lt; y &amp;lt;&amp;lt; '\n';
} // y is destroyed here

int main()
{
    int x = 5;
    std::cout &amp;lt;&amp;lt; &quot;x = &quot; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; '\n';

    foo(x);

    std::cout &amp;lt;&amp;lt; &quot;x = &quot; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; '\n';
    return 0;
}

/*
x = 5
y = 5
y = 6
x = 5
*/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;main&lt;/em&gt; 함수의 시작 부분에서 변수 &lt;em&gt;x&lt;/em&gt;의 값은 &lt;em&gt;5&lt;/em&gt;다. &lt;em&gt;foo(x)&lt;/em&gt;가 호출되면 &lt;em&gt;foo&lt;/em&gt;의 매개 변수 &lt;em&gt;y&lt;/em&gt;에 값 &lt;em&gt;5&lt;/em&gt;가 복사되어 전달된다. &lt;em&gt;foo()&lt;/em&gt; 함수 내부에서 &lt;em&gt;y&lt;/em&gt;의 값은 &lt;em&gt;6&lt;/em&gt;으로 변경되고 소멸하지만, &lt;em&gt;y&lt;/em&gt;가 변경되더라도 변수 &lt;em&gt;x&lt;/em&gt;의 값은 아무런 영향을 받지 않는다. (변경되지 않는다)&lt;/p&gt;
&lt;p&gt;값으로 전달된 함수 매개 변수도 &lt;code&gt;const&lt;/code&gt;로 만들 수 있다. 함수가 매개 변수의 값을 변경하지 못하게 할 때 유용하다.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&quot;prosandconsofpassbyvalue&quot;&gt;값으로 전달의 장단점 (Pros and cons of pass by value)&lt;/h2&gt;

&lt;p&gt;&lt;b&gt;값으로 전달의 장점:&lt;/b&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;호출되는 함수에 의해 인수가 변경되지 않으므로 예기치 못한 부작용이 발생하지 않는다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;b&gt;값으로 전달의 단점:&lt;/b&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;함수를 여러 번 호출하는 경우 구조체(struct) 및 클래스(class)를 복사하는데 큰 비용이 들어 성능이 저하될 수 있다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;b&gt;값으로 전달을 사용해야 하는 경우:&lt;/b&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;기본 자료형과 열거자를 전달할 때(함수가 인수를 변경할 필요가 없을 때)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;b&gt;값으로 전달을 사용하지 않아야 하는 경우:&lt;/b&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;배열(array), 구조체(struct) 및 클래스(class)를 전달할 때&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;값에 의한 전달은 함수가 인수를 변경할 필요가 없을 때 기본 자료형 변수를 전달하는 가장 좋은 방법이다.&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/990F163A5B65CB942B&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F990F163A5B65CB942B&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;cpp&quot; filename=&quot;cpp2.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: rgb(140, 140, 140); font-size: 11pt;&quot;&gt;번역: 이 포스트의 원문은 http://www.learncpp.com/cpp-tutorial/72-passing-arguments-by-value/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <category>C++ 이야기/08. 함수</category>
      <category>C++</category>
      <category>C++ 함수</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/216</guid>
      <comments>https://boycoding.tistory.com/216#entry216comment</comments>
      <pubDate>Sat, 4 Aug 2018 20:06:01 +0900</pubDate>
    </item>
    <item>
      <title>C++ 08.01 - 함수 매개 변수와 인수 (Function parameters and arguments)</title>
      <link>https://boycoding.tistory.com/215</link>
      <description>&lt;h1 id=&quot;0801functionparametersandarguments&quot;&gt;함수 매개 변수와 인수 (Function parameters and arguments)&lt;/h1&gt;

&lt;p&gt;정의를 다시 살펴보겠다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;매개 변수(function parameter)&lt;/strong&gt;는 함수에서 전달받은 인수를 함수 내부로 전달하기 위해&amp;nbsp;사용하는 변수를 의미한다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;즉, 매개 변수는 함수 선언에 선언된 변수다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void foo(int x); // 선언(함수 원형) -- x는 매개 변수(parameter)

void foo(int x)  // 정의(또는 선언) -- x는 매개 변수(parameter) 
{
}
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;인수(argument)&lt;/strong&gt;는 호출자가 함수에 전달한 값이다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;foo(6); // 6은 매개 변수 x에 전달되는 인수
foo(y+1); // y+1의 값은 매개 변수 x에 전달되는 인수
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;함수가 호출되면 함수의 모든 매개 변수가 변수로 생성되고, 인수의 값은 매개 변수로 복사된다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void foo(int x, int y)
{
}

foo(6, 7);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;인수 &lt;em&gt;6&lt;/em&gt;과 &lt;em&gt;7&lt;/em&gt;을 사용하여 &lt;code&gt;foo(6, 7)&lt;/code&gt; 함수를 호출하면 &lt;em&gt;foo&lt;/em&gt;의 매개 변수 &lt;em&gt;x&lt;/em&gt;가 생성된 후 값 &lt;em&gt;6&lt;/em&gt;이 할당되고, 매개 변수 &lt;em&gt;y&lt;/em&gt;가 생성된 후 값 &lt;em&gt;7&lt;/em&gt;이 할당된다.&lt;/p&gt;&lt;p&gt;함수 블록 내에서 매개 변수가 선언되지 않았지만, 함수 매개 변수는 &lt;strong&gt;지역 범위(local scope)&lt;/strong&gt;를 가지고 있다. 이것은 함수가 호출될 때 생성되며, 함수 블록이 끝나면 소멸한다는 의미다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;cpp language-cpp&quot;&gt;void foo(int x, int y) // 여기서 x와 y가 생성된다.
{
} // x와 y는 여기서 소멸된다.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;함수에 인수를 전달하는 3가지 방법이 있다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;값으로 전달(pass by value)&lt;/li&gt;

&lt;li&gt;참조로 전달(pass by reference)&lt;/li&gt;

&lt;li&gt;주소로 전달(pass by address)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;다음 포스트에서 위 3가지 방법을 살펴볼 예정이다.&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 50px; width: 50px; height: 50px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99141F3B5B62DD5135&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99141F3B5B62DD5135&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;cpp&quot; filename=&quot;cpp2.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 50px; height: 50px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 11pt; color: rgb(140, 140, 140);&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: 11pt; color: rgb(140, 140, 140);&quot;&gt;번역: 이 포스트의 원문은 http://www.learncpp.com/cpp-tutorial/71-function-parameters-and-arguments/ 입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <category>C++ 이야기/08. 함수</category>
      <category>C++</category>
      <category>C++ 함수</category>
      <author>소년코딩</author>
      <guid isPermaLink="true">https://boycoding.tistory.com/215</guid>
      <comments>https://boycoding.tistory.com/215#entry215comment</comments>
      <pubDate>Thu, 2 Aug 2018 19:31:21 +0900</pubDate>
    </item>
  </channel>
</rss>