<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Kshitij Patil</title><link>https://ksh.fyi/</link><description>Recent content on Kshitij Patil</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sat, 03 Oct 2026 20:40:00 +0530</lastBuildDate><atom:link href="https://ksh.fyi/index.xml" rel="self" type="application/rss+xml"/><item><title>Makefile basics for a small C project</title><link>https://ksh.fyi/notes/makefile-basics/</link><pubDate>Sat, 03 Oct 2026 20:40:00 +0530</pubDate><guid>https://ksh.fyi/notes/makefile-basics/</guid><description>&lt;p&gt;The Makefile for a one-binary C project, explained piece by piece.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-make" data-lang="make"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;CC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; gcc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;CFLAGS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; -std&lt;span class="o"&gt;=&lt;/span&gt;gnu23 -Og -g3 -Wall -Werror -Wextra -Wwrite-strings
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;OUT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; out
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;BIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;$(&lt;/span&gt;OUT&lt;span class="k"&gt;)&lt;/span&gt;/tinybox
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;BIN&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Any out/&amp;lt;name&amp;gt; from &amp;lt;name&amp;gt;.c, e.g. `make out/scratch`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;$(OUT)/%&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; %.&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="n"&gt;Makefile&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;OUT&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;$(&lt;/span&gt;CC&lt;span class="k"&gt;)&lt;/span&gt; &lt;span class="k"&gt;$(&lt;/span&gt;CFLAGS&lt;span class="k"&gt;)&lt;/span&gt; -o &lt;span class="nv"&gt;$@&lt;/span&gt; $&amp;lt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;$(OUT)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; mkdir -p &lt;span class="nv"&gt;$@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;tinybox&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;BIN&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;scratch&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;OUT&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;/&lt;span class="n"&gt;scratch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;BIN&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ./&lt;span class="k"&gt;$(&lt;/span&gt;BIN&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; rm -rf &lt;span class="k"&gt;$(&lt;/span&gt;OUT&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="n"&gt;clean&lt;/span&gt; &lt;span class="n"&gt;tinybox&lt;/span&gt; &lt;span class="n"&gt;scratch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="rules"&gt;Rules&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-make" data-lang="make"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;target&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prerequisites&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; recipe
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;make rebuilds &lt;code&gt;target&lt;/code&gt; if it doesn&amp;rsquo;t exist or &lt;strong&gt;any prerequisite is newer&lt;/strong&gt;
(by file modification time). That&amp;rsquo;s the whole algorithm.&lt;/li&gt;
&lt;li&gt;The recipe lines must start with a &lt;strong&gt;tab&lt;/strong&gt;. Spaces give
&lt;code&gt;*** missing separator. Stop.&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;A rule with no recipe (&lt;code&gt;tinybox: $(BIN)&lt;/code&gt;) just says &amp;ldquo;to make this, make that&amp;rdquo;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="variables"&gt;Variables&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;CC&lt;/code&gt;, &lt;code&gt;CFLAGS&lt;/code&gt; are conventional names (make&amp;rsquo;s built-in rules use them too).
Use with &lt;code&gt;$(NAME)&lt;/code&gt;. Override from the command line: &lt;code&gt;make CFLAGS=-O2&lt;/code&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>The Makefile for a one-binary C project, explained piece by piece.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-make" data-lang="make"><span class="line"><span class="cl"><span class="nv">CC</span>     <span class="o">=</span> gcc
</span></span><span class="line"><span class="cl"><span class="nv">CFLAGS</span> <span class="o">=</span> -std<span class="o">=</span>gnu23 -Og -g3 -Wall -Werror -Wextra -Wwrite-strings
</span></span><span class="line"><span class="cl"><span class="nv">OUT</span>    <span class="o">=</span> out
</span></span><span class="line"><span class="cl"><span class="nv">BIN</span>    <span class="o">=</span> <span class="k">$(</span>OUT<span class="k">)</span>/tinybox
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">all</span><span class="o">:</span> <span class="k">$(</span><span class="nv">BIN</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># Any out/&lt;name&gt; from &lt;name&gt;.c, e.g. `make out/scratch`
</span></span></span><span class="line"><span class="cl"><span class="nf">$(OUT)/%</span><span class="o">:</span> %.<span class="n">c</span> <span class="n">Makefile</span> <span class="p">|</span> <span class="k">$(</span><span class="nv">OUT</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">$(</span>CC<span class="k">)</span> <span class="k">$(</span>CFLAGS<span class="k">)</span> -o <span class="nv">$@</span> $&lt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">$(OUT)</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">	mkdir -p <span class="nv">$@</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">tinybox</span><span class="o">:</span> <span class="k">$(</span><span class="nv">BIN</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">scratch</span><span class="o">:</span> <span class="k">$(</span><span class="nv">OUT</span><span class="k">)</span>/<span class="n">scratch</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">run</span><span class="o">:</span> <span class="k">$(</span><span class="nv">BIN</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">	./<span class="k">$(</span>BIN<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">clean</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">	rm -rf <span class="k">$(</span>OUT<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">.PHONY</span><span class="o">:</span> <span class="n">all</span> <span class="n">run</span> <span class="n">clean</span> <span class="n">tinybox</span> <span class="n">scratch</span>
</span></span></code></pre></div><h2 id="rules">Rules</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-make" data-lang="make"><span class="line"><span class="cl"><span class="nf">target</span><span class="o">:</span> <span class="n">prerequisites</span>
</span></span><span class="line"><span class="cl">	recipe
</span></span></code></pre></div><ul>
<li>make rebuilds <code>target</code> if it doesn&rsquo;t exist or <strong>any prerequisite is newer</strong>
(by file modification time). That&rsquo;s the whole algorithm.</li>
<li>The recipe lines must start with a <strong>tab</strong>. Spaces give
<code>*** missing separator.  Stop.</code></li>
<li>A rule with no recipe (<code>tinybox: $(BIN)</code>) just says &ldquo;to make this, make that&rdquo;.</li>
</ul>
<h2 id="variables">Variables</h2>
<p><code>CC</code>, <code>CFLAGS</code> are conventional names (make&rsquo;s built-in rules use them too).
Use with <code>$(NAME)</code>. Override from the command line: <code>make CFLAGS=-O2</code>.</p>
<h2 id="automatic-variables">Automatic variables</h2>
<p>Inside a recipe:</p>
<ul>
<li><code>$@</code>: the target (<code>out/tinybox</code>)</li>
<li><code>$&lt;</code>: the <strong>first</strong> prerequisite (<code>tinybox.c</code>)</li>
<li><code>$^</code>: all prerequisites</li>
</ul>
<h2 id="the-default-target-and-why-its-called-all">The default target, and why it&rsquo;s called <code>all</code></h2>
<p>Plain <code>make</code> builds the <strong>first</strong> target in the file, whatever its name.
Calling it <code>all</code> is a convention (GNU Coding Standards), not a requirement.</p>
<h2 id="pattern-rules">Pattern rules</h2>
<p><code>$(OUT)/%: %.c</code> means &ldquo;<code>out/&lt;anything&gt;</code> is built from <code>&lt;anything&gt;.c</code>&rdquo;. One
rule covers <code>out/tinybox</code>, <code>out/scratch</code>, and any future file. Some IDEs
(e.g. CLion) only list named targets, which is why <code>tinybox:</code> and <code>scratch:</code>
exist as aliases.</p>
<h2 id="the-makefile-as-a-prerequisite">The Makefile as a prerequisite</h2>
<p>If only <code>tinybox.c</code> is listed, changing <code>CFLAGS</code> doesn&rsquo;t trigger a rebuild:
make says <code>Nothing to be done</code> and your new warning flag silently does
nothing. Listing <code>Makefile</code> fixes that. (<code>$&lt;</code> still picks <code>tinybox.c</code>,
since it&rsquo;s first.)</p>
<h2 id="order-only-prerequisites--out">Order-only prerequisites: <code>| $(OUT)</code></h2>
<p>Everything after <code>|</code> must <strong>exist</strong> before the recipe runs, but its timestamp
is ignored. That matters for directories: a directory&rsquo;s mtime changes
whenever a file inside it is added or removed, so as a normal prerequisite it
would make targets look out of date constantly.</p>
<h2 id="phony"><code>.PHONY</code></h2>
<p>Marks targets that are names, not files. Without it:</p>
<ul>
<li>A file called <code>clean</code> in the directory would make <code>make clean</code> think it&rsquo;s
up to date.</li>
<li>make goes looking for <strong>built-in rules</strong>. It has one for <code>foo</code> from
<code>foo.c</code>, so without <code>.PHONY</code>, <code>make tinybox</code> would build <code>out/tinybox</code> and
<em>then</em> run <code>gcc $(CFLAGS) tinybox.c out/tinybox -o tinybox</code>, building a stray
<code>./tinybox</code> and feeding the binary in as an extra input. Phony targets skip
the built-in rule search.</li>
</ul>
<p>(Fun fact: with no Makefile at all, <code>make scratch</code> works: the built-in rule
runs <code>cc scratch.c -o scratch</code>.)</p>
<h2 id="handy-flags">Handy flags</h2>
<ul>
<li><code>make -n</code>: dry run, print the commands without running them</li>
<li><code>make -B</code>: rebuild everything regardless of timestamps</li>
<li><code>make -p</code>: dump the database, including all built-in rules</li>
</ul>
<p>References: <code>info make</code> (the GNU make manual), especially &ldquo;Rule Syntax&rdquo;,
&ldquo;Pattern Rules&rdquo;, &ldquo;Automatic Variables&rdquo;, &ldquo;Phony Targets&rdquo;, &ldquo;Prerequisite Types&rdquo;.</p>
]]></content:encoded></item><item><title>Reading GCC error messages</title><link>https://ksh.fyi/notes/reading-gcc-errors/</link><pubDate>Sat, 03 Oct 2026 20:30:00 +0530</pubDate><guid>https://ksh.fyi/notes/reading-gcc-errors/</guid><description>&lt;p&gt;Every GCC diagnostic has the same shape:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;scratch.c:3:13: error: initialization discards &amp;#39;const&amp;#39; qualifier from pointer target type [-Werror=discarded-qualifiers]
3 | char *s = &amp;#34;scratching surface&amp;#34;;
| ^~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="line-1-filelinecol-severity-message-flag"&gt;Line 1: &lt;code&gt;file:line:col: severity: message [flag]&lt;/code&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;scratch.c:3:13&lt;/code&gt;&lt;/strong&gt;: where. Columns start at 1. Most terminals/editors
make this clickable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;severity&lt;/strong&gt;: &lt;code&gt;error&lt;/code&gt; stops the build; &lt;code&gt;warning&lt;/code&gt; doesn&amp;rsquo;t; &lt;code&gt;note:&lt;/code&gt; adds
context to the diagnostic above it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;[-Werror=discarded-qualifiers]&lt;/code&gt;&lt;/strong&gt;: the flag that controls this check.
&lt;code&gt;-Werror=&lt;/code&gt; means it&amp;rsquo;s really the warning &lt;code&gt;-Wdiscarded-qualifiers&lt;/code&gt;, promoted
to an error by &lt;code&gt;-Werror&lt;/code&gt;. Search &lt;code&gt;man gcc&lt;/code&gt; for &lt;code&gt;/Wdiscarded-qualifiers&lt;/code&gt;.
&lt;code&gt;-Wno-&amp;lt;name&amp;gt;&lt;/code&gt; disables it, which is rarely the right fix.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-message-has-a-fixed-vocabulary"&gt;The message has a fixed vocabulary&lt;/h2&gt;
&lt;p&gt;The first words say &lt;em&gt;where in the code&lt;/em&gt; the problem is:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Every GCC diagnostic has the same shape:</p>
<pre tabindex="0"><code>scratch.c:3:13: error: initialization discards &#39;const&#39; qualifier from pointer target type [-Werror=discarded-qualifiers]
    3 |   char *s = &#34;scratching surface&#34;;
      |             ^~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
</code></pre><h2 id="line-1-filelinecol-severity-message-flag">Line 1: <code>file:line:col: severity: message [flag]</code></h2>
<ul>
<li><strong><code>scratch.c:3:13</code></strong>: where. Columns start at 1. Most terminals/editors
make this clickable.</li>
<li><strong>severity</strong>: <code>error</code> stops the build; <code>warning</code> doesn&rsquo;t; <code>note:</code> adds
context to the diagnostic above it.</li>
<li><strong><code>[-Werror=discarded-qualifiers]</code></strong>: the flag that controls this check.
<code>-Werror=</code> means it&rsquo;s really the warning <code>-Wdiscarded-qualifiers</code>, promoted
to an error by <code>-Werror</code>. Search <code>man gcc</code> for <code>/Wdiscarded-qualifiers</code>.
<code>-Wno-&lt;name&gt;</code> disables it, which is rarely the right fix.</li>
</ul>
<h2 id="the-message-has-a-fixed-vocabulary">The message has a fixed vocabulary</h2>
<p>The first words say <em>where in the code</em> the problem is:</p>
<table>
	<thead>
			<tr>
					<th>Phrase</th>
					<th>Means</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>initialization</code></td>
					<td>the <code>=</code> in a declaration (<code>T x = ...</code>)</td>
			</tr>
			<tr>
					<td><code>assignment</code></td>
					<td>a plain <code>x = ...</code></td>
			</tr>
			<tr>
					<td><code>passing argument N of 'f'</code></td>
					<td>a function call</td>
			</tr>
			<tr>
					<td><code>return</code></td>
					<td>a <code>return</code> statement</td>
			</tr>
	</tbody>
</table>
<p>The rest says <em>what&rsquo;s wrong</em>. Here: &ldquo;discards &lsquo;const&rsquo; qualifier&rdquo; = a <code>const</code>
would be lost (<code>const</code>/<code>volatile</code> are <em>qualifiers</em>); &ldquo;from pointer target
type&rdquo; = the <code>const</code> is on the thing <strong>pointed to</strong>, not on the pointer.</p>
<h2 id="lines-23-the-source-excerpt">Lines 2–3: the source excerpt</h2>
<p><code>^</code> marks the column, <code>~~~</code> underlines the whole expression it&rsquo;s blaming:
here the literal, not <code>s</code>.</p>
<h2 id="note-lines-compare-the-types"><code>note:</code> lines: compare the types</h2>
<p>Type errors usually come with a note giving both types:</p>
<pre tabindex="0"><code>error: passing argument 2 of &#39;execv&#39; from incompatible pointer type
note: expected &#39;char * const*&#39; but argument is of type &#39;const char * const*&#39;
</code></pre><p>Put them side by side and <a href="https://ksh.fyi/notes/reading-c-declarations/">read each right to left</a>.
The difference is the bug.</p>
<h2 id="the-last-line-whos-talking">The last line: who&rsquo;s talking</h2>
<p><code>gcc</code> is a <em>driver</em> that runs a pipeline: preprocessor → <strong><code>cc1</code></strong> (the
compiler) → <code>as</code> (assembler) → <code>ld</code> (linker, errors show up via
<code>collect2</code>). The prefix tells you which stage complained. <code>undefined reference to 'foo'</code> with no line number = linker: compilation succeeded,
the final link couldn&rsquo;t find <code>foo</code>.</p>
<h2 id="habits">Habits</h2>
<ul>
<li>Fix the <strong>first</strong> error first. One missing <code>;</code> or <code>}</code> can cause a cascade.</li>
<li>Read the <code>note:</code>s.</li>
<li>Look the flag up in <code>man gcc</code> before silencing it.</li>
</ul>
]]></content:encoded></item><item><title>Reading C declarations: const, pointers, and argv</title><link>https://ksh.fyi/notes/reading-c-declarations/</link><pubDate>Sat, 03 Oct 2026 20:20:00 +0530</pubDate><guid>https://ksh.fyi/notes/reading-c-declarations/</guid><description>&lt;h2 id="read-from-the-name-outward-right-to-left"&gt;Read from the name outward, right to left&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;char * const argv[]
argv[] → argv is an array
* const → of const pointers
char → to char
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Rules of thumb:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[]&lt;/code&gt; and &lt;code&gt;()&lt;/code&gt; next to the name bind first (&amp;ldquo;array of&amp;rdquo;, &amp;ldquo;function returning&amp;rdquo;).&lt;/li&gt;
&lt;li&gt;Then read leftward: each &lt;code&gt;*&lt;/code&gt; is &amp;ldquo;pointer to&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;const&lt;/code&gt; applies to whatever is &lt;strong&gt;immediately to its left&lt;/strong&gt;; if nothing
is to its left, it applies to the thing on its right.&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Declaration&lt;/th&gt;
&lt;th&gt;Can change the chars?&lt;/th&gt;
&lt;th&gt;Can repoint?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;char *p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;const char *p&lt;/code&gt; (same as &lt;code&gt;char const *p&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;char *const p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;const char *const p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="the-duplicate-const-trap"&gt;The duplicate-const trap&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="c1"&gt;// looks like &amp;#34;const pointers to const chars&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Both &lt;code&gt;const&lt;/code&gt;s are left of the &lt;code&gt;*&lt;/code&gt;, so both apply to &lt;code&gt;char&lt;/code&gt;: you&amp;rsquo;ve made the
chars const twice and the pointers not at all. GCC says:&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="read-from-the-name-outward-right-to-left">Read from the name outward, right to left</h2>
<pre tabindex="0"><code>char * const argv[]
            argv[]   → argv is an array
      * const        → of const pointers
char                 → to char
</code></pre><p>Rules of thumb:</p>
<ul>
<li><code>[]</code> and <code>()</code> next to the name bind first (&ldquo;array of&rdquo;, &ldquo;function returning&rdquo;).</li>
<li>Then read leftward: each <code>*</code> is &ldquo;pointer to&rdquo;.</li>
<li>A <code>const</code> applies to whatever is <strong>immediately to its left</strong>; if nothing
is to its left, it applies to the thing on its right.</li>
</ul>
<table>
	<thead>
			<tr>
					<th>Declaration</th>
					<th>Can change the chars?</th>
					<th>Can repoint?</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>char *p</code></td>
					<td>yes</td>
					<td>yes</td>
			</tr>
			<tr>
					<td><code>const char *p</code> (same as <code>char const *p</code>)</td>
					<td>no</td>
					<td>yes</td>
			</tr>
			<tr>
					<td><code>char *const p</code></td>
					<td>yes</td>
					<td>no</td>
			</tr>
			<tr>
					<td><code>const char *const p</code></td>
					<td>no</td>
					<td>no</td>
			</tr>
	</tbody>
</table>
<h2 id="the-duplicate-const-trap">The duplicate-const trap</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="k">const</span> <span class="o">*</span><span class="n">v</span><span class="p">[];</span>   <span class="c1">// looks like &#34;const pointers to const chars&#34;
</span></span></span></code></pre></div><p>Both <code>const</code>s are left of the <code>*</code>, so both apply to <code>char</code>: you&rsquo;ve made the
chars const twice and the pointers not at all. GCC says:</p>
<pre tabindex="0"><code>error: duplicate &#39;const&#39; declaration specifier [-Werror=duplicate-decl-specifier]
</code></pre><p>What was meant: <code>const char *const v[]</code>. The <code>const</code> for the pointer goes
<strong>after</strong> the <code>*</code>.</p>
<h2 id="why-execv-takes-char-const-argv">Why <code>execv</code> takes <code>char *const argv[]</code></h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">execv</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">path</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="k">const</span> <span class="n">argv</span><span class="p">[]);</span>
</span></span></code></pre></div><p>exec promises not to repoint the slots, but not to leave the chars alone,
even though it never writes to them. The fully-const signature would have
been <code>const char *const argv[]</code>, but C won&rsquo;t implicitly convert <code>char **</code> to
<code>const char *const *</code>, so every existing caller would have needed a cast.
POSIX kept the old signature and documents that the strings aren&rsquo;t modified
(<code>man 3p exec</code>, RATIONALE).</p>
<p>The other direction bites if you write const-correct code:</p>
<pre tabindex="0"><code>error: passing argument 2 of &#39;execv&#39; from incompatible pointer type [-Wincompatible-pointer-types]
note: expected &#39;char * const*&#39; but argument is of type &#39;const char * const*&#39;
</code></pre><p>The usual answer is an explicit cast at the call, <code>(char *const *)argv</code>,
which is safe because of the POSIX guarantee. (In GCC 14+ incompatible
pointer types are an error by default, even without <code>-Werror</code>.)</p>
<p>References: <code>man 3 exec</code>, <code>man 3p exec</code>; <a href="https://cdecl.org">cdecl.org</a> to
check your reading.</p>
]]></content:encoded></item><item><title>C string literals are read-only, but not const</title><link>https://ksh.fyi/notes/c-string-literals/</link><pubDate>Sat, 03 Oct 2026 20:10:00 +0530</pubDate><guid>https://ksh.fyi/notes/c-string-literals/</guid><description>&lt;p&gt;A string literal like &lt;code&gt;&amp;quot;/bin/sh&amp;quot;&lt;/code&gt; is stored in &lt;code&gt;.rodata&lt;/code&gt;, which is mapped
read-only (see &lt;a href="https://ksh.fyi/notes/c-memory-sections/"&gt;where things live in a C program&lt;/a&gt;).
But its C type is plain &lt;code&gt;char[N]&lt;/code&gt;, &lt;strong&gt;not&lt;/strong&gt; &lt;code&gt;const char[N]&lt;/code&gt; (C++ fixed this, C
kept it for historical reasons). So the compiler happily lets you do this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;/bin/sh&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// compiles without a warning by default
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;&amp;#39;X&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// undefined behavior; in practice SIGSEGV
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;$ ./ub
Segmentation fault (exit status 139 = 128 + SIGSEGV)
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="-wwrite-strings-make-the-compiler-tell-you"&gt;&lt;code&gt;-Wwrite-strings&lt;/code&gt;: make the compiler tell you&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;-Wwrite-strings&lt;/code&gt; gives literals the type &lt;code&gt;const char[N]&lt;/code&gt;, so pointing a plain
&lt;code&gt;char *&lt;/code&gt; at one becomes a warning (an error with &lt;code&gt;-Werror&lt;/code&gt;):&lt;/p&gt;</description><content:encoded><![CDATA[<p>A string literal like <code>&quot;/bin/sh&quot;</code> is stored in <code>.rodata</code>, which is mapped
read-only (see <a href="https://ksh.fyi/notes/c-memory-sections/">where things live in a C program</a>).
But its C type is plain <code>char[N]</code>, <strong>not</strong> <code>const char[N]</code> (C++ fixed this, C
kept it for historical reasons). So the compiler happily lets you do this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">char</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="s">&#34;/bin/sh&#34;</span><span class="p">;</span>   <span class="c1">// compiles without a warning by default
</span></span></span><span class="line"><span class="cl"><span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="sc">&#39;X&#39;</span><span class="p">;</span>            <span class="c1">// undefined behavior; in practice SIGSEGV
</span></span></span></code></pre></div><pre tabindex="0"><code>$ ./ub
Segmentation fault        (exit status 139 = 128 + SIGSEGV)
</code></pre><h2 id="-wwrite-strings-make-the-compiler-tell-you"><code>-Wwrite-strings</code>: make the compiler tell you</h2>
<p><code>-Wwrite-strings</code> gives literals the type <code>const char[N]</code>, so pointing a plain
<code>char *</code> at one becomes a warning (an error with <code>-Werror</code>):</p>
<pre tabindex="0"><code>scratch.c:3:13: error: initialization discards &#39;const&#39; qualifier from pointer target type [-Werror=discarded-qualifiers]
    3 |   char *s = &#34;scratching surface&#34;;
      |             ^~~~~~~~~~~~~~~~~~~~
</code></pre><p>The fix is to say what you mean: <code>const char *s = &quot;...&quot;;</code>. It&rsquo;s not part of
<code>-Wall</code> or <code>-Wextra</code>; you have to add it yourself.</p>
<h2 id="pointer-to-a-literal-vs-array-initialized-from-one">Pointer to a literal vs. array initialized from one</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">char</span> <span class="o">*</span><span class="n">s</span> <span class="o">=</span> <span class="s">&#34;/bin/sh&#34;</span><span class="p">;</span>   <span class="c1">// A
</span></span></span><span class="line"><span class="cl"><span class="kt">char</span>  <span class="n">s</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&#34;/bin/sh&#34;</span><span class="p">;</span> <span class="c1">// B
</span></span></span></code></pre></div><p>They look almost identical but are very different:</p>
<ul>
<li><strong>A</strong> is a pointer. It points <em>at the literal itself</em>, in <code>.rodata</code>.
With <code>-Wwrite-strings</code> this is the error above.</li>
<li><strong>B</strong> is an array of 8 <code>char</code>s (7 + <code>'\0'</code>). The literal is only its
<em>initializer</em>: the bytes are <strong>copied</strong> into the array. No pointer, no
<code>const</code> dropped, and the array is yours to modify.</li>
</ul>
<table>
	<thead>
			<tr>
					<th></th>
					<th>The variable <code>s</code></th>
					<th>The bytes <code>&quot;/bin/sh&quot;</code></th>
					<th><code>s[0] = 'X'</code></th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>A (local)</td>
					<td>8-byte pointer on the stack</td>
					<td>the literal in <code>.rodata</code></td>
					<td>UB / SIGSEGV</td>
			</tr>
			<tr>
					<td>B (local)</td>
					<td>the array itself, on the stack</td>
					<td>a copy, inside the array</td>
					<td>fine</td>
			</tr>
			<tr>
					<td>B (<code>static</code> or global)</td>
					<td>the array itself, in <code>.data</code></td>
					<td>a copy, inside the array</td>
					<td>fine</td>
			</tr>
	</tbody>
</table>
<p>More differences:</p>
<ul>
<li>A can be repointed (<code>s = &quot;other&quot;;</code>). B can&rsquo;t: arrays aren&rsquo;t assignable,
only their contents are.</li>
<li><code>sizeof s</code> is the pointer size for A and the array size for B. For
<code>&quot;/bin/sh&quot;</code> both happen to be 8 on a 64-bit machine; use a longer string to
see them differ.</li>
</ul>
<p>The real question isn&rsquo;t &ldquo;binary or stack&rdquo;, it&rsquo;s <strong>&ldquo;am I pointing at the
literal, or do I own a copy?&rdquo;</strong></p>
<p>References: the C23 standard, sections &ldquo;String literals&rdquo; and &ldquo;Initialization&rdquo;;
<code>man gcc</code> (<code>-Wwrite-strings</code>).</p>
]]></content:encoded></item><item><title>Where things live in a C program: .text, .rodata, .data, .bss, stack</title><link>https://ksh.fyi/notes/c-memory-sections/</link><pubDate>Sat, 03 Oct 2026 20:00:00 +0530</pubDate><guid>https://ksh.fyi/notes/c-memory-sections/</guid><description>&lt;p&gt;A compiled C program is an ELF file split into &lt;strong&gt;sections&lt;/strong&gt;. The loader maps
them into memory with different permissions, and those permissions are what
the CPU enforces at runtime.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Section&lt;/th&gt;
&lt;th&gt;Holds&lt;/th&gt;
&lt;th&gt;Permissions&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;machine code&lt;/td&gt;
&lt;td&gt;read + execute&lt;/td&gt;
&lt;td&gt;&lt;code&gt;main&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.rodata&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;read-only data&lt;/td&gt;
&lt;td&gt;read (no write)&lt;/td&gt;
&lt;td&gt;string literals, &lt;code&gt;static const&lt;/code&gt; tables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;initialized globals / statics&lt;/td&gt;
&lt;td&gt;read + write&lt;/td&gt;
&lt;td&gt;&lt;code&gt;static char buf[] = &amp;quot;hi&amp;quot;;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.bss&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;zero-initialized globals / statics&lt;/td&gt;
&lt;td&gt;read + write&lt;/td&gt;
&lt;td&gt;&lt;code&gt;static int counter;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;stack&lt;/td&gt;
&lt;td&gt;locals, return addresses&lt;/td&gt;
&lt;td&gt;read + write&lt;/td&gt;
&lt;td&gt;&lt;code&gt;char buf[8];&lt;/code&gt; inside a function&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.data&lt;/code&gt; and &lt;code&gt;.rodata&lt;/code&gt; are both &lt;strong&gt;in the binary&lt;/strong&gt;; &amp;ldquo;in the binary&amp;rdquo; doesn&amp;rsquo;t mean
read-only. The section decides that.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.bss&lt;/code&gt; takes no space in the file (type &lt;code&gt;NOBITS&lt;/code&gt;): the loader just hands out
zeroed memory.&lt;/li&gt;
&lt;li&gt;Writing to &lt;code&gt;.rodata&lt;/code&gt; (e.g. through a pointer to a string literal) → the page
isn&amp;rsquo;t writable → &lt;code&gt;SIGSEGV&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="see-it-yourself"&gt;See it yourself&lt;/h2&gt;
&lt;p&gt;Sections and their flags (&lt;code&gt;A&lt;/code&gt; = alloc, &lt;code&gt;W&lt;/code&gt; = write, &lt;code&gt;X&lt;/code&gt; = exec):&lt;/p&gt;</description><content:encoded><![CDATA[<p>A compiled C program is an ELF file split into <strong>sections</strong>. The loader maps
them into memory with different permissions, and those permissions are what
the CPU enforces at runtime.</p>
<table>
	<thead>
			<tr>
					<th>Section</th>
					<th>Holds</th>
					<th>Permissions</th>
					<th>Example</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>.text</code></td>
					<td>machine code</td>
					<td>read + execute</td>
					<td><code>main</code></td>
			</tr>
			<tr>
					<td><code>.rodata</code></td>
					<td>read-only data</td>
					<td>read (no write)</td>
					<td>string literals, <code>static const</code> tables</td>
			</tr>
			<tr>
					<td><code>.data</code></td>
					<td>initialized globals / statics</td>
					<td>read + write</td>
					<td><code>static char buf[] = &quot;hi&quot;;</code></td>
			</tr>
			<tr>
					<td><code>.bss</code></td>
					<td>zero-initialized globals / statics</td>
					<td>read + write</td>
					<td><code>static int counter;</code></td>
			</tr>
			<tr>
					<td>stack</td>
					<td>locals, return addresses</td>
					<td>read + write</td>
					<td><code>char buf[8];</code> inside a function</td>
			</tr>
	</tbody>
</table>
<ul>
<li><code>.data</code> and <code>.rodata</code> are both <strong>in the binary</strong>; &ldquo;in the binary&rdquo; doesn&rsquo;t mean
read-only. The section decides that.</li>
<li><code>.bss</code> takes no space in the file (type <code>NOBITS</code>): the loader just hands out
zeroed memory.</li>
<li>Writing to <code>.rodata</code> (e.g. through a pointer to a string literal) → the page
isn&rsquo;t writable → <code>SIGSEGV</code>.</li>
</ul>
<h2 id="see-it-yourself">See it yourself</h2>
<p>Sections and their flags (<code>A</code> = alloc, <code>W</code> = write, <code>X</code> = exec):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">readelf -SW ./prog <span class="p">|</span> grep -E <span class="s1">&#39;\.text|\.rodata|\.data |\.bss&#39;</span>
</span></span></code></pre></div><pre tabindex="0"><code>  [12] .text    PROGBITS ... AX
  [14] .rodata  PROGBITS ... A
  [22] .data    PROGBITS ... WA
  [23] .bss     NOBITS   ... WA
</code></pre><p>Sections are grouped into <strong>segments</strong> (<code>readelf -lW ./prog</code>, the <code>LOAD</code>
lines), and segments are what actually get mapped. At runtime, compare
addresses (<code>printf(&quot;%p&quot;, (void *)ptr)</code>) against <code>/proc/self/maps</code>:</p>
<pre tabindex="0"><code>aaaabfd80000-aaaabfd81000 r-xp ... /path/to/prog   &lt;- .text + .rodata (literal was here)
aaaabfd9f000-aaaabfda0000 r--p ... /path/to/prog   &lt;- RELRO (.got etc., made read-only after startup)
aaaabfda0000-aaaabfda1000 rw-p ... /path/to/prog   &lt;- .data / .bss (static array was here)
ffffe15ba000-ffffe15db000 rw-p ...  [stack]         &lt;- local array was here
</code></pre><p>Note: on this arm64 build <code>.rodata</code> shares the <code>r-xp</code> segment with <code>.text</code>.
Other toolchains (e.g. x86-64 with <code>-z separate-code</code>) give it its own <code>r--p</code>
mapping. Either way: not writable.</p>
<p>References: <code>man 5 elf</code>, <code>man 1 readelf</code>, <code>man 5 proc</code> (<code>/proc/pid/maps</code>).</p>
]]></content:encoded></item></channel></rss>