<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Compiler on TouchingFish.top</title><link>https://touchingfish.top/tags/compiler/</link><description>Recent content in Compiler on TouchingFish.top</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Sun, 19 Oct 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://touchingfish.top/tags/compiler/index.xml" rel="self" type="application/rss+xml"/><item><title>Rust 的"解法"</title><link>https://touchingfish.top/2025/learning-rust-c-memory-safety/</link><pubDate>Sun, 19 Oct 2025 00:00:00 +0000</pubDate><guid>https://touchingfish.top/2025/learning-rust-c-memory-safety/</guid><description>&lt;p&gt;翻出 2020 年写的 C 语言博客。&lt;/p&gt;
&lt;p&gt;&lt;a href="../../2020/array-and-pointer/"&gt;数组与指针&lt;/a&gt;，&lt;a href="../../2020/journey-to-c-language/"&gt;内存布局与调试&lt;/a&gt;。那时候写得挺认真的，后来还画内存图，标注高地址低地址，解释为什么 &lt;code&gt;arr[3] = 20&lt;/code&gt; 能把旁边变量 &lt;code&gt;i&lt;/code&gt; 的值也改了。文章结尾写的是：&amp;ldquo;为避免这种错误，始终要确保在使用数组时不越界访问。&amp;rdquo;&lt;/p&gt;
&lt;p&gt;现在回头看，那句话翻译成人话大概是：你最好别写错。&lt;/p&gt;
&lt;p&gt;因为写错了编译器不会告诉你。&lt;/p&gt;
&lt;p&gt;学 Rust 的时候，很多东西越看越像是对 C 里那些经典问题的系统性回应。不是&amp;quot;Rust 比 C 好&amp;quot;——是 Rust 把二十年来系统编程中最常见的错误模式，逐个编码进了编译器和类型系统里。一个 Rust 新手在编译期被挡下的问题，可能比一个 C 老手十年遇到的运行时 bug 还全面。&lt;/p&gt;
&lt;h2 id="指针三兄弟"&gt;指针三兄弟&lt;/h2&gt;
&lt;p&gt;NULL、野的、悬空——C 程序员的日常恐惧来源。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;int *p = NULL; *p = 1;&lt;/code&gt; 编译通过。运行到这一行，操作系统发来 SIGSEGV。不是编译期能发现的事——NULL 是一个合法的指针值，只是指向的地址不可访问。编译器没有语义层面的&amp;quot;这个指针可能为空&amp;quot;的概念。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;int* f() { int x = 5; return &amp;amp;x; }&lt;/code&gt; 也编译通过。x 在 f 的栈帧里，函数返回后栈帧被回收，返回的地址指向一块随时可能被覆盖的内存。这个地址仍然&amp;quot;合法&amp;quot;——它在一个可访问的内存段内，只是内容不受你控制。有时候打印出 5，有时候打印出随机数，有时候什么都没发生，取决于后续函数调用有没有踩到同一块栈空间。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;free(p); *p = 1;&lt;/code&gt; 还是编译通过。free 之后那块堆内存被标记为可用，但 p 的值没变，仍然指向原地址。继续通过 p 读写，运气好时数据还没被覆盖，程序照常运行——这比直接崩溃更危险，因为你不知道错误已经埋下了。&lt;/p&gt;
&lt;p&gt;Rust 的做法很简单：没有 NULL。没有悬垂引用。没有 use-after-free。&lt;/p&gt;</description></item><item><title>从源代码到可执行程序</title><link>https://touchingfish.top/2020/journey-to-c-language/</link><pubDate>Fri, 01 May 2020 00:00:00 +0000</pubDate><guid>https://touchingfish.top/2020/journey-to-c-language/</guid><description>&lt;p&gt;编程是和计算机对话的过程。我们按照计算机能理解的方式，用抽象的编程语言来描述算法和逻辑，给计算机下达指令，让它完成工作。&lt;/p&gt;
&lt;p&gt;在 C 语言的程序开发过程中，预处理、编译和汇编是将源代码转化为可执行程序的几个关键阶段。理解它们之间的关系有助于更好地理解C语言的编译过程。下面将详细介绍这几个阶段的作用及其相互关系。&lt;/p&gt;
&lt;h2 id="预处理"&gt;预处理&lt;/h2&gt;
&lt;p&gt;预处理（Preprocessing）是 C 语言编译过程中的第一个阶段。在这个阶段，C 编译器的预处理器（Preprocessor）处理代码中的预处理指令，如 &lt;code&gt;#include&lt;/code&gt;、&lt;code&gt;#define&lt;/code&gt;、&lt;code&gt;#ifdef&lt;/code&gt; 等。这些指令通常用来管理宏定义、文件包含、条件编译等。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;宏替换：将所有定义的宏替换为相应的代码。例如：&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="cp"&gt;#define PI 3.14
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在程序中所有出现 &lt;code&gt;PI&lt;/code&gt; 的地方，都会被替换为 &lt;code&gt;3.14&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;文件包含：将 &lt;code&gt;#include&lt;/code&gt; 指定的头文件内容插入到当前文件中。例如：&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="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;会将 &lt;code&gt;stdio.h&lt;/code&gt; 文件的内容包含到当前源文件中。在一段代码中，通常会在首行引用头文件，指令告诉 &lt;code&gt;CPP&lt;/code&gt; 从系统库中获取 &lt;code&gt;stdio.h&lt;/code&gt;，并拷贝其中的文本到当前的源文件里。（&lt;strong&gt;乱用头文件会影响编译的效率&lt;/strong&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;条件编译：根据条件判断是否编译某些代码段。例如：&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="cp"&gt;#ifdef DEBUG
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Debug mode&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#endif
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;只有在定义了 &lt;code&gt;DEBUG&lt;/code&gt; 宏的情况下，&lt;code&gt;printf&lt;/code&gt; 语句才会被编译。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;预处理后的输出是一个扩展的源文件，其中所有的预处理指令都已经被处理和替换。这个文件将传递给编译器的下一阶段。&lt;/p&gt;
&lt;h2 id="编译"&gt;编译&lt;/h2&gt;
&lt;p&gt;编译（Compilation）是将预处理后的 C 代码转换为汇编代码的过程。编译器会检查代码的语法和结构，优化代码，并生成汇编代码。&lt;/p&gt;
&lt;p&gt;在这个阶段，编译器会执行以下操作：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;语法分析：检查代码的语法是否正确，确保符合C语言的语法规则。&lt;/li&gt;
&lt;li&gt;语义分析：检查代码的语义是否合理，例如变量类型的正确性、函数调用的合法性等。&lt;/li&gt;
&lt;li&gt;优化：编译器可能会对代码进行优化，以提高生成代码的运行效率。&lt;/li&gt;
&lt;li&gt;生成汇编代码：将C语言的代码转换为特定硬件架构的汇编语言。例如，&lt;code&gt;gcc&lt;/code&gt; 编译器将生成 &lt;code&gt;.s&lt;/code&gt; 文件（包含汇编代码）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;汇编代码是一种低级语言，更接近机器代码，但仍然是人类可读的（如 &lt;code&gt;MOV&lt;/code&gt;, &lt;code&gt;ADD&lt;/code&gt; 等指令）。&lt;/p&gt;
&lt;h2 id="汇编"&gt;汇编&lt;/h2&gt;
&lt;p&gt;汇编（Assembly）是将汇编代码转换为机器代码的过程。在这个阶段，汇编器（Assembler）会将汇编代码翻译成目标机器的二进制指令，这些指令可以直接在处理器上执行。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;生成目标文件：汇编器将生成一个目标文件（通常是 &lt;code&gt;.o&lt;/code&gt; 或 &lt;code&gt;.obj&lt;/code&gt; 文件），它包含了机器代码以及一些调试和链接信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;目标文件最终会被打包成可执行文件（例如 &lt;code&gt;.exe&lt;/code&gt; 文件），可以直接在计算机上运行。&lt;/p&gt;
&lt;h2 id="关系与流程"&gt;关系与流程&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;预处理是编译的第一步，处理宏、头文件等预处理指令，生成一个纯C语言的源代码文件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;编译阶段将预处理后的源代码翻译为汇编代码，这个过程中包括了语法检查、优化等重要步骤。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;汇编阶段将汇编代码转换为机器代码，生成目标文件。&lt;/p&gt;</description></item></channel></rss>