<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Pointer on TouchingFish.top</title><link>https://touchingfish.top/tags/pointer/</link><description>Recent content in Pointer 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/pointer/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/array-and-pointer/</link><pubDate>Mon, 04 May 2020 00:00:00 +0000</pubDate><guid>https://touchingfish.top/2020/array-and-pointer/</guid><description>&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;数组和指针的关系：在 C 语言中，数组名其实是一个指针，指向数组的第一个元素。例如，如果你有一个数组 &lt;code&gt;int arr[10];&lt;/code&gt;，那么 &lt;code&gt;arr&lt;/code&gt; 实际上是指向 &lt;code&gt;arr[0]&lt;/code&gt; 的指针。这意味着你可以用指针来遍历数组，也可以通过指针操作数组元素。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;通过指针访问数组：当你理解了数组名是一个指针后，你就能明白为什么 &lt;code&gt;*(arr + 1)&lt;/code&gt; 等价于 &lt;code&gt;arr[1]&lt;/code&gt;。这个表达式的意思是“通过指针 &lt;code&gt;arr&lt;/code&gt;，向后移动一个位置，然后取该位置的值”。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;指针的灵活性：数组在定义时大小是固定的，而指针则更为灵活，可以指向任何内存位置。这使得你在处理数据时，能够更灵活地进行操作，例如动态分配内存或处理不同类型的数据结构。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;内存理解：通过理解指针如何操作数组，你会对内存管理有更深入的认识。这在 C 语言中尤为重要，因为你需要手动管理内存，避免错误操作。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;把数组和指针放在一起学习，有助于更深入地理解两者的工作原理，可以更好地理解内存和数据访问的底层机制，让编程更加高效和可靠。&lt;/p&gt;
&lt;h2 id="数组"&gt;数组&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;具有相同的数据类型&lt;/li&gt;
&lt;li&gt;使用过程中需要保留原始数据&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="一维数组"&gt;一维数组&lt;/h3&gt;
&lt;p&gt;数组初始化中的常量表达式：&lt;strong&gt;数组名 [常量表达式]&lt;/strong&gt;（通常为常量，或宏定义的符号常量）表示数组的大小，这是因为&lt;strong&gt;一个函数的函数栈空间大小是固定的&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;数组元素的引用方式是 &lt;strong&gt;数组名 [下标]&lt;/strong&gt;（从 0 开始）访问数组中的数据&lt;/p&gt;
&lt;h4 id="数组在内存中的存放"&gt;数组在内存中的存放&lt;/h4&gt;
&lt;p&gt;数组存放在一块连续的内存中，元素间的地址根据其数据类型所占字节数决定。&lt;/p&gt;
&lt;h4 id="访问越界-out-of-bounds-access"&gt;访问越界 (Out-of-Bounds Access)&lt;/h4&gt;
&lt;p&gt;C 语言的内存管理：先定义的变量在高地址，后定义的变量在低地址。&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;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="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&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="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 定义一个整数变量 i，并赋值为 10
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// 定义一个大小为 3 的整数数组
&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;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Before out-of-bounds access:&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="nf"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;i = %d&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 class="n"&gt;i&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="nf"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;arr[0] = %d, arr[1] = %d, arr[2] = %d&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 class="n"&gt;arr&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="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 访问数组的第 4 个元素（越界访问）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&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&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;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;After out-of-bounds access:&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="nf"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;i = %d&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 class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 查看 i 的值是否被改变
&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;arr[0] = %d, arr[1] = %d, arr[2] = %d&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 class="n"&gt;arr&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="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&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="p"&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;i&lt;/code&gt;的地址（高地址），&lt;code&gt;i&lt;/code&gt;的值（该地址中的值）发生改变。&lt;/p&gt;</description></item></channel></rss>