<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://sonsuzus.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://sonsuzus.github.io/" rel="alternate" type="text/html" /><updated>2026-08-09T07:13:20+00:00</updated><id>https://sonsuzus.github.io/feed.xml</id><title type="html">SonsuzUs</title><subtitle>Programlama ve Yazılım</subtitle><author><name>Sonsuz Us</name></author><entry><title type="html">C ile Basit Bir Mark-and-Sweep Garbage Collector Yazalım</title><link href="https://sonsuzus.github.io/posts/c-ile-basit-bir-mark-and-sweep-garbage-collector-yazalim/" rel="alternate" type="text/html" title="C ile Basit Bir Mark-and-Sweep Garbage Collector Yazalım" /><published>2026-08-09T00:00:00+00:00</published><updated>2026-08-09T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/c-ile-basit-bir-mark-and-sweep-garbage-collector-yazalim</id><content type="html" xml:base="https://sonsuzus.github.io/posts/c-ile-basit-bir-mark-and-sweep-garbage-collector-yazalim/"><![CDATA[<p>Bellek yönetimi, programlamanın “ışıkları kapattım mı?” sorusudur: Bir nesneyi ayırdığımızı hatırlarız ama serbest bırakıp bırakmadığımızdan asla tam emin olamayız. Bu projede C ile küçük bir <strong>mark-and-sweep garbage collector</strong> geliştirerek manuel bellek yönetimini, nesne erişilebilirliğini ve sanal belleğin süreçteki rolünü birlikte inceleyeceğiz.</p>

<p>``</p>

<h2 id="önce-bellek-haritasını-anlayalım">Önce bellek haritasını anlayalım</h2>

<p>Bir süreç çalıştırıldığında işletim sistemi ona geniş ve kesintisiz görünen bir <strong>sanal adres alanı</strong> sunar. Programın kullandığı adresler doğrudan fiziksel RAM adresleri değildir. Sayfa tabloları, sanal sayfaları fiziksel çerçevelere eşler.</p>

<p>Bir adres alanı kabaca kod, global veriler, stack ve heap bölgelerinden oluşur. <code class="language-plaintext highlighter-rouge">malloc</code> ile istediğimiz alan heap üzerinde yönetilir; ancak her <code class="language-plaintext highlighter-rouge">malloc</code> çağrısının anında yeni bir fiziksel sayfa ayırması gerekmez. İşletim sistemi sayfaları ihtiyaç anında eşleyebilir.</p>

<p>Bellek kullanımını basitleştirerek şöyle ifade edebiliriz:</p>

\[M_{etkin} = M_{ayrılan} - M_{erişilemeyen}\]

<p>Garbage collector’ın amacı, artık kök nesnelerden erişilemeyen $M_{erişilemeyen}$ bölümünü bulup yeniden kullanılabilir hâle getirmektir.</p>

<table>
  <thead>
    <tr>
      <th>Kavram</th>
      <th>Manuel yönetim</th>
      <th>Garbage collector</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Ayırma</td>
      <td><code class="language-plaintext highlighter-rouge">malloc</code></td>
      <td>GC tarafından izlenen ayırma</td>
    </tr>
    <tr>
      <td>Serbest bırakma</td>
      <td><code class="language-plaintext highlighter-rouge">free</code> çağrısı</td>
      <td>Erişilebilirlik analizinden sonra</td>
    </tr>
    <tr>
      <td>Temel risk</td>
      <td>Sızıntı, çift <code class="language-plaintext highlighter-rouge">free</code></td>
      <td>Duraklama, ek metadata</td>
    </tr>
    <tr>
      <td>Kontrol</td>
      <td>Programcıda</td>
      <td>Çalışma zamanı sisteminde</td>
    </tr>
    <tr>
      <td>Sanal bellek ilişkisi</td>
      <td>Heap sayfalarını kullanır</td>
      <td>Aynı heap içinde canlı nesneleri belirler</td>
    </tr>
  </tbody>
</table>

<h2 id="mark-and-sweep-nasıl-çalışır">Mark-and-sweep nasıl çalışır?</h2>

<p>Algoritma iki temel aşamadan oluşur:</p>

<ol>
  <li><strong>Mark:</strong> Stack, global değişkenler veya çalışma zamanı tarafından tutulan köklerden başlanır. Ulaşılabilen bütün nesneler işaretlenir.</li>
  <li><strong>Sweep:</strong> Ayrılmış nesneler taranır. İşaretlenmemiş olanlar erişilemez kabul edilerek serbest bırakılır.</li>
</ol>

<p>Bu örnekte gerçek stack taraması yapmayacağız. Bunun yerine kökleri açıkça kaydedeceğiz. Böylece algoritmanın özü, platforma özgü ayrıntılar arasında kaybolmayacak.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
</span>
<span class="k">typedef</span> <span class="k">struct</span> <span class="n">Object</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">marked</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">value</span><span class="p">;</span>
    <span class="k">struct</span> <span class="n">Object</span> <span class="o">*</span><span class="n">ref</span><span class="p">;</span>
    <span class="k">struct</span> <span class="n">Object</span> <span class="o">*</span><span class="n">next</span><span class="p">;</span>
<span class="p">}</span> <span class="n">Object</span><span class="p">;</span>

<span class="k">static</span> <span class="n">Object</span> <span class="o">*</span><span class="n">heap</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>

<span class="n">Object</span> <span class="o">*</span><span class="nf">gc_alloc</span><span class="p">(</span><span class="kt">int</span> <span class="n">value</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">Object</span> <span class="o">*</span><span class="n">obj</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">Object</span><span class="p">));</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">obj</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">perror</span><span class="p">(</span><span class="s">"malloc"</span><span class="p">);</span>
        <span class="n">exit</span><span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">obj</span><span class="o">-&gt;</span><span class="n">marked</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="n">obj</span><span class="o">-&gt;</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span><span class="p">;</span>
    <span class="n">obj</span><span class="o">-&gt;</span><span class="n">ref</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
    <span class="n">obj</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">=</span> <span class="n">heap</span><span class="p">;</span>
    <span class="n">heap</span> <span class="o">=</span> <span class="n">obj</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">obj</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">mark</span><span class="p">(</span><span class="n">Object</span> <span class="o">*</span><span class="n">obj</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">obj</span> <span class="o">==</span> <span class="nb">NULL</span> <span class="o">||</span> <span class="n">obj</span><span class="o">-&gt;</span><span class="n">marked</span><span class="p">)</span>
        <span class="k">return</span><span class="p">;</span>

    <span class="n">obj</span><span class="o">-&gt;</span><span class="n">marked</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="n">mark</span><span class="p">(</span><span class="n">obj</span><span class="o">-&gt;</span><span class="n">ref</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">sweep</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">Object</span> <span class="o">**</span><span class="n">cursor</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">heap</span><span class="p">;</span>

    <span class="k">while</span> <span class="p">(</span><span class="o">*</span><span class="n">cursor</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">Object</span> <span class="o">*</span><span class="n">obj</span> <span class="o">=</span> <span class="o">*</span><span class="n">cursor</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">obj</span><span class="o">-&gt;</span><span class="n">marked</span><span class="p">)</span> <span class="p">{</span>
            <span class="o">*</span><span class="n">cursor</span> <span class="o">=</span> <span class="n">obj</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">;</span>
            <span class="n">printf</span><span class="p">(</span><span class="s">"Silindi: %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">obj</span><span class="o">-&gt;</span><span class="n">value</span><span class="p">);</span>
            <span class="n">free</span><span class="p">(</span><span class="n">obj</span><span class="p">);</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">obj</span><span class="o">-&gt;</span><span class="n">marked</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
            <span class="n">cursor</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">obj</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">collect</span><span class="p">(</span><span class="n">Object</span> <span class="o">**</span><span class="n">roots</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">count</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
        <span class="n">mark</span><span class="p">(</span><span class="n">roots</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
    <span class="n">sweep</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">heap</code> listesi ayrılan tüm nesnelerin envanteridir. <code class="language-plaintext highlighter-rouge">ref</code>, bir nesneden diğerine referans oluşturarak küçük bir nesne grafiği kurar. <code class="language-plaintext highlighter-rouge">mark</code> fonksiyonu bu grafiği derinlik öncelikli dolaşır. Döngüsel referanslarda sonsuz özyinelemeyi <code class="language-plaintext highlighter-rouge">marked</code> kontrolü engeller.</p>

<h2 id="collectorı-deneyelim">Collector’ı deneyelim</h2>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">Object</span> <span class="o">*</span><span class="n">a</span> <span class="o">=</span> <span class="n">gc_alloc</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>
    <span class="n">Object</span> <span class="o">*</span><span class="n">b</span> <span class="o">=</span> <span class="n">gc_alloc</span><span class="p">(</span><span class="mi">20</span><span class="p">);</span>
    <span class="n">Object</span> <span class="o">*</span><span class="n">orphan</span> <span class="o">=</span> <span class="n">gc_alloc</span><span class="p">(</span><span class="mi">99</span><span class="p">);</span>

    <span class="n">a</span><span class="o">-&gt;</span><span class="n">ref</span> <span class="o">=</span> <span class="n">b</span><span class="p">;</span>

    <span class="n">Object</span> <span class="o">*</span><span class="n">roots</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="n">a</span> <span class="p">};</span>
    <span class="n">collect</span><span class="p">(</span><span class="n">roots</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>

    <span class="n">roots</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
    <span class="n">collect</span><span class="p">(</span><span class="n">roots</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>İlk koleksiyonda <code class="language-plaintext highlighter-rouge">a</code> köktür; <code class="language-plaintext highlighter-rouge">b</code> nesnesine <code class="language-plaintext highlighter-rouge">a-&gt;ref</code> üzerinden ulaşılır. <code class="language-plaintext highlighter-rouge">orphan</code> ise hiçbir kökten erişilemediği için silinir. İkinci koleksiyonda kök kaldırıldığından hem <code class="language-plaintext highlighter-rouge">a</code> hem <code class="language-plaintext highlighter-rouge">b</code> temizlenir. Referans döngüsü kurulsa bile kökten ulaşılamayan nesneler toplanabilir; bu, referans sayımına göre önemli bir avantajdır.</p>

<h2 id="gerçek-sistemlerden-farkı">Gerçek sistemlerden farkı</h2>

<p>Bu oyuncak collector thread güvenliği, bellek sıkıştırma, nesil tabanlı toplama ve stack taraması sunmaz. Ayrıca özyinelemeli <code class="language-plaintext highlighter-rouge">mark</code>, çok derin grafiklerde stack taşmasına yol açabilir. Üretim sistemleri iş listesinden yararlanan yinelemeli tarama, tri-color marking ve eşzamanlı toplama gibi yöntemler kullanır.</p>

<p>Yine de proje kritik fikri gösterir: <code class="language-plaintext highlighter-rouge">free</code> işlemini otomatikleştirmek yalnızca adresleri takip etmek değildir; programın canlı nesne grafiğini anlamaktır. Sanal bellek bize adres alanını sağlar, allocator blokları dağıtır, garbage collector ise bu bloklardan hangilerinin artık hikâyede rolü kalmadığına karar verir.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Proje" /><category term="C" /><category term="Bellek Yönetimi" /><category term="Garbage Collector" /><summary type="html"><![CDATA[Bellek yönetimi, programlamanın “ışıkları kapattım mı?” sorusudur: Bir nesneyi ayırdığımızı hatırlarız ama serbest bırakıp bırakmadığımızdan asla tam emin olamayız. Bu projede C ile küçük bir mark-and-sweep garbage collector geliştirerek manuel bellek yönetimini, nesne erişilebilirliğini ve sanal belleğin süreçteki rolünü birlikte inceleyeceğiz.]]></summary></entry><entry><title type="html">C/C++ İçin GCC, Clang ve VS Code Geliştirme Ortamı Kurulumu</title><link href="https://sonsuzus.github.io/posts/cc-icin-gcc-clang-ve-vs-code-gelistirme-ortami-kurulumu/" rel="alternate" type="text/html" title="C/C++ İçin GCC, Clang ve VS Code Geliştirme Ortamı Kurulumu" /><published>2026-08-09T00:00:00+00:00</published><updated>2026-08-09T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/cc-icin-gcc-clang-ve-vs-code-gelistirme-ortami-kurulumu</id><content type="html" xml:base="https://sonsuzus.github.io/posts/cc-icin-gcc-clang-ve-vs-code-gelistirme-ortami-kurulumu/"><![CDATA[<p>C ve C++, işlemciye ve belleğe yakın çalışabilmeleri sayesinde işletim sistemlerinden oyun motorlarına kadar pek çok alanda karşımıza çıkar. Ancak ilk programı yazmadan önce derleyici, hata ayıklayıcı ve editör üçlüsünü doğru biçimde hazırlamak gerekir. Bu rehberde GCC/Clang altyapısını kuracak, VS Code’u yapılandıracak ve kaynak kodun çalıştırılabilir programa nasıl dönüştüğünü öğreneceğiz.</p>

<p>``</p>

<h2 id="derleme-süreci-nasıl-çalışır">Derleme süreci nasıl çalışır?</h2>

<p>C/C++ kaynak kodu doğrudan işlemci tarafından anlaşılmaz. Yazdığımız <code class="language-plaintext highlighter-rouge">.c</code> veya <code class="language-plaintext highlighter-rouge">.cpp</code> dosyaları birkaç aşamadan geçer:</p>

<ol>
  <li><strong>Ön işleme:</strong> <code class="language-plaintext highlighter-rouge">#include</code> ve <code class="language-plaintext highlighter-rouge">#define</code> ifadeleri işlenir.</li>
  <li><strong>Derleme:</strong> Kod, assembly diline dönüştürülür.</li>
  <li><strong>Assembly:</strong> Makine kodunu içeren nesne dosyası üretilir.</li>
  <li><strong>Bağlama:</strong> Nesne dosyaları ve kütüphaneler tek programda birleştirilir.</li>
</ol>

<p>Basitleştirilmiş dönüşüm şöyledir:</p>

\[KaynakKod \rightarrow NesneDosyasi \rightarrow CalistirilabilirProgram\]

<p>Donanıma yakınlık özellikle veri boyutlarında hissedilir. Örneğin $N$ bitlik işaretsiz bir tamsayı, $0$ ile $2^N-1$ arasındaki değerleri temsil edebilir. Belleğin sınırlı olduğu gömülü sistemlerde seçilen veri türü bu nedenle önemlidir.</p>

<table>
  <thead>
    <tr>
      <th>Araç</th>
      <th>GCC</th>
      <th>Clang</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>C derleyicisi</td>
      <td><code class="language-plaintext highlighter-rouge">gcc</code></td>
      <td><code class="language-plaintext highlighter-rouge">clang</code></td>
    </tr>
    <tr>
      <td>C++ derleyicisi</td>
      <td><code class="language-plaintext highlighter-rouge">g++</code></td>
      <td><code class="language-plaintext highlighter-rouge">clang++</code></td>
    </tr>
    <tr>
      <td>Güçlü tarafı</td>
      <td>Yaygın platform desteği</td>
      <td>Açıklayıcı hata mesajları</td>
    </tr>
    <tr>
      <td>Hata ayıklayıcı</td>
      <td>GDB</td>
      <td>LLDB</td>
    </tr>
    <tr>
      <td>Lisans ailesi</td>
      <td>GNU GPL</td>
      <td>Apache 2.0</td>
    </tr>
  </tbody>
</table>

<h2 id="derleyici-kurulumu">Derleyici kurulumu</h2>

<p>Ubuntu ve Debian tabanlı sistemlerde GCC araç zinciri şu komutla kurulabilir:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt update
<span class="nb">sudo </span>apt <span class="nb">install </span>build-essential gdb
</code></pre></div></div>

<p>Clang kullanmak isteyenler ayrıca şunu çalıştırabilir:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt <span class="nb">install </span>clang lldb
</code></pre></div></div>

<p>macOS üzerinde Terminal’den <code class="language-plaintext highlighter-rouge">xcode-select --install</code> komutu Apple Clang araçlarını kurar. Windows’ta ise <strong>MSYS2</strong> kurulup UCRT64 terminalinde aşağıdaki komut kullanılabilir:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pacman <span class="nt">-S</span> mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-gdb
</code></pre></div></div>

<p>Ardından MSYS2’nin <code class="language-plaintext highlighter-rouge">ucrt64/bin</code> klasörü <code class="language-plaintext highlighter-rouge">PATH</code> değişkenine eklenmelidir. Kurulumu doğrulamak için <code class="language-plaintext highlighter-rouge">gcc --version</code>, <code class="language-plaintext highlighter-rouge">g++ --version</code> veya <code class="language-plaintext highlighter-rouge">clang --version</code> çalıştırılır.</p>

<h2 id="i̇lk-program-ve-manuel-derleme">İlk program ve manuel derleme</h2>

<p><code class="language-plaintext highlighter-rouge">main.cpp</code> adında bir dosya oluşturalım:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">sayi</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"Bellekteki değer: "</span> <span class="o">&lt;&lt;</span> <span class="n">sayi</span> <span class="o">&lt;&lt;</span> <span class="sc">'\n'</span><span class="p">;</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Programı GCC ile derlemek için şu komut yeterlidir:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>g++ <span class="nt">-std</span><span class="o">=</span>c++20 <span class="nt">-Wall</span> <span class="nt">-Wextra</span> <span class="nt">-g</span> main.cpp <span class="nt">-o</span> uygulama
</code></pre></div></div>

<p>Burada <code class="language-plaintext highlighter-rouge">-std=c++20</code> dil standardını seçer, <code class="language-plaintext highlighter-rouge">-Wall -Wextra</code> olası hatalar için uyarıları açar, <code class="language-plaintext highlighter-rouge">-g</code> ise hata ayıklama bilgisi ekler. Linux/macOS üzerinde program <code class="language-plaintext highlighter-rouge">./uygulama</code>, Windows’ta <code class="language-plaintext highlighter-rouge">uygulama.exe</code> komutuyla çalıştırılır.</p>

<h2 id="vs-code-yapılandırması">VS Code yapılandırması</h2>

<p>VS Code’a <strong>C/C++</strong> uzantısını kurduktan sonra proje klasöründe <code class="language-plaintext highlighter-rouge">.vscode/tasks.json</code> dosyası oluşturulur:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0.0"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"tasks"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"C++ Derle"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"shell"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"g++"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"args"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="s2">"-std=c++20"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"-Wall"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"-Wextra"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"-g"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"${file}"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"-o"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"${fileDirname}/${fileBasenameNoExtension}"</span><span class="w">
      </span><span class="p">],</span><span class="w">
      </span><span class="nl">"group"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"kind"</span><span class="p">:</span><span class="w"> </span><span class="s2">"build"</span><span class="p">,</span><span class="w"> </span><span class="nl">"isDefault"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w"> </span><span class="p">},</span><span class="w">
      </span><span class="nl">"problemMatcher"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"$gcc"</span><span class="p">]</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Bu görev, açık olan dosyayı <strong>Ctrl+Shift+B</strong> ile derler. Clang tercih ediliyorsa <code class="language-plaintext highlighter-rouge">command</code> alanı <code class="language-plaintext highlighter-rouge">clang++</code> yapılabilir.</p>

<p>Hata ayıklama sırasında breakpoint kullanmak, değişkenlerin bellekteki değişimini adım adım izlemeyi sağlar. VS Code’un <strong>Run and Debug</strong> bölümünden C++ yapılandırması seçildiğinde GDB veya LLDB çalıştırılır. Böylece yalnızca programın sonucunu değil; çağrı yığınını, işaretçileri ve bellek adreslerini de inceleyebilirsiniz.</p>

<p>Son olarak derleyici uyarılarını düşman değil, ücretsiz kod inceleme ekibi olarak görün. C/C++ öğrenirken <code class="language-plaintext highlighter-rouge">-Wall -Wextra</code> kullanmak ve küçük programları hata ayıklayıcıyla yürütmek, bellek yönetimi ile donanım arasındaki ilişkiyi çok daha görünür hâle getirir.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="C/C++" /><category term="VS Code" /><category term="GCC ve Clang" /><summary type="html"><![CDATA[C ve C++, işlemciye ve belleğe yakın çalışabilmeleri sayesinde işletim sistemlerinden oyun motorlarına kadar pek çok alanda karşımıza çıkar. Ancak ilk programı yazmadan önce derleyici, hata ayıklayıcı ve editör üçlüsünü doğru biçimde hazırlamak gerekir. Bu rehberde GCC/Clang altyapısını kuracak, VS Code’u yapılandıracak ve kaynak kodun çalıştırılabilir programa nasıl dönüştüğünü öğreneceğiz.]]></summary></entry><entry><title type="html">Değişkenler ve Bellek Yönetimi: Tipler Baytlara Dönüşürken</title><link href="https://sonsuzus.github.io/posts/degiskenler-ve-bellek-yonetimi-tipler-baytlara-donusurken/" rel="alternate" type="text/html" title="Değişkenler ve Bellek Yönetimi: Tipler Baytlara Dönüşürken" /><published>2026-08-09T00:00:00+00:00</published><updated>2026-08-09T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/degiskenler-ve-bellek-yonetimi-tipler-baytlara-donusurken</id><content type="html" xml:base="https://sonsuzus.github.io/posts/degiskenler-ve-bellek-yonetimi-tipler-baytlara-donusurken/"><![CDATA[<p>Bir değişkeni yalnızca <code class="language-plaintext highlighter-rouge">x = 42</code> diye düşünmek, buzdağının görünen kısmına bakmaktır. Statik tipli dillerde değişken; adı, tipi, yaşam süresi ve bellekte temsil edilme biçimi olan bir sözleşmedir. Derleyici bu sözleşmeyi program çalışmadan önce denetler: <code class="language-plaintext highlighter-rouge">int</code> bekleyen yere metin, belirli aralıkta sayı bekleyen yere taşacak bir değer koymaya çalışırsanız alarm çalar. Bu katılık ilk anda sınırlayıcı görünse de bellek düzenini öngörülebilir, hataları erken yakalanabilir ve performansı daha hesaplanabilir yapar.
``</p>

<p>Statik tip sisteminin ana fikri şudur: Her ifadenin bir tipi vardır ve işlemler yalnızca uyumlu tipler arasında gerçekleşir. Örneğin <code class="language-plaintext highlighter-rouge">int32</code> ile <code class="language-plaintext highlighter-rouge">float64</code> aynı sayısal dünyada yaşasa da bit düzeyindeki anlamları farklıdır. Birincisi tam sayıyı ikinin tümleyeniyle, ikincisi yaklaşık reel sayıyı işaret, üs ve kesir alanlarıyla saklar. Bu nedenle tip dönüşümü sadece etiketi değiştirmek değildir; çoğu zaman bitlerin yeniden yorumlanması veya yeni bir temsile dönüştürülmesidir.</p>

<p>Bir veri türünün teorik kapasitesi, bit sayısıyla doğrudan ilişkilidir. İşaretsiz $n$ bitlik bir tamsayı için olası değer sayısı $2^n$ ve aralık $[0, 2^n-1]$ olur. İşaretli, ikinin tümleyeni kullanan bir tamsayıda ise aralık genellikle $[-2^{n-1}, 2^{n-1}-1]$ şeklindedir. Sekiz bitlik <code class="language-plaintext highlighter-rouge">uint8</code> bu yüzden 256 farklı değer taşır; 255’ten sonra ne olacağı ise dilin taşma kurallarına bağlıdır.</p>

<table>
  <thead>
    <tr>
      <th>Tür</th>
      <th style="text-align: right">Tipik boyut</th>
      <th>Temsil yaklaşımı</th>
      <th>Kritik sınır</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">bool</code></td>
      <td style="text-align: right">1 bayt*</td>
      <td>Doğru/yanlış</td>
      <td>Mantıksal değerler</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">int32</code></td>
      <td style="text-align: right">4 bayt</td>
      <td>İkinin tümleyeni</td>
      <td>$-2^{31}$ ile $2^{31}-1$</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">uint8</code></td>
      <td style="text-align: right">1 bayt</td>
      <td>İşaretsiz ikili sayı</td>
      <td>0 ile 255</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">float64</code></td>
      <td style="text-align: right">8 bayt</td>
      <td>IEEE 754 kayan nokta</td>
      <td>Hassasiyet ve yuvarlama</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">char</code></td>
      <td style="text-align: right">Dile bağlı</td>
      <td>Karakter kodu</td>
      <td>Kodlama standardı</td>
    </tr>
  </tbody>
</table>

<p>*Donanım ve dil uygulamasına göre yerleşim değişebilir. Özellikle <code class="language-plaintext highlighter-rouge">bool</code>, yapılar içinde hizalama nedeniyle beklenenden fazla alan etkisi yaratabilir.</p>

<p>Bellek, byte byte adreslenen büyük bir dizi gibi düşünülebilir. Ancak işlemci bazı verilerin belirli adres sınırlarında başlamasını tercih eder; buna <strong>hizalama</strong> denir. Dört baytlık bir <code class="language-plaintext highlighter-rouge">int32</code> çoğunlukla 4’ün katı bir adreste, sekiz baytlık bir <code class="language-plaintext highlighter-rouge">float64</code> ise 8’in katında konumlanır. Yapıların içinde derleyicinin eklediği görünmez boşluklar, yani padding, bu yüzden ortaya çıkar. Küçük bir alan tasarrufu umarken bellekte fazladan baytlarla karşılaşmak oldukça klasik bir programcı sürprizidir.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp">
</span>
<span class="k">struct</span> <span class="n">SensorA</span> <span class="p">{</span>
    <span class="kt">uint8_t</span> <span class="n">active</span><span class="p">;</span>
    <span class="kt">int32_t</span> <span class="n">reading</span><span class="p">;</span>
    <span class="kt">uint16_t</span> <span class="n">id</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="n">SensorB</span> <span class="p">{</span>
    <span class="kt">int32_t</span> <span class="n">reading</span><span class="p">;</span>
    <span class="kt">uint16_t</span> <span class="n">id</span><span class="p">;</span>
    <span class="kt">uint8_t</span> <span class="n">active</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>Bu C örneğinde iki yapı aynı mantıksal alanları taşır, fakat alan sırası bellek boyutunu değiştirebilir. <code class="language-plaintext highlighter-rouge">SensorA</code> içinde <code class="language-plaintext highlighter-rouge">active</code> sonrasında <code class="language-plaintext highlighter-rouge">int32_t</code> hizalamak için boşluk eklenebilir. <code class="language-plaintext highlighter-rouge">SensorB</code>, geniş alanları önce yerleştirerek padding’i azaltma şansına sahiptir. Kesin sonuç derleyiciye, mimariye ve ABI kurallarına bağlıdır; <code class="language-plaintext highlighter-rouge">sizeof</code> ile ölçmek en güvenli yoldur.</p>

<table>
  <thead>
    <tr>
      <th>Bellek bölgesi</th>
      <th>Genellikle ne tutar?</th>
      <th>Yaşam süresi</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Stack (yığın)</td>
      <td>Yerel değişkenler, çağrı bilgileri</td>
      <td>Fonksiyon çağrısı boyunca</td>
    </tr>
    <tr>
      <td>Heap (öbek)</td>
      <td>Dinamik oluşturulan nesneler</td>
      <td>Açıkça serbest bırakılana veya çöp toplanana kadar</td>
    </tr>
    <tr>
      <td>Static/Data</td>
      <td>Global ve statik veriler</td>
      <td>Programın tamamı boyunca</td>
    </tr>
  </tbody>
</table>

<p>Statik tip, belleğin sahibini tek başına belirlemez. Örneğin Rust’ta tip sistemi sahiplik ve ödünç alma kurallarıyla bellek güvenliğine güçlü katkı sunar; C++’ta RAII nesnenin ömrünü kapsamla ilişkilendirir; Java ve C# ise çöp toplayıcıyla erişilemeyen heap nesnelerini geri kazanır. Buna karşılık C’de <code class="language-plaintext highlighter-rouge">malloc</code> ile alınan alanın <code class="language-plaintext highlighter-rouge">free</code> edilmesi programcının sorumluluğundadır.</p>

<p>Sonuçta değişken tanımı, derleyiciye verilmiş bir niyet beyanıdır: Kaç bayt gerektiğini, bitlerin nasıl yorumlanacağını ve hangi işlemlerin anlamlı olduğunu söyler. Tip sınırlarını, taşmayı, hizalamayı ve yaşam süresini anlayan geliştirici; hem daha güvenli kod yazar hem de “Neden bu yapı 12 değil 16 bayt?” sorusuna paniklemeden cevap verir.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="statik tip sistemi" /><category term="bellek yönetimi" /><category term="değişkenler" /><summary type="html"><![CDATA[Bir değişkeni yalnızca x = 42 diye düşünmek, buzdağının görünen kısmına bakmaktır. Statik tipli dillerde değişken; adı, tipi, yaşam süresi ve bellekte temsil edilme biçimi olan bir sözleşmedir. Derleyici bu sözleşmeyi program çalışmadan önce denetler: int bekleyen yere metin, belirli aralıkta sayı bekleyen yere taşacak bir değer koymaya çalışırsanız alarm çalar. Bu katılık ilk anda sınırlayıcı görünse de bellek düzenini öngörülebilir, hataları erken yakalanabilir ve performansı daha hesaplanabilir yapar.]]></summary></entry><entry><title type="html">Diziler ve İşaretçi Aritmetiği: Bellekte Matematiksel Gezinme</title><link href="https://sonsuzus.github.io/posts/diziler-ve-isaretci-aritmetigi-bellekte-matematiksel-gezinme/" rel="alternate" type="text/html" title="Diziler ve İşaretçi Aritmetiği: Bellekte Matematiksel Gezinme" /><published>2026-08-09T00:00:00+00:00</published><updated>2026-08-09T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/diziler-ve-isaretci-aritmetigi-bellekte-matematiksel-gezinme</id><content type="html" xml:base="https://sonsuzus.github.io/posts/diziler-ve-isaretci-aritmetigi-bellekte-matematiksel-gezinme/"><![CDATA[<p>Bir C dizisi, sadece aynı türden değerlerin listesi değildir; bellekte art arda yerleştirilmiş sabit boyutlu fiziksel blokların ta kendisidir. İşaretçi aritmetiği ise bu blokların adresleri üzerinde güvenli ve anlamlı biçimde gezinmenin yoludur. İlk bakışta <code class="language-plaintext highlighter-rouge">p + 1</code> ifadesi bir adresi yalnızca bir artırıyormuş gibi görünür. Oysa C burada tür bilgisini kullanır ve bir sonraki elemana sıçrar. Bu küçük ayrıntı, dizilerin performanslı çalışmasının temelidir.</p>

<p>``</p>

<h2 id="ardışık-bellek-fikri">Ardışık bellek fikri</h2>

<p><code class="language-plaintext highlighter-rouge">int sayilar[5]</code> tanımlandığında derleyici, beş <code class="language-plaintext highlighter-rouge">int</code> değerini yan yana saklayacak kadar yer ayırır. Eğer bir <code class="language-plaintext highlighter-rouge">int</code> 4 bayt ise, dizinin kapladığı alan teorik olarak $5 \times 4 = 20$ bayttır. İlk elemanın adresi $B$ ve eleman türünün boyutu $S$ ise, $i$. elemanın adresi şu formülle bulunur:</p>

\[adres(dizi[i]) = B + i \times S\]

<p>Bu nedenle <code class="language-plaintext highlighter-rouge">sayilar[3]</code>, aslında <code class="language-plaintext highlighter-rouge">*(sayilar + 3)</code> ile eşdeğerdir. Dizi adı çoğu ifadede ilk elemana işaretçiye dönüşür; yani <code class="language-plaintext highlighter-rouge">sayilar</code> pratikte <code class="language-plaintext highlighter-rouge">&amp;sayilar[0]</code> gibi davranır. Ancak dizi adı değiştirilebilir bir işaretçi değildir: <code class="language-plaintext highlighter-rouge">sayilar++</code> geçersizdir, fakat <code class="language-plaintext highlighter-rouge">int *p = sayilar; p++;</code> tamamen geçerlidir.</p>

<table>
  <thead>
    <tr>
      <th>İfade</th>
      <th>Anlamı</th>
      <th>Sonuç türü</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dizi</code></td>
      <td>İlk elemanın adresi</td>
      <td><code class="language-plaintext highlighter-rouge">int *</code> benzeri işaretçi ifadesi</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dizi[i]</code></td>
      <td><code class="language-plaintext highlighter-rouge">i</code>. elemana erişim</td>
      <td><code class="language-plaintext highlighter-rouge">int</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">*(dizi + i)</code></td>
      <td>İşaretçiyle <code class="language-plaintext highlighter-rouge">i</code>. elemana erişim</td>
      <td><code class="language-plaintext highlighter-rouge">int</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">&amp;dizi[i]</code></td>
      <td><code class="language-plaintext highlighter-rouge">i</code>. elemanın adresi</td>
      <td><code class="language-plaintext highlighter-rouge">int *</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dizi + i</code></td>
      <td><code class="language-plaintext highlighter-rouge">i</code>. elemanın başlangıç adresi</td>
      <td><code class="language-plaintext highlighter-rouge">int *</code></td>
    </tr>
  </tbody>
</table>

<h2 id="i̇şaretçi-neden-bayt-bayt-ilerlemez">İşaretçi neden bayt bayt ilerlemez?</h2>

<p>Bir <code class="language-plaintext highlighter-rouge">char *</code> işaretçisinde <code class="language-plaintext highlighter-rouge">p + 1</code> tipik olarak 1 bayt ilerler; çünkü <code class="language-plaintext highlighter-rouge">char</code> bir bayttır. Buna karşılık <code class="language-plaintext highlighter-rouge">double *</code> için aynı işlem çoğu sistemde 8 baytlık bir sıçramadır. Bu davranış, kaynak kodunu hem okunabilir hem de tür güvenliği açısından daha anlamlı yapar. C’nin yaptığı hesap şudur:</p>

\[p + n = adres(p) + n \times sizeof(*p)\]

<p>Aşağıdaki örnek, indeksleme ile işaretçi aritmetiğinin aynı veriyi nasıl dolaştığını gösterir:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">notlar</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">72</span><span class="p">,</span> <span class="mi">85</span><span class="p">,</span> <span class="mi">91</span><span class="p">,</span> <span class="mi">64</span><span class="p">,</span> <span class="mi">78</span><span class="p">};</span>
    <span class="kt">size_t</span> <span class="n">adet</span> <span class="o">=</span> <span class="k">sizeof</span> <span class="n">notlar</span> <span class="o">/</span> <span class="k">sizeof</span> <span class="n">notlar</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
    <span class="kt">int</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="n">notlar</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">toplam</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">adet</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">toplam</span> <span class="o">+=</span> <span class="o">*</span><span class="p">(</span><span class="n">p</span> <span class="o">+</span> <span class="n">i</span><span class="p">);</span>       <span class="c1">// notlar[i] ile aynıdır</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"%zu. not: %d, adres: %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
               <span class="n">i</span><span class="p">,</span> <span class="o">*</span><span class="p">(</span><span class="n">p</span> <span class="o">+</span> <span class="n">i</span><span class="p">),</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)(</span><span class="n">p</span> <span class="o">+</span> <span class="n">i</span><span class="p">));</span>
    <span class="p">}</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"Ortalama: %.2f</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">toplam</span> <span class="o">/</span> <span class="n">adet</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Burada <code class="language-plaintext highlighter-rouge">sizeof notlar / sizeof notlar[0]</code> kalıbı, sabit boyutlu dizideki eleman sayısını hesaplar. <code class="language-plaintext highlighter-rouge">p + i</code> her turda bir sonraki <code class="language-plaintext highlighter-rouge">int</code> bloğuna gider; <code class="language-plaintext highlighter-rouge">*</code> operatörü de o adresteki değeri okur. Adresler ekranda sayısal olarak ardışık görünmeyebilir, fakat aralarındaki fark <code class="language-plaintext highlighter-rouge">sizeof(int)</code> kadardır.</p>

<h2 id="sınırlar-hızlı-olmak-sınırsız-olmak-değildir">Sınırlar: hızlı olmak, sınırsız olmak değildir</h2>

<p>İşaretçi aritmetiği yalnızca aynı dizi nesnesinin sınırları içinde tanımlıdır. Dizinin son elemanından bir sonraki konumu gösteren <code class="language-plaintext highlighter-rouge">dizi + adet</code> adresini hesaplamak yasaldır; buna <em>one-past-the-end</em> denir. Fakat bu adresin içeriğini okumak, yani <code class="language-plaintext highlighter-rouge">*(dizi + adet)</code> yapmak tanımsız davranıştır. Aynı şekilde, farklı dizilere ait işaretçileri çıkarma veya karşılaştırma girişimleri taşınabilir değildir.</p>

<table>
  <thead>
    <tr>
      <th>İşlem</th>
      <th>Durum</th>
      <th>Neden</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">p + 2</code></td>
      <td>Güvenli</td>
      <td>Hedef aynı dizinin içindeyse</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">p &lt; dizi + adet</code></td>
      <td>Güvenli</td>
      <td>Aynı dizi sınırında karşılaştırma</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dizi + adet</code></td>
      <td>Güvenli</td>
      <td>Sadece sınır sonunu gösterir</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">*(dizi + adet)</code></td>
      <td>Hatalı</td>
      <td>Dizinin dışında okuma yapar</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">p - dizi</code></td>
      <td>Güvenli</td>
      <td>Aynı dizide eleman uzaklığı verir</td>
    </tr>
  </tbody>
</table>

<p>Özetle dizi, bellekteki düzenli blok planıdır; işaretçi ise bu planın koordinat aracıdır. İndeksleme çoğu zaman daha okunaklıdır, ancak işaretçi aritmetiğini anlamak fonksiyon parametreleri, tamponlar, metinler ve yüksek performanslı C kodu için vazgeçilmezdir.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="C" /><category term="Diziler" /><category term="İşaretçiler" /><category term="Bellek Yönetimi" /><summary type="html"><![CDATA[Bir C dizisi, sadece aynı türden değerlerin listesi değildir; bellekte art arda yerleştirilmiş sabit boyutlu fiziksel blokların ta kendisidir. İşaretçi aritmetiği ise bu blokların adresleri üzerinde güvenli ve anlamlı biçimde gezinmenin yoludur. İlk bakışta p + 1 ifadesi bir adresi yalnızca bir artırıyormuş gibi görünür. Oysa C burada tür bilgisini kullanır ve bir sonraki elemana sıçrar. Bu küçük ayrıntı, dizilerin performanslı çalışmasının temelidir.]]></summary></entry><entry><title type="html">Düşman Yapay Zekâsı: Sonlu Durum Makinesi mi, Davranış Ağacı mı?</title><link href="https://sonsuzus.github.io/posts/dusman-yapay-zekasi-sonlu-durum-makinesi-mi-davranis-agaci-mi/" rel="alternate" type="text/html" title="Düşman Yapay Zekâsı: Sonlu Durum Makinesi mi, Davranış Ağacı mı?" /><published>2026-08-09T00:00:00+00:00</published><updated>2026-08-09T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/dusman-yapay-zekasi-sonlu-durum-makinesi-mi-davranis-agaci-mi</id><content type="html" xml:base="https://sonsuzus.github.io/posts/dusman-yapay-zekasi-sonlu-durum-makinesi-mi-davranis-agaci-mi/"><![CDATA[<p>Bir düşmanın oyuncuyu görünce kovalamaya başlaması, canı azalınca kaçması ve hedefini kaybedince devriye rotasına dönmesi basit görünür. Ancak bu kararlar doğrudan kodun içine serpiştirildiğinde kısa sürede yönetilemeyen bir koşul çorbası oluşur. Sonlu Durum Makinesi ve Davranış Ağacı, aynı düşman davranışını daha düzenli, test edilebilir ve genişletilebilir biçimde modelleyen iki klasik oyun yapay zekâsı tekniğidir.
``</p>
<h2 id="önce-problemi-modelleyelim">Önce problemi modelleyelim</h2>

<p>Örnek düşmanımız dört temel eylem gerçekleştirsin: devriye gezme, oyuncuyu kovalama, saldırma ve kaçma. Karar verirken oyuncuya olan $d$ mesafesini, düşmanın $h$ sağlık oranını ve oyuncunun görülüp görülmediğini kullanalım.</p>

<p>Saldırı koşulu basitçe şöyle ifade edilebilir:</p>

\[Attack = visible \land d \leq d_{attack}\]

<p>Kaçma kararı ise $h &lt; 0.2$ olduğunda etkinleşsin. Buradaki önemli ayrım şudur: Sonlu Durum Makinesi, karakterin <strong>hangi durumda bulunduğuna</strong>; Davranış Ağacı ise hedefe ulaşmak için <strong>hangi kararların hangi sırayla sınanacağına</strong> odaklanır.</p>

<h2 id="sonlu-durum-makinesi">Sonlu Durum Makinesi</h2>

<p>Sonlu Durum Makinesi veya FSM, durumlar ve bu durumlar arasındaki geçişlerden oluşur. Düşman aynı anda yalnızca bir etkin durumda bulunur. Matematiksel olarak FSM, $M=(S,\Sigma,\delta,s_0)$ biçiminde gösterilebilir. Burada $S$ durum kümesi, $\Sigma$ girdiler, $\delta$ geçiş fonksiyonu ve $s_0$ başlangıç durumudur.</p>

<p>Aşağıdaki C# örneği temel karar döngüsünü uygular:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">enum</span> <span class="n">EnemyState</span> <span class="p">{</span> <span class="n">Patrol</span><span class="p">,</span> <span class="n">Chase</span><span class="p">,</span> <span class="n">Attack</span><span class="p">,</span> <span class="n">Flee</span> <span class="p">}</span>

<span class="n">EnemyState</span> <span class="n">state</span> <span class="p">=</span> <span class="n">EnemyState</span><span class="p">.</span><span class="n">Patrol</span><span class="p">;</span>

<span class="k">void</span> <span class="nf">UpdateAI</span><span class="p">(</span><span class="kt">bool</span> <span class="n">visible</span><span class="p">,</span> <span class="kt">float</span> <span class="n">distance</span><span class="p">,</span> <span class="kt">float</span> <span class="n">health</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">health</span> <span class="p">&lt;</span> <span class="m">0.2f</span><span class="p">)</span>
        <span class="n">state</span> <span class="p">=</span> <span class="n">EnemyState</span><span class="p">.</span><span class="n">Flee</span><span class="p">;</span>
    <span class="k">else</span> <span class="k">if</span> <span class="p">(!</span><span class="n">visible</span><span class="p">)</span>
        <span class="n">state</span> <span class="p">=</span> <span class="n">EnemyState</span><span class="p">.</span><span class="n">Patrol</span><span class="p">;</span>
    <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">distance</span> <span class="p">&lt;=</span> <span class="n">attackRange</span><span class="p">)</span>
        <span class="n">state</span> <span class="p">=</span> <span class="n">EnemyState</span><span class="p">.</span><span class="n">Attack</span><span class="p">;</span>
    <span class="k">else</span>
        <span class="n">state</span> <span class="p">=</span> <span class="n">EnemyState</span><span class="p">.</span><span class="n">Chase</span><span class="p">;</span>

    <span class="nf">ExecuteState</span><span class="p">();</span>
<span class="p">}</span>

<span class="k">void</span> <span class="nf">ExecuteState</span><span class="p">()</span>
<span class="p">{</span>
    <span class="k">switch</span> <span class="p">(</span><span class="n">state</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">case</span> <span class="n">EnemyState</span><span class="p">.</span><span class="n">Patrol</span><span class="p">:</span> <span class="nf">FollowPatrolRoute</span><span class="p">();</span> <span class="k">break</span><span class="p">;</span>
        <span class="k">case</span> <span class="n">EnemyState</span><span class="p">.</span><span class="n">Chase</span><span class="p">:</span> <span class="nf">MoveToPlayer</span><span class="p">();</span> <span class="k">break</span><span class="p">;</span>
        <span class="k">case</span> <span class="n">EnemyState</span><span class="p">.</span><span class="n">Attack</span><span class="p">:</span> <span class="nf">UseWeapon</span><span class="p">();</span> <span class="k">break</span><span class="p">;</span>
        <span class="k">case</span> <span class="n">EnemyState</span><span class="p">.</span><span class="n">Flee</span><span class="p">:</span> <span class="nf">MoveAwayFromPlayer</span><span class="p">();</span> <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<table>
  <tbody>
    <tr>
      <td>Bu kod, sensör verilerini tek bir duruma dönüştürür ve ilgili eylemi çalıştırır. Küçük düşmanlar için okunaklıdır; fakat onlarca durum eklendiğinde her durumdan diğerlerine geçiş ihtimali doğar. En kötü durumda geçiş sayısı yaklaşık $</td>
      <td>S</td>
      <td>(</td>
      <td>S</td>
      <td>-1)$ olur. İşte spagetti canavarı burada doğar!</td>
    </tr>
  </tbody>
</table>

<h2 id="davranış-ağacı">Davranış Ağacı</h2>

<p>Davranış Ağacı, kökten yapraklara doğru değerlendirilen hiyerarşik düğümler kullanır. Her düğüm <code class="language-plaintext highlighter-rouge">Success</code>, <code class="language-plaintext highlighter-rouge">Failure</code> veya <code class="language-plaintext highlighter-rouge">Running</code> sonucu döndürür. <code class="language-plaintext highlighter-rouge">Selector</code>, başarılı olan ilk çocuğu seçerken <code class="language-plaintext highlighter-rouge">Sequence</code>, bütün çocukların sırayla başarılı olmasını bekler.</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Selector
├── Sequence: Kaç
│   ├── Sağlık %20 altında mı?
│   └── Oyuncudan uzaklaş
├── Sequence: Saldır
│   ├── Oyuncu görünür mü?
│   ├── Menzilde mi?
│   └── Silah kullan
├── Sequence: Kovala
│   ├── Oyuncu görünür mü?
│   └── Oyuncuya ilerle
└── Devriye gez
</code></pre></div></div>

<p>Basitleştirilmiş düğüm mantığı şöyledir:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Node</span> <span class="n">root</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Selector</span><span class="p">(</span>
    <span class="k">new</span> <span class="nf">Sequence</span><span class="p">(</span><span class="n">IsLowHealth</span><span class="p">,</span> <span class="n">FleeFromPlayer</span><span class="p">),</span>
    <span class="k">new</span> <span class="nf">Sequence</span><span class="p">(</span><span class="n">CanSeePlayer</span><span class="p">,</span> <span class="n">IsInRange</span><span class="p">,</span> <span class="n">AttackPlayer</span><span class="p">),</span>
    <span class="k">new</span> <span class="nf">Sequence</span><span class="p">(</span><span class="n">CanSeePlayer</span><span class="p">,</span> <span class="n">ChasePlayer</span><span class="p">),</span>
    <span class="n">Patrol</span>
<span class="p">);</span>

<span class="k">void</span> <span class="nf">UpdateAI</span><span class="p">()</span> <span class="p">=&gt;</span> <span class="n">root</span><span class="p">.</span><span class="nf">Tick</span><span class="p">();</span>
</code></pre></div></div>

<p>Ağaç her güncellemede öncelikli davranışları yukarıdan aşağı sınar. Böylece kaçma davranışı saldırıdan önce değerlendirilir. Yeni bir “yardım çağır” dalı eklemek, çoğu zaman mevcut düğümlerin içini değiştirmeden mümkündür.</p>

<h2 id="karşılaştırma">Karşılaştırma</h2>

<table>
  <thead>
    <tr>
      <th>Ölçüt</th>
      <th>FSM</th>
      <th>Davranış Ağacı</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Temel yapı</td>
      <td>Durum ve geçiş</td>
      <td>Hiyerarşik düğüm</td>
    </tr>
    <tr>
      <td>Öğrenme eğrisi</td>
      <td>Düşük</td>
      <td>Orta</td>
    </tr>
    <tr>
      <td>Küçük yapay zekâ</td>
      <td>Çok uygun</td>
      <td>Gereğinden kapsamlı olabilir</td>
    </tr>
    <tr>
      <td>Büyük davranış seti</td>
      <td>Geçişler karmaşıklaşır</td>
      <td>Modüler biçimde büyür</td>
    </tr>
    <tr>
      <td>Hata ayıklama</td>
      <td>Etkin durum izlenir</td>
      <td>Çalışan dal izlenir</td>
    </tr>
    <tr>
      <td>Yeniden kullanım</td>
      <td>Sınırlı</td>
      <td>Alt ağaçlar paylaşılabilir</td>
    </tr>
  </tbody>
</table>

<h2 id="hangisini-seçmeli">Hangisini seçmeli?</h2>

<p>Kapı, tuzak veya üç-dört davranışlı sıradan düşmanlar için FSM yalın ve hızlıdır. Boss karakterleri, takım koordinasyonu ya da önceliği sık değişen kararlar için Davranış Ağacı daha sürdürülebilirdir. İki yaklaşım rakip olmak zorunda da değildir: Üst seviyede FSM ile “Savaş” ve “Keşif” kipleri yönetilirken, her kipin ayrıntıları bir Davranış Ağacıyla çalıştırılabilir. En iyi oyun yapay zekâsı, en karmaşık sistem değil; tasarım ekibinin davranışı rahatça anlayıp değiştirebildiği sistemdir.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Program" /><category term="oyun yapay zekâsı" /><category term="sonlu durum makinesi" /><category term="davranış ağacı" /><summary type="html"><![CDATA[Bir düşmanın oyuncuyu görünce kovalamaya başlaması, canı azalınca kaçması ve hedefini kaybedince devriye rotasına dönmesi basit görünür. Ancak bu kararlar doğrudan kodun içine serpiştirildiğinde kısa sürede yönetilemeyen bir koşul çorbası oluşur. Sonlu Durum Makinesi ve Davranış Ağacı, aynı düşman davranışını daha düzenli, test edilebilir ve genişletilebilir biçimde modelleyen iki klasik oyun yapay zekâsı tekniğidir.]]></summary></entry><entry><title type="html">Fonksiyonlar ve Yığın (Stack) Mimarisi: Çağrıların Bellekteki Yolculuğu</title><link href="https://sonsuzus.github.io/posts/fonksiyonlar-ve-yigin-stack-mimarisi-cagrilarin-bellekteki-yolculugu/" rel="alternate" type="text/html" title="Fonksiyonlar ve Yığın (Stack) Mimarisi: Çağrıların Bellekteki Yolculuğu" /><published>2026-08-09T00:00:00+00:00</published><updated>2026-08-09T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/fonksiyonlar-ve-yigin-stack-mimarisi-cagrilarin-bellekteki-yolculugu</id><content type="html" xml:base="https://sonsuzus.github.io/posts/fonksiyonlar-ve-yigin-stack-mimarisi-cagrilarin-bellekteki-yolculugu/"><![CDATA[<p>Fonksiyonlar, büyük bir programı yönetilebilir görevlere ayırır; yığın (stack) ise bu görevlerin kim tarafından, hangi parametrelerle ve nereye dönmek üzere çağrıldığını düzenler. Bir fonksiyon çağrısı yalnızca kodun başka bir satıra atlaması değildir: İşletim sistemi, derleyici ve işlemci birlikte çalışarak geçici veriler için düzenli bir bellek kaydı oluşturur. Bu mekanizma sayesinde iç içe çağrılar, özyineleme ve yerel değişkenler güvenle yönetilir.</p>

<!--more-->

<h2 id="yığın-neden-gereklidir">Yığın neden gereklidir?</h2>

<p>Programın belleği kavramsal olarak kod, global veri, heap ve stack gibi bölümlere ayrılır. Heap dinamik olarak oluşturulan ve ömrü programcının ya da çöp toplayıcının kontrolündeki nesneler içindir. Stack ise fonksiyon çağrılarının doğal <strong>LIFO</strong> (Last In, First Out — son giren ilk çıkar) düzenini izler. <code class="language-plaintext highlighter-rouge">main()</code> içinden <code class="language-plaintext highlighter-rouge">A()</code>, onun içinden de <code class="language-plaintext highlighter-rouge">B()</code> çağrılırsa, önce <code class="language-plaintext highlighter-rouge">B()</code> bitmek zorundadır.</p>

<p>Her çağrıda stack üzerinde bir <strong>stack frame</strong> (çağrı çerçevesi) oluşturulur. Bu çerçeve tipik olarak dönüş adresini, parametreleri, yerel değişkenleri, kaydedilmiş kayıtçıları ve hizalama için ayrılan alanı içerir. Stack işaretçisi <code class="language-plaintext highlighter-rouge">$SP</code>, yığının güncel tepesini; frame pointer ise çoğu mimaride mevcut çerçevenin sabit bir referans noktasını gösterir.</p>

<table>
  <thead>
    <tr>
      <th>Bellek bölgesi</th>
      <th>Temel kullanım</th>
      <th>Ömür</th>
      <th>Yönetim</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Kod bölgesi</td>
      <td>Derlenmiş komutlar</td>
      <td>Program boyunca</td>
      <td>İşletim sistemi / yükleyici</td>
    </tr>
    <tr>
      <td>Global veri</td>
      <td>Global ve statik değişkenler</td>
      <td>Program boyunca</td>
      <td>Çalışma zamanı</td>
    </tr>
    <tr>
      <td>Heap</td>
      <td>Dinamik nesneler</td>
      <td>İhtiyaca bağlı</td>
      <td><code class="language-plaintext highlighter-rouge">malloc/free</code> veya GC</td>
    </tr>
    <tr>
      <td>Stack</td>
      <td>Çağrılar ve yerel veriler</td>
      <td>Fonksiyon bitene kadar</td>
      <td>Otomatik</td>
    </tr>
  </tbody>
</table>

<h2 id="bir-çağrının-anatomisi">Bir çağrının anatomisi</h2>

<p>Örneğin aşağıdaki C kodunda <code class="language-plaintext highlighter-rouge">topla</code>, iki parametre alır ve sonucu çağırana döndürür:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">topla</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="kt">int</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">araSonuc</span> <span class="o">=</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">araSonuc</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">sonuc</span> <span class="o">=</span> <span class="n">topla</span><span class="p">(</span><span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">sonuc</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">main</code>, <code class="language-plaintext highlighter-rouge">topla(7, 5)</code> çağrısını hazırlarken parametreleri ilgili kayıtçılara veya mimarinin çağrı sözleşmesine göre stack’e yerleştirir. Ardından dönüş adresini saklayarak <code class="language-plaintext highlighter-rouge">topla</code> koduna dallanır. Fonksiyon kendi yerel alanını ayırır, toplamayı yapar ve dönüş değerini genellikle belirlenmiş bir kayıtçıyla iletir. Son olarak frame temizlenir; işlemci dönüş adresine gider.</p>

<p>Basitleştirilmiş yığın görünümü şöyledir:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Yüksek adresler
+-------------------+
| main'in frame'i   |
+-------------------+
| dönüş adresi      |
| kaydedilmiş kayıt |
| yerel: araSonuc   | &lt;- topla'nın frame'i
+-------------------+
Düşük adresler
</code></pre></div></div>

<p>Çoğu modern sistemde stack düşük adreslere doğru büyür. Bu evrensel bir zorunluluk değildir; önemli olan derleyici, ABI (Application Binary Interface) ve işlemcinin aynı sözleşmeyi izlemesidir. Bir frame için kabaca şu ilişki düşünülebilir:</p>

\[S_{yeni} = S_{eski} - (P + L + R + A)\]

<p>Burada $P$ parametre alanı, $L$ yerel değişkenler, $R$ saklanan kayıtçılar ve $A$ hizalama boşluğudur.</p>

<h2 id="parametreler-nasıl-aktarılır">Parametreler nasıl aktarılır?</h2>

<p>Çağrı sözleşmesi, parametrelerin nerede duracağını ve kimin temizleme yapacağını belirler. Güncel 64-bit mimariler ilk birkaç parametreyi kayıtçılarla aktararak bellek erişimini azaltır; fazlası stack’e taşar. Kayıtçıların sınırlı olması, büyük yapıların veya çok sayıda argümanın stack kullanımını hâlâ gerekli kılar.</p>

<table>
  <thead>
    <tr>
      <th>Yaklaşım</th>
      <th>Avantaj</th>
      <th>Dikkat edilmesi gereken</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Kayıtçı ile aktarım</td>
      <td>Hızlı, az bellek erişimi</td>
      <td>Kayıtçı sayısı sınırlıdır</td>
    </tr>
    <tr>
      <td>Stack ile aktarım</td>
      <td>Çok sayıda argümanı destekler</td>
      <td>Daha fazla bellek trafiği</td>
    </tr>
    <tr>
      <td>Değer ile aktarım</td>
      <td>Çağıranın verisi korunur</td>
      <td>Büyük veriler kopyalanabilir</td>
    </tr>
    <tr>
      <td>Referans/işaretçi ile aktarım</td>
      <td>Kopyalama maliyeti düşer</td>
      <td>Yan etkiler oluşabilir</td>
    </tr>
  </tbody>
</table>

<p>Özyinelemeli fonksiyonlar stack’in önemini dramatik biçimde gösterir. Her <code class="language-plaintext highlighter-rouge">faktoriyel(n)</code> çağrısı ayrı bir frame açar; dolayısıyla bellek tüketimi yaklaşık olarak $O(n)$ olur. Kontrolsüz derinlik, <strong>stack overflow</strong> üretir. Ayrıca bir fonksiyondan yerel dizinin adresini döndürmek tehlikelidir: Fonksiyon bittiğinde frame geçersizdir.</p>

<p>Özetle stack, modüler programlamanın görünmez sahne amiridir. Fonksiyonlar arası geçişi, dönüşü ve geçici verileri disiplinli biçimde düzenler. Bu yapıyı anlamak; performans analizi, hata ayıklama, güvenli kod yazımı ve assembly seviyesindeki davranışı yorumlama için güçlü bir temel sağlar.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="fonksiyonlar" /><category term="stack" /><category term="bellek yönetimi" /><summary type="html"><![CDATA[Fonksiyonlar, büyük bir programı yönetilebilir görevlere ayırır; yığın (stack) ise bu görevlerin kim tarafından, hangi parametrelerle ve nereye dönmek üzere çağrıldığını düzenler. Bir fonksiyon çağrısı yalnızca kodun başka bir satıra atlaması değildir: İşletim sistemi, derleyici ve işlemci birlikte çalışarak geçici veriler için düzenli bir bellek kaydı oluşturur. Bu mekanizma sayesinde iç içe çağrılar, özyineleme ve yerel değişkenler güvenle yönetilir. Yığın neden gereklidir? Programın belleği kavramsal olarak kod, global veri, heap ve stack gibi bölümlere ayrılır. Heap dinamik olarak oluşturulan ve ömrü programcının ya da çöp toplayıcının kontrolündeki nesneler içindir. Stack ise fonksiyon çağrılarının doğal LIFO (Last In, First Out — son giren ilk çıkar) düzenini izler. main() içinden A(), onun içinden de B() çağrılırsa, önce B() bitmek zorundadır. Her çağrıda stack üzerinde bir stack frame (çağrı çerçevesi) oluşturulur. Bu çerçeve tipik olarak dönüş adresini, parametreleri, yerel değişkenleri, kaydedilmiş kayıtçıları ve hizalama için ayrılan alanı içerir. Stack işaretçisi $SP, yığının güncel tepesini; frame pointer ise çoğu mimaride mevcut çerçevenin sabit bir referans noktasını gösterir. Bellek bölgesi Temel kullanım Ömür Yönetim Kod bölgesi Derlenmiş komutlar Program boyunca İşletim sistemi / yükleyici Global veri Global ve statik değişkenler Program boyunca Çalışma zamanı Heap Dinamik nesneler İhtiyaca bağlı malloc/free veya GC Stack Çağrılar ve yerel veriler Fonksiyon bitene kadar Otomatik Bir çağrının anatomisi Örneğin aşağıdaki C kodunda topla, iki parametre alır ve sonucu çağırana döndürür:]]></summary></entry><entry><title type="html">Heap Macerası: C’de malloc/free, C++’ta new/delete ve Bellek Sızıntılarını Önleme</title><link href="https://sonsuzus.github.io/posts/heap-macerasi-cde-mallocfree-cta-newdelete-ve-bellek-sizintilarini-onleme/" rel="alternate" type="text/html" title="Heap Macerası: C’de malloc/free, C++’ta new/delete ve Bellek Sızıntılarını Önleme" /><published>2026-08-09T00:00:00+00:00</published><updated>2026-08-09T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/heap-macerasi-cde-mallocfree-cta-newdelete-ve-bellek-sizintilarini-onleme</id><content type="html" xml:base="https://sonsuzus.github.io/posts/heap-macerasi-cde-mallocfree-cta-newdelete-ve-bellek-sizintilarini-onleme/"><![CDATA[<p>Programlar çalışırken tüm veri boyutları baştan bilinmez: Kullanıcının gireceği metnin uzunluğu, dosyadaki kayıt sayısı veya oluşturulacak nesne miktarı değişkendir. İşte bu noktada <strong>heap (öbek)</strong> alanı devreye girer. Heap, çalışma zamanında bellek talep etmemizi sağlayan bölgedir. Bu bellek çağıran fonksiyon bittiğinde kendiliğinden kaybolmaz; programcı onu bilinçli biçimde iade etmelidir. Güç büyük, sorumluluk daha da büyüktür.
``</p>

<h2 id="stack-ve-heap-neden-farklıdır">Stack ve heap neden farklıdır?</h2>

<p>Yerel değişkenler çoğunlukla <strong>stack</strong> üzerinde tutulur. Fonksiyon sona erdiğinde stack çerçevesi otomatik temizlenir. Heap ise daha uzun ömürlü veriler için uygundur: Belleği bir fonksiyonda ayırıp başka bir yerde kullanabiliriz. Ancak bu esneklik, yaşam döngüsünü yönetme zorunluluğu getirir.</p>

<table>
  <thead>
    <tr>
      <th>Özellik</th>
      <th>Stack</th>
      <th>Heap</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Tahsis yöntemi</td>
      <td>Otomatik</td>
      <td>Programcının isteğiyle</td>
    </tr>
    <tr>
      <td>Yaşam süresi</td>
      <td>Kapsam sonuna kadar</td>
      <td>Serbest bırakılana kadar</td>
    </tr>
    <tr>
      <td>Hız</td>
      <td>Genellikle çok hızlı</td>
      <td>Yönetim maliyeti daha yüksek</td>
    </tr>
    <tr>
      <td>Tipik risk</td>
      <td>Stack overflow</td>
      <td>Leak, dangling pointer, fragmentation</td>
    </tr>
  </tbody>
</table>

<p>Bir programın heap tüketimini kabaca şöyle düşünebiliriz: $H(t) = A(t) - F(t)$; burada $A(t)$ toplam ayrılan, $F(t)$ ise toplam serbest bırakılan bellek miktarıdır. Program artık kullanmadığı verileri bırakmıyorsa $H(t)$ sürekli büyür. Bu durum <strong>memory leak</strong> olarak bilinir.</p>

<h2 id="c-dünyası-malloc-ve-free">C dünyası: <code class="language-plaintext highlighter-rouge">malloc</code> ve <code class="language-plaintext highlighter-rouge">free</code></h2>

<p>C’de <code class="language-plaintext highlighter-rouge">malloc</code>, istenen bayt sayısında ham bellek ayırır ve başlangıç adresini <code class="language-plaintext highlighter-rouge">void*</code> olarak döndürür. Tahsis başarısızsa sonuç <code class="language-plaintext highlighter-rouge">NULL</code> olur. Bu nedenle dönüş değeri mutlaka kontrol edilmelidir. <code class="language-plaintext highlighter-rouge">calloc</code> ise ayrıca alanı sıfırlarla başlatır; <code class="language-plaintext highlighter-rouge">realloc</code> mevcut bloğun boyutunu değiştirmeye çalışır.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">size_t</span> <span class="n">adet</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
    <span class="kt">int</span> <span class="o">*</span><span class="n">sayilar</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">adet</span> <span class="o">*</span> <span class="k">sizeof</span> <span class="o">*</span><span class="n">sayilar</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">sayilar</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"Bellek tahsisi basarisiz!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
        <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">adet</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">sayilar</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)(</span><span class="n">i</span> <span class="o">*</span> <span class="n">i</span><span class="p">);</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"%d "</span><span class="p">,</span> <span class="n">sayilar</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
    <span class="p">}</span>

    <span class="n">free</span><span class="p">(</span><span class="n">sayilar</span><span class="p">);</span>
    <span class="n">sayilar</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Burada <code class="language-plaintext highlighter-rouge">sizeof *sayilar</code> yazımı, işaretçinin hedef türü değişirse boyut hesabının da doğru kalmasını sağlar. <code class="language-plaintext highlighter-rouge">free</code> sonrasında işaretçiye <code class="language-plaintext highlighter-rouge">NULL</code> atamak, yanlışlıkla tekrar kullanımı görünür kılar. Fakat <code class="language-plaintext highlighter-rouge">NULL</code> yapmak, başka kopyalanmış işaretçileri güvenli hâle getirmez.</p>

<h2 id="c-dünyası-new-ve-delete">C++ dünyası: <code class="language-plaintext highlighter-rouge">new</code> ve <code class="language-plaintext highlighter-rouge">delete</code></h2>

<p>C++’ta <code class="language-plaintext highlighter-rouge">new</code>, belleği ayırmanın yanında nesnenin kurucusunu da çağırır; <code class="language-plaintext highlighter-rouge">delete</code> ise yıkıcıyı çalıştırıp alanı geri verir. Tek nesne ile dizi tahsisi için karşılık gelen silme işlemi farklıdır:</p>

<table>
  <thead>
    <tr>
      <th>Tahsis</th>
      <th>Doğru iade</th>
      <th>Hatalı eşleşme</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">new T</code></td>
      <td><code class="language-plaintext highlighter-rouge">delete ptr</code></td>
      <td><code class="language-plaintext highlighter-rouge">delete[] ptr</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">new T[n]</code></td>
      <td><code class="language-plaintext highlighter-rouge">delete[] ptr</code></td>
      <td><code class="language-plaintext highlighter-rouge">delete ptr</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">malloc(...)</code></td>
      <td><code class="language-plaintext highlighter-rouge">free(ptr)</code></td>
      <td><code class="language-plaintext highlighter-rouge">delete ptr</code></td>
    </tr>
  </tbody>
</table>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="kt">int</span><span class="o">*</span> <span class="n">notlar</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">int</span><span class="p">[</span><span class="mi">3</span><span class="p">]{</span><span class="mi">70</span><span class="p">,</span> <span class="mi">85</span><span class="p">,</span> <span class="mi">95</span><span class="p">};</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
        <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">notlar</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="sc">' '</span><span class="p">;</span>

    <span class="k">delete</span><span class="p">[]</span> <span class="n">notlar</span><span class="p">;</span>
    <span class="n">notlar</span> <span class="o">=</span> <span class="nb">nullptr</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">malloc/free</code> ile <code class="language-plaintext highlighter-rouge">new/delete</code> karıştırılmamalıdır. İlki kurucu-yıkıcı çağırmaz; ikincisi C++ nesne yaşam döngüsünü yönetir. Eşleşmeyen çiftler tanımsız davranışa, yani bazen çalışan ama en beklenmedik anda çöken kodlara davetiye çıkarır.</p>

<h2 id="sızıntıya-karşı-modern-yaklaşım-sahiplik">Sızıntıya karşı modern yaklaşım: sahiplik</h2>

<p>C++’ta ham işaretçiyle <code class="language-plaintext highlighter-rouge">new</code> kullanmak çoğu zaman son çaredir. <strong>RAII</strong> ilkesi, kaynağın nesneyle birlikte edinilip nesne yok edilirken otomatik bırakılmasını söyler. <code class="language-plaintext highlighter-rouge">std::vector</code> ve <code class="language-plaintext highlighter-rouge">std::unique_ptr</code> bu fikri uygular:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;memory&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">auto</span> <span class="n">veri</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">make_unique</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">[]</span><span class="o">&gt;</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>
    <span class="n">veri</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
<span class="p">}</span> <span class="c1">// unique_ptr kapsam bitince delete[] otomatik çağrılır</span>
</code></pre></div></div>

<p>Bu yaklaşım özellikle erken <code class="language-plaintext highlighter-rouge">return</code>, istisna (<code class="language-plaintext highlighter-rouge">exception</code>) veya karmaşık kontrol akışlarında sızıntıları azaltır. C projelerinde ise her başarılı <code class="language-plaintext highlighter-rouge">malloc</code> için tek ve belirgin bir <code class="language-plaintext highlighter-rouge">free</code> yolu tasarlamak; C++ projelerinde de mümkün olduğunca standart kapları ve akıllı işaretçileri tercih etmek en sağlam savunmadır. Heap’i özgürce kullanın, ama her tahsisin dönüş biletini unutmayın.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="C" /><category term="C++" /><category term="Dinamik Bellek" /><category term="Heap" /><category term="Memory Leak" /><summary type="html"><![CDATA[Programlar çalışırken tüm veri boyutları baştan bilinmez: Kullanıcının gireceği metnin uzunluğu, dosyadaki kayıt sayısı veya oluşturulacak nesne miktarı değişkendir. İşte bu noktada heap (öbek) alanı devreye girer. Heap, çalışma zamanında bellek talep etmemizi sağlayan bölgedir. Bu bellek çağıran fonksiyon bittiğinde kendiliğinden kaybolmaz; programcı onu bilinçli biçimde iade etmelidir. Güç büyük, sorumluluk daha da büyüktür.]]></summary></entry><entry><title type="html">Kontrol Yapıları: if-else, switch ve Döngülerin Makine Seviyesindeki Maliyeti</title><link href="https://sonsuzus.github.io/posts/kontrol-yapilari-if-else-switch-ve-dongulerin-makine-seviyesindeki-maliyeti/" rel="alternate" type="text/html" title="Kontrol Yapıları: if-else, switch ve Döngülerin Makine Seviyesindeki Maliyeti" /><published>2026-08-09T00:00:00+00:00</published><updated>2026-08-09T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/kontrol-yapilari-if-else-switch-ve-dongulerin-makine-seviyesindeki-maliyeti</id><content type="html" xml:base="https://sonsuzus.github.io/posts/kontrol-yapilari-if-else-switch-ve-dongulerin-makine-seviyesindeki-maliyeti/"><![CDATA[<p>Yüksek seviyeli bir dilde yazılan <code class="language-plaintext highlighter-rouge">if</code>, <code class="language-plaintext highlighter-rouge">switch</code> veya <code class="language-plaintext highlighter-rouge">for</code> satırları zararsız görünür; ancak işlemci bunları doğrudan “karar” olarak algılamaz. Arka planda karşılaştırmalar, koşullu sıçramalar (branch), bellek erişimleri ve bazen tablo üzerinden dolaylı atlamalar çalışır. Bu nedenle kontrol yapısının maliyeti yalnızca kaynak koddaki satır sayısıyla değil, veri dağılımı, derleyici optimizasyonu ve CPU’nun dal tahmin başarısıyla belirlenir.
``</p>

<h2 id="kararın-atomu-karşılaştırma-ve-sıçrama">Kararın atomu: karşılaştırma ve sıçrama</h2>

<p>Bir <code class="language-plaintext highlighter-rouge">if-else</code> yapısı çoğunlukla iki temel makine işlemi üretir: bir karşılaştırma ve bunun sonucuna göre koşullu sıçrama. Kavramsal assembly karşılığı şöyledir:</p>

<pre><code class="language-asm">cmp eax, 10        ; eax ile 10'u karşılaştır
jl  small_value    ; eax &lt; 10 ise etikete atla
; else gövdesi
jmp end_if
small_value:
; if gövdesi
end_if:
</code></pre>

<p>Buradaki maliyet sabit görünse de modern işlemciler paralel çalışan boru hatlarına sahiptir. İşlemci, <code class="language-plaintext highlighter-rouge">jl</code> sonucunu beklemeden hangi yolun çalışacağını <strong>tahmin eder</strong>. Tahmin doğruysa sıçrama neredeyse ucuzdur; yanlışsa yanlış yola ait işler iptal edilir ve boru hattı yeniden doldurulur. Bu ceza mimariye göre değişse de yaklaşık 10–20 çevrim olabilir.</p>

<p>Bir koşulun beklenen maliyetini basitleştirerek şöyle düşünebiliriz:</p>

\[E[C] = C_{base} + P_{miss} \times C_{penalty}\]

<p>Burada $C_{base}$ karşılaştırma ve doğru tahmin edilen sıçramanın maliyeti, $P_{miss}$ yanlış tahmin olasılığı, $C_{penalty}$ ise boru hattı temizleme cezasıdır. Örneğin rastgele dağılan bir boolean veri, tahminci için düzenli bir veriden daha pahalı olabilir.</p>

<table>
  <thead>
    <tr>
      <th>Yapı</th>
      <th>Tipik düşük seviye karşılık</th>
      <th>Güçlü yanı</th>
      <th>Olası maliyet</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">if-else</code></td>
      <td>Karşılaştırma + koşullu branch</td>
      <td>Az sayıda koşulda nettir</td>
      <td>Yanlış branch tahmini</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">else if</code> zinciri</td>
      <td>Ardışık karşılaştırmalar</td>
      <td>Seyrek ve öncelikli durumlar</td>
      <td>Geç eşleşmede çok test</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">switch</code></td>
      <td>Branch zinciri veya jump table</td>
      <td>Çoklu sabit seçenekler</td>
      <td>Tablo erişimi / dolaylı atlama</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">?:</code> ternary</td>
      <td>Branch veya koşulsuz seçim</td>
      <td>Küçük ifadelerde kompakt</td>
      <td>Derleyiciye ve türe bağlı</td>
    </tr>
  </tbody>
</table>

<h2 id="switch-case-ne-zaman-sıçrama-tablosuna-dönüşür">switch-case ne zaman sıçrama tablosuna dönüşür?</h2>

<p><code class="language-plaintext highlighter-rouge">switch-case</code>, her zaman sihirli biçimde $O(1)$ değildir. Case değerleri seyrekse derleyici çoğu zaman bir karşılaştırma zinciri ya da ikili arama ağacı üretir. Değerler yoğun bir aralıktaysa, bir <strong>jump table</strong> oluşturmak mantıklı hale gelir. Girdi değeri tablonun indeksine çevrilir ve uygun adrese dolaylı atlama yapılır.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">puan_ver</span><span class="p">(</span><span class="kt">int</span> <span class="n">seviye</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">switch</span> <span class="p">(</span><span class="n">seviye</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">case</span> <span class="mi">1</span><span class="p">:</span> <span class="k">return</span> <span class="mi">100</span><span class="p">;</span>
        <span class="k">case</span> <span class="mi">2</span><span class="p">:</span> <span class="k">return</span> <span class="mi">250</span><span class="p">;</span>
        <span class="k">case</span> <span class="mi">3</span><span class="p">:</span> <span class="k">return</span> <span class="mi">500</span><span class="p">;</span>
        <span class="k">case</span> <span class="mi">4</span><span class="p">:</span> <span class="k">return</span> <span class="mi">1000</span><span class="p">;</span>
        <span class="nl">default:</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Bu kod, yoğun <code class="language-plaintext highlighter-rouge">1..4</code> aralığı nedeniyle jump table adayıdır. Ancak sadece <code class="language-plaintext highlighter-rouge">case 1</code>, <code class="language-plaintext highlighter-rouge">case 1000</code> ve <code class="language-plaintext highlighter-rouge">case 900000</code> olsaydı devasa ve boşluklarla dolu bir tablo anlamsız olurdu. Derleyici sürümü, optimizasyon seviyesi ve hedef işlemci bu kararı etkiler.</p>

<table>
  <thead>
    <tr>
      <th>Case dağılımı</th>
      <th>Muhtemel strateji</th>
      <th>Yaklaşık arama davranışı</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Az sayıda case</td>
      <td>Karşılaştırma zinciri</td>
      <td>$O(n)$</td>
    </tr>
    <tr>
      <td>Sıralı, orta sayıda seyrek case</td>
      <td>İkili arama ağacı</td>
      <td>$O(\log n)$</td>
    </tr>
    <tr>
      <td>Yoğun sayısal aralık</td>
      <td>Jump table</td>
      <td>Ortalama $O(1)$</td>
    </tr>
  </tbody>
</table>

<h2 id="döngülerin-görünmeyen-masrafı">Döngülerin görünmeyen masrafı</h2>

<p>Bir döngüde her turda yalnızca gövde çalışmaz. Sayaç artırılır, sınır kontrol edilir ve geri sıçrama yapılır. Basit bir toplama örneği:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">long</span> <span class="nf">toplam</span><span class="p">(</span><span class="k">const</span> <span class="kt">int</span> <span class="o">*</span><span class="n">veri</span><span class="p">,</span> <span class="kt">int</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">long</span> <span class="n">sonuc</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">sonuc</span> <span class="o">+=</span> <span class="n">veri</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">sonuc</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Teorik karmaşıklık $T(n) = an + b$ olduğundan çalışma $O(n)$’dir. Fakat pratikte asıl sınırlandırıcı unsur branch olmayabilir: <code class="language-plaintext highlighter-rouge">veri[i]</code> bellekten gelir ve önbellek kaçırmaları işlemciyi bekletebilir. Düzenli erişimli bu örnekte CPU öngetiricisi başarılıdır; düzensiz pointer takibi yapan döngülerde ise bellek gecikmesi baskın hale gelir.</p>

<p>Döngü koşulu genellikle son tur dışında kolay tahmin edilir: branch çoğu kez “devam et”, bir kez “çık” sonucunu verir. Buna karşılık döngü içindeki rastgele <code class="language-plaintext highlighter-rouge">if</code> koşulları tahminciyi zorlayabilir. Performans kritik kodda önce ölçüm yapmak, sonra gerekirse veriyi gruplayarak branch öngörülebilirliğini artırmak en sağlıklı yaklaşımdır. Kısacası iyi kontrol akışı, yalnızca doğru yolu seçmez; işlemcinin o yolu önceden sezmesine de yardım eder.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="kontrol yapıları" /><category term="performans" /><category term="runtime" /><category term="assembly" /><category term="algoritmalar" /><summary type="html"><![CDATA[Yüksek seviyeli bir dilde yazılan if, switch veya for satırları zararsız görünür; ancak işlemci bunları doğrudan “karar” olarak algılamaz. Arka planda karşılaştırmalar, koşullu sıçramalar (branch), bellek erişimleri ve bazen tablo üzerinden dolaylı atlamalar çalışır. Bu nedenle kontrol yapısının maliyeti yalnızca kaynak koddaki satır sayısıyla değil, veri dağılımı, derleyici optimizasyonu ve CPU’nun dal tahmin başarısıyla belirlenir.]]></summary></entry><entry><title type="html">Pointer’lar ve Bellek Adresleme: Veriye Değil, Konumuna Ulaşmak</title><link href="https://sonsuzus.github.io/posts/pointerlar-ve-bellek-adresleme-veriye-degil-konumuna-ulasmak/" rel="alternate" type="text/html" title="Pointer’lar ve Bellek Adresleme: Veriye Değil, Konumuna Ulaşmak" /><published>2026-08-09T00:00:00+00:00</published><updated>2026-08-09T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/pointerlar-ve-bellek-adresleme-veriye-degil-konumuna-ulasmak</id><content type="html" xml:base="https://sonsuzus.github.io/posts/pointerlar-ve-bellek-adresleme-veriye-degil-konumuna-ulasmak/"><![CDATA[<p>Bir değişkeni düşünün: çoğu zaman onun değerini kullanırız, fakat bilgisayar açısından asıl önemli soru şudur: Bu değer RAM’in <em>neresinde</em> duruyor? İşaretçiler (pointer), verinin kendisini taşımak yerine o verinin bellekteki adresini saklayan değişkenlerdir. Böylece büyük bir diziyi, karmaşık bir nesneyi veya bir yapıyı kopyalamak yerine ona doğrudan erişebiliriz. Bu yaklaşım sistem programlama, oyun motorları, gömülü yazılımlar ve yüksek performanslı uygulamaların görünmez turbo düğmesidir.
``</p>

<p>Bir belleği numaralandırılmış evler bulunan uzun bir sokak gibi ele alalım. <code class="language-plaintext highlighter-rouge">int sayi = 42;</code> ifadesi, <code class="language-plaintext highlighter-rouge">42</code> değerini bir eve yerleştirir. <code class="language-plaintext highlighter-rouge">&amp;sayi</code> operatörü o evin adresini verir. Pointer ise bu adresin yazılı olduğu bir not kâğıdıdır. C dilinde <code class="language-plaintext highlighter-rouge">int *p = &amp;sayi;</code> yazdığımızda, <code class="language-plaintext highlighter-rouge">p</code> artık <code class="language-plaintext highlighter-rouge">sayi</code> değişkeninin bulunduğu konumu bilir. <code class="language-plaintext highlighter-rouge">*p</code> ise “bu adrese git ve oradaki değeri oku ya da değiştir” anlamına gelir.</p>

<p>Matematiksel olarak, bir değişkenin adresini $a$ ve o adresteki değeri $V(a)$ ile gösterelim. Pointer’ın tuttuğu değer $p = a$ ise dereference işlemi şu fikre karşılık gelir:</p>

\[*p = V(p) = V(a)\]

<p>Yani pointer değerin kendisi değil, değere ulaşan rotadır. Bu ayrım özellikle büyük veri yapılarında kritiktir. Boyutu $n$ olan bir yapıyı kopyalamanın maliyeti kabaca $O(n)$ iken, adresini kopyalamak çoğu modern mimaride sabit boyutlu bir adres olduğu için $O(1)$ maliyetlidir.</p>

<table>
  <thead>
    <tr>
      <th>İşlem</th>
      <th style="text-align: right">Değer Kopyalama</th>
      <th style="text-align: right">Pointer ile Aktarma</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Büyük yapı gönderme</td>
      <td style="text-align: right">$O(n)$</td>
      <td style="text-align: right">$O(1)$</td>
    </tr>
    <tr>
      <td>Bellek tüketimi</td>
      <td style="text-align: right">Yeni kopya gerekir</td>
      <td style="text-align: right">Sadece adres saklanır</td>
    </tr>
    <tr>
      <td>Orijinali değiştirme</td>
      <td style="text-align: right">Genellikle etkilemez</td>
      <td style="text-align: right">Doğrudan etkileyebilir</td>
    </tr>
    <tr>
      <td>Güvenlik riski</td>
      <td style="text-align: right">Daha düşük</td>
      <td style="text-align: right">Geçersiz adres riski vardır</td>
    </tr>
  </tbody>
</table>

<p>Aşağıdaki C örneği, değer ve adres arasındaki ilişkiyi netleştirir:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span>
<span class="kt">void</span> <span class="nf">indirimUygula</span><span class="p">(</span><span class="kt">double</span> <span class="o">*</span><span class="n">fiyat</span><span class="p">,</span> <span class="kt">double</span> <span class="n">oran</span><span class="p">)</span> <span class="p">{</span>
    <span class="o">*</span><span class="n">fiyat</span> <span class="o">=</span> <span class="o">*</span><span class="n">fiyat</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span><span class="p">.</span><span class="mi">0</span> <span class="o">-</span> <span class="n">oran</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">double</span> <span class="n">urunFiyati</span> <span class="o">=</span> <span class="mi">120</span><span class="p">.</span><span class="mi">0</span><span class="p">;</span>
    <span class="kt">double</span> <span class="o">*</span><span class="n">fiyatAdresi</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">urunFiyati</span><span class="p">;</span>

    <span class="n">indirimUygula</span><span class="p">(</span><span class="n">fiyatAdresi</span><span class="p">,</span> <span class="mi">0</span><span class="p">.</span><span class="mi">25</span><span class="p">);</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"Yeni fiyat: %.2f</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">urunFiyati</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Bellek adresi: %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">fiyatAdresi</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Burada <code class="language-plaintext highlighter-rouge">indirimUygula</code>, fiyatın kopyasını almak yerine adresini alır. Fonksiyon içindeki <code class="language-plaintext highlighter-rouge">*fiyat</code>, çağıranın belleğindeki gerçek <code class="language-plaintext highlighter-rouge">urunFiyati</code> değişkenidir; sonuç olarak değer <code class="language-plaintext highlighter-rouge">90.00</code> olur. Bu teknik, büyük <code class="language-plaintext highlighter-rouge">struct</code> nesnelerini fonksiyonlara iletirken gereksiz kopyalamayı önler.</p>

<p>Pointer aritmetiği de belleğin ardışık düzeninden yararlanır. Bir <code class="language-plaintext highlighter-rouge">int *p</code> için <code class="language-plaintext highlighter-rouge">p + 1</code>, adresi bir bayt değil, <code class="language-plaintext highlighter-rouge">sizeof(int)</code> kadar ilerletir. Genel ifade şöyledir:</p>

\[\operatorname{adres}(p+i) = \operatorname{adres}(p) + i \times \operatorname{sizeof}(T)\]

<p>Bu nedenle diziler ve pointer’lar yakın akrabadır: <code class="language-plaintext highlighter-rouge">dizi[i]</code> ifadesi kavramsal olarak <code class="language-plaintext highlighter-rouge">*(dizi + i)</code> ile eşdeğerdir. Ancak bu güç dikkat ister. Başlatılmamış pointer kullanmak, serbest bırakılmış belleğe erişmek (<em>dangling pointer</em>) veya dizi sınırının dışına taşmak tanımsız davranış üretir.</p>

<table>
  <thead>
    <tr>
      <th>İyi Uygulama</th>
      <th>Neden Önemli?</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Pointer’ı <code class="language-plaintext highlighter-rouge">NULL</code>/<code class="language-plaintext highlighter-rouge">nullptr</code> ile başlatmak</td>
      <td>Rastgele adres kullanımını azaltır</td>
    </tr>
    <tr>
      <td>Kullanımdan önce null kontrolü yapmak</td>
      <td>Çökme riskini düşürür</td>
    </tr>
    <tr>
      <td>Dinamik belleği doğru zamanda serbest bırakmak</td>
      <td>Bellek sızıntısını önler</td>
    </tr>
    <tr>
      <td>Sınırları korumak</td>
      <td>Taşma ve güvenlik açıklarını engeller</td>
    </tr>
  </tbody>
</table>

<p>Özetle pointer, “veriyi taşıma” yerine “verinin yerini paylaşma” aracıdır. Doğru kullanıldığında performans ve esneklik kazandırır; dikkatsiz kullanıldığında ise programı mayın tarlasına çevirebilir. Adresi bilmek güçtür, ama o gücün yanında mutlaka sorumluluk da gelir.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="C" /><category term="C++" /><category term="Pointer" /><category term="Bellek Yönetimi" /><category term="Performans" /><summary type="html"><![CDATA[Bir değişkeni düşünün: çoğu zaman onun değerini kullanırız, fakat bilgisayar açısından asıl önemli soru şudur: Bu değer RAM’in neresinde duruyor? İşaretçiler (pointer), verinin kendisini taşımak yerine o verinin bellekteki adresini saklayan değişkenlerdir. Böylece büyük bir diziyi, karmaşık bir nesneyi veya bir yapıyı kopyalamak yerine ona doğrudan erişebiliriz. Bu yaklaşım sistem programlama, oyun motorları, gömülü yazılımlar ve yüksek performanslı uygulamaların görünmez turbo düğmesidir.]]></summary></entry><entry><title type="html">Sistem Programlama Felsefesi: C’nin Çıplak Gerçekliği ve C++’ın Mimari Gücü</title><link href="https://sonsuzus.github.io/posts/sistem-programlama-felsefesi-cnin-ciplak-gercekligi-ve-cin-mimari-gucu/" rel="alternate" type="text/html" title="Sistem Programlama Felsefesi: C’nin Çıplak Gerçekliği ve C++’ın Mimari Gücü" /><published>2026-08-09T00:00:00+00:00</published><updated>2026-08-09T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/sistem-programlama-felsefesi-cnin-ciplak-gercekligi-ve-cin-mimari-gucu</id><content type="html" xml:base="https://sonsuzus.github.io/posts/sistem-programlama-felsefesi-cnin-ciplak-gercekligi-ve-cin-mimari-gucu/"><![CDATA[<p>Sistem programlama, bilgisayarın yalnızca ne yaptığını değil, bunu <strong>hangi maliyetle</strong> yaptığını da önemser. Bellek adresleri, işlemci önbelleği, kesmeler, dosya tanıtıcıları ve eşzamanlılık burada günlük araçlardır. C ile C++ arasındaki seçim de basitçe “eski mi yeni mi?” sorusu değildir: C doğrudanlık ve evrensel uyumluluk sunarken, C++ aynı donanım düzeyinde daha sağlam soyutlamalar kurmayı hedefler.
``</p>
<h2 id="temel-ayrım-kontrol-mü-ifade-gücü-mü">Temel ayrım: kontrol mü, ifade gücü mü?</h2>

<p>C, işletim sistemi arayüzlerinin doğal dilidir. Linux çekirdeğinin C ile yazılması tesadüf değildir: dilin çalışma zamanı yükü çok küçüktür, bellek düzeni öngörülebilirdir ve üretilen makine koduna yaklaşmak kolaydır. Bir <code class="language-plaintext highlighter-rouge">struct</code>, bellekteki verinin açık bir tarifidir; bir işaretçi ise doğrudan adresle konuşur.</p>

<p>C++ ise C’nin düşük seviyeli alanını terk etmeden, kaynak yönetimi ve büyük ölçekli tasarım için araçlar ekler. Sınıflar, şablonlar, RAII ve tür güvenliği doğru kullanıldığında “sıfır maliyetli soyutlama” hedefler. İdeal durumda kullanılmayan özelliğin bedeli yoktur; kullanılan soyutlama da elle yazılmış C koduna yakın maliyet üretir.</p>

<p>Bu yaklaşım kabaca şöyle modellenebilir:</p>

\[T_{toplam} = T_{hesaplama} + T_{bellek} + T_{senkronizasyon} + T_{soyutlama}\]

<p>C’de geliştirici son terimi elle azaltır. C++’ta ise iyi tasarlanmış bir soyutlama ile $T_{soyutlama} \approx 0$ olmaya çalışır. Ancak sanal fonksiyonlar, kontrolsüz dinamik tahsis ve istisnalar bazı senaryolarda bu maliyeti görünür hâle getirebilir.</p>

<table>
  <thead>
    <tr>
      <th>Başlık</th>
      <th>C</th>
      <th>C++</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Kaynak yönetimi</td>
      <td><code class="language-plaintext highlighter-rouge">malloc/free</code> disiplini</td>
      <td>RAII, akıllı işaretçiler</td>
    </tr>
    <tr>
      <td>Soyutlama</td>
      <td>Fonksiyonlar ve <code class="language-plaintext highlighter-rouge">struct</code></td>
      <td>Sınıflar, şablonlar, kavramlar</td>
    </tr>
    <tr>
      <td>ABI ve uyumluluk</td>
      <td>Çok kararlı, C API’leri yaygın</td>
      <td>Ad karmaşası ve ABI ayrıntıları daha karmaşık</td>
    </tr>
    <tr>
      <td>Hata modeli</td>
      <td>Hata kodları, <code class="language-plaintext highlighter-rouge">errno</code></td>
      <td>Hata kodları veya istisnalar</td>
    </tr>
    <tr>
      <td>Tipik alan</td>
      <td>Çekirdek, sürücü, gömülü sistem</td>
      <td>Oyun motoru, tarayıcı, yüksek performanslı servis</td>
    </tr>
  </tbody>
</table>

<h2 id="c-donanımla-pazarlıksız-konuşmak">C: donanımla pazarlıksız konuşmak</h2>

<p>Bir sürücü, önyükleyici veya mikrodenetleyici kodunda başlangıç maliyeti, ikili boyut ve davranışın kesinliği kritiktir. C burada güçlüdür. Aşağıdaki örnek, bir bellek eşlemeli yazmacı temsil eder:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp">
</span>
<span class="cp">#define GPIO_OUT (*(volatile uint32_t *)0x40020014u)
</span>
<span class="kt">void</span> <span class="nf">led_yak</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">GPIO_OUT</span> <span class="o">|=</span> <span class="p">(</span><span class="mi">1u</span> <span class="o">&lt;&lt;</span> <span class="mi">5</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">volatile</code>, derleyiciye bu belleğin donanım tarafından değişebileceğini söyler; bu yüzden okuma veya yazma işlemini “gereksiz” diye silemez. Bu kodun etkisi nettir: belirli adresteki yazmacın beşinci biti etkinleşir. Fakat bu güç sorumluluk getirir: yanlış adres, taşan dizi veya unutulan <code class="language-plaintext highlighter-rouge">free</code>, doğrudan hata üretir.</p>

<h2 id="c-kaynakları-nesne-ömrüne-bağlamak">C++: kaynakları nesne ömrüne bağlamak</h2>

<p>İşletim sistemi seviyesinde her şey sınıf olmak zorunda değildir. Fakat dosya tanıtıcısı, kilit veya soket gibi <strong>sahipliği olan kaynaklar</strong>, C++ için çok uygundur. RAII’nin fikri basittir: kaynak kurucuda alınır, nesne yok edilirken bırakılır.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdexcept&gt;</span><span class="cp">
</span>
<span class="k">class</span> <span class="nc">DosyaTan</span><span class="err">ı</span><span class="n">t</span><span class="err">ı</span><span class="n">c</span><span class="err">ı</span><span class="n">s</span><span class="err">ı</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">fd_</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
<span class="nl">public:</span>
    <span class="k">explicit</span> <span class="n">DosyaTan</span><span class="err">ı</span><span class="n">t</span><span class="err">ı</span><span class="n">c</span><span class="err">ı</span><span class="n">s</span><span class="err">ı</span><span class="p">(</span><span class="kt">int</span> <span class="n">fd</span><span class="p">)</span> <span class="o">:</span> <span class="n">fd_</span><span class="p">(</span><span class="n">fd</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">fd_</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="k">throw</span> <span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">"dosya acilamadi"</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="o">~</span><span class="n">DosyaTan</span><span class="err">ı</span><span class="n">t</span><span class="err">ı</span><span class="n">c</span><span class="err">ı</span><span class="n">s</span><span class="err">ı</span><span class="p">()</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">fd_</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="n">close</span><span class="p">(</span><span class="n">fd_</span><span class="p">);</span> <span class="p">}</span>
    <span class="n">DosyaTan</span><span class="err">ı</span><span class="n">t</span><span class="err">ı</span><span class="n">c</span><span class="err">ı</span><span class="n">s</span><span class="err">ı</span><span class="p">(</span><span class="k">const</span> <span class="n">DosyaTan</span><span class="err">ı</span><span class="n">t</span><span class="err">ı</span><span class="n">c</span><span class="err">ı</span><span class="n">s</span><span class="err">ı</span><span class="o">&amp;</span><span class="p">)</span> <span class="o">=</span> <span class="k">delete</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>Bu sınıfın amacı dosya okumak değil, <code class="language-plaintext highlighter-rouge">close</code> çağrısının unutulmasını zorlaştırmaktır. Kopyalamayı kapatması da aynı tanıtıcının iki kez kapatılmasını önler. Gerçek zamanlı veya çekirdek kodunda istisnalar tercih edilmeyebilir; yine de RAII, istisnasız bir C++ tarzında da değerlidir.</p>

<h2 id="karar-rehberi">Karar rehberi</h2>

<table>
  <thead>
    <tr>
      <th>Senaryo</th>
      <th>Daha doğal tercih</th>
      <th>Gerekçe</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Çekirdek modülü, bootloader</td>
      <td>C</td>
      <td>Küçük çalışma zamanı ve yerleşik ekosistem</td>
    </tr>
    <tr>
      <td>Donanım sürücüsü</td>
      <td>C, bazen kısıtlı C++</td>
      <td>Kesin kontrol ve platform kuralları</td>
    </tr>
    <tr>
      <td>Yüksek performanslı ağ sunucusu</td>
      <td>C++</td>
      <td>RAII, şablonlar ve güçlü tür modeli</td>
    </tr>
    <tr>
      <td>C ile sunulan kütüphane API’si</td>
      <td>C arayüzü</td>
      <td>Farklı dillerle kolay bağlanabilirlik</td>
    </tr>
    <tr>
      <td>Büyük motor veya altyapı</td>
      <td>C++</td>
      <td>Karmaşıklığı modüllere ayırma avantajı</td>
    </tr>
  </tbody>
</table>

<p>Sonuçta C “her ayrıntıyı ben yönetirim” felsefesidir; C++ ise “ayrıntıyı yönetirim, ama tekrar eden hataları dile devrederim” yaklaşımıdır. İyi sistem yazılımı dil savaşından değil, gecikme bütçesi, hata toleransı, ekip deneyimi ve hedef platformun gerçeklerinden doğar.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="C" /><category term="C++" /><category term="Sistem Programlama" /><summary type="html"><![CDATA[Sistem programlama, bilgisayarın yalnızca ne yaptığını değil, bunu hangi maliyetle yaptığını da önemser. Bellek adresleri, işlemci önbelleği, kesmeler, dosya tanıtıcıları ve eşzamanlılık burada günlük araçlardır. C ile C++ arasındaki seçim de basitçe “eski mi yeni mi?” sorusu değildir: C doğrudanlık ve evrensel uyumluluk sunarken, C++ aynı donanım düzeyinde daha sağlam soyutlamalar kurmayı hedefler.]]></summary></entry></feed>