<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tony Bai &#187; dereference</title>
	<atom:link href="http://tonybai.com/tag/dereference/feed/" rel="self" type="application/rss+xml" />
	<link>https://tonybai.com</link>
	<description>一个程序员的心路历程</description>
	<lastBuildDate>Mon, 08 Jun 2026 23:32:23 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>简析多级指针解引用</title>
		<link>https://tonybai.com/2013/03/23/multi-dimension-pointer-in-c/</link>
		<comments>https://tonybai.com/2013/03/23/multi-dimension-pointer-in-c/#comments</comments>
		<pubDate>Sat, 23 Mar 2013 02:38:18 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Blog]]></category>
		<category><![CDATA[Blogger]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[dereference]]></category>
		<category><![CDATA[Pointer]]></category>
		<category><![CDATA[Programmer]]></category>
		<category><![CDATA[博客]]></category>
		<category><![CDATA[学习]]></category>
		<category><![CDATA[思考]]></category>
		<category><![CDATA[指针]]></category>
		<category><![CDATA[程序员]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[解引用]]></category>
		<category><![CDATA[语法]]></category>

		<guid isPermaLink="false">http://tonybai.com/?p=1227</guid>
		<description><![CDATA[指针是C语言中公认的最为强大的语法要素，但同时也是最难理解的语法要素，它曾给程序员带来了无数麻烦和痛苦，以致于在C语言之后诞生的很多新兴 语言中我们再也难觅指针的身影了。 下面是一个最简单的C语言指针的例子： int a = 5; int *p = &#38;a; 其中p就是一个指针变量。如果C语言中仅仅存在这类指针，那显然指针不会形成&#8220;大患&#8221;。经常地我们会在代码中看到下面的情形： int **q = &#38;p; int ***z = &#38;q; 随着符号&#39;*&#39;个数的增加，C代码的理解复杂度似乎也曾指数级别增长似的。像q、z这样的指向指针的指针(pointer to pointer to &#8230;)变量，中文俗称&#8220;多级指针&#8221;。不过在一些正式的英文C语言教程中，我没能找到其正式的英文说法。在老外的这些书 中，它们多被称为pointer to pointer (to pointer to &#8230;.)。多级指针的确是很难理解的，特别当与函数、数组等联合在一起使用时。今天在写代码时恰好撞见了多级指针，于是就打算在这里说说对多级指针以及 其解引用的一些粗浅理解。 指针究竟是啥？ 和普通变量想比，指针变量到底有何不同，究竟何为指针(变量)？我们来看一个例子： int a = 5; int *p = &#38;a; printf(&#34;a addr = [%p]\n&#34;, &#38;a); printf(&#34;a content = [%d]\n&#34;, a); printf(&#34;p addr [...]]]></description>
			<content:encoded><![CDATA[<p>指针是<a href="http://en.wikipedia.org/wiki/C_(programming_language)">C语言</a>中公认的最为强大的语法要素，但同时也是最难理解的语法要素，它曾给程序员带来了无数麻烦和痛苦，以致于在C语言之后诞生的很多新兴 语言中我们再也难觅指针的身影了。</p>
<p>下面是一个最简单的C语言指针的例子：<br />
	<span style="font-family:courier new,courier,monospace;">int a = 5;<br />
	int *p = &amp;a;</span></p>
<p>其中p就是一个指针变量。如果C语言中仅仅存在这类指针，那显然指针不会形成&ldquo;大患&rdquo;。经常地我们会在代码中看到下面的情形：</p>
<p><span style="font-family:courier new,courier,monospace;">int **q = &amp;p;<br />
	int ***z = &amp;q;</span></p>
<p>随着符号&#39;*&#39;个数的增加，C代码的理解复杂度似乎也曾指数级别增长似的。像q、z这样的指向指针的指针(pointer to pointer to &#8230;)变量，中文俗称&ldquo;<b>多级指针</b>&rdquo;。不过在一些正式的英文C语言教程中，我没能找到其正式的英文说法。在老外的这些书 中，它们多被称为pointer to pointer (to pointer to &#8230;.)。多级指针的确是很难理解的，特别当与函数、数组等联合在一起使用时。今天在写代码时恰好撞见了多级指针，于是就打算在这里说说对多级指针以及 其解引用的一些粗浅理解。</p>
<p><b>指针究竟是啥？</b></p>
<p>和普通变量想比，指针变量到底有何不同，究竟何为指针(变量)？我们来看一个例子：</p>
<p><span style="font-family:courier new,courier,monospace;">int a = 5;<br />
	int *p = &amp;a;</span></p>
<p>printf(&quot;a addr = [%p]\n&quot;, &amp;a);<br />
	printf(&quot;a content = [%d]\n&quot;, a);<br />
	printf(&quot;p addr = [%p]\n&quot;, &amp;p);<br />
	printf(&quot;p content = [%p]\n&quot;, p);<br />
	printf(&quot;*p = [%d]\n&quot;, *p);</p>
<p>*p = 6;<br />
	printf(&quot;after modify, *p = [%d]\n&quot;, *p);</p>
<p>编译这个小程序并执行，输出结果如下：</p>
<p><span style="font-family:courier new,courier,monospace;">a addr = [0xbfb609b8]<br />
	a content = [5]<br />
	p addr = [0xbfb609bc]<br />
	p content = [0xbfb609b8]<br />
	*p = [5]<br />
	after modify, *p = [6]</span></p>
<p>通过两个变量的addr，我们可以看到a、p两个变量都是在栈上分配的变量。不同的是普通整型变量a对应的内存单元(a content)中存储的值为整型值5，是一个数值；而变量p对应的内存单元(p content)中存储的值为0xbfb609b8，是变量a的地址，用栈变量简图可以表示如下：</p>
<p><span style="font-family:courier new,courier,monospace;">| &#8230;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
	|<u>0xbfb609b8</u>| &lt;- &amp;p [0xbfb609bc]<br />
	|5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | &lt;- &amp;a [<u>0xbfb609b8</u>]<br />
	| &#8230;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span></p>
<p>可以看出指针变量的<b>第一个特点</b>是它是一种以存储其他变量地址为目的的变量。一个T类型的指针变量(一级指针)就是一个存储了某T类 型值变量的地址的内存单元。</p>
<p>例子中最后那个输出是对指针的解引用(dereference)操作，指针的解引用操作的结果是得到指针所指的地址上的变量的值。在这个例子中指 针所指到内存地址为0xbfb609b8，也就是a变量的位置，因此*p的结果为变量a的值，即5。因此我们得到指针变量的<b>第二个特点</b>： 通过对指针的解引用，我们可以获得其指向的内存单元所表示的值。</p>
<p>在例子中，我们看到了这行代码 *p = 6，并发现执行这行代码后，a变量的值变为了6。这就是指针的<b>第三个特点</b>：当解引用作左值时，它可以修改其所指内存地址上变量的值。a被修改后的栈变量分布简图：</p>
<p><span style="font-family:courier new,courier,monospace;">| &#8230;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
	|0xbfb609b8| &lt;- &amp;p [0xbfb609bc]<br />
	|6 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | &lt;- &amp;a [0xbfb609b8]<br />
	| &#8230;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span></p>
<p><b>二级指针</b></p>
<p>我们再来分析一下下面的示例程序的输出结果。</p>
<p><span style="font-family:courier new,courier,monospace;">int a = 5;<br />
	int b = 13;<br />
	int *p = &amp;a;<br />
	printf(&quot;*p = %d\n&quot;, *p);&nbsp;<br />
	int **q = &amp;p;<br />
	(*q) = &amp;b;<br />
	printf(&quot;*p = %d\n&quot;, *p);</span></p>
<p>根据前面的分析，第一次*p输出时p指向a的地址，对p解引用的结果就是a所在内存单元的值，即5。接下来的代码分析起来就需要谨慎一些了。我们先来看看 int **q = &amp;p这行代码。根据对一级指针的分析，我们可以将int **q理解成(int*) *q，这样q指向的地址就是一个int*型的变量的内存地址，该地址上的值本身也是一个地址值。在这个例子中，(int*) *q = &amp;p; 也就是说q中存储的值就是变量p的地址。通过*q我们可以得到p中存储的地址值(&amp;a)；而若*q作为左值，显然就是修改p中存储的地址值喽，因 此(*q) = &amp;b则相当于p = &amp;b，则第二个*p的输出结果为变量b所在内存单元的值，即13。</p>
<p>在修改*q前，栈上内存布局：</p>
<p><span style="font-family:courier new,courier,monospace;">| &#8230;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
	|<u>0xbf830ec8</u>| &lt;- &amp;q [0xbf830ecc]<br />
	|0xbf830ec0| &lt;- &amp;p [<u>0xbf830ec8</u>]<br />
	|11 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | &lt;- &amp;b [0xbf830ec4]<br />
	|5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | &lt;- &amp;a [0xbf830ec0]<br />
	| &#8230;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span></p>
<p>在修改*q的值后，栈上内存布局：</p>
<p><span style="font-family:courier new,courier,monospace;">| &#8230;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
	|0xbf830ec8| &lt;- &amp;q [0xbf830ecc]<br />
	|<u>0xbf830ec4</u>| &lt;- &amp;p [0xbf830ec8] /* 通过*q修改 */<br />
	|11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | &lt;- &amp;b [<u>0xbf830ec4</u>]<br />
	|5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | &lt;- &amp;a [0xbf830ec0]<br />
	| &#8230;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span></p>
<p>再来分析一下**q的值又是啥呢？有了前面的铺垫：*q &lt;=&gt; p，那**q &lt;=&gt; *(*q) &lt;=&gt; *p，其值自然就明了了，就是b的值。</p>
<p><b>多级指针</b></p>
<p>有了一级指针和二级指针的分析打基础，当我们遇到更多*的时候，只是遵循这个方法耐心分析就是了，比如：</p>
<p><span style="font-family:courier new,courier,monospace;">int a = 5;<br />
	int *p = &amp;a;<br />
	int **q = &amp;p;<br />
	int ***z = &amp;q;</span></p>
<p>我们可以对比着前面一、二级指针的理解方法来理解这三个指针p、q和z：<br />
	&nbsp;&nbsp;&nbsp; &#8211; 一级指针p自身存储的是整型值变量a的地址，对一级指针解引用(*p)得到的是值变量a的值；*p作左值，修改的是变量a的值；<br />
	&nbsp;&nbsp;&nbsp; &#8211; 二级指针q自身存储的是一级整型指针变量p的地址，对二级指针解引用(*q)得到的是一级指针p自身存储的值(a的地址:&amp;a)；*p作左值时，修改的一级指针p的指向；<br />
	&nbsp;&nbsp;&nbsp; &#8211; 三级指针z自身存储的是二级整型指针变量q的地址，对三级指针解引用(*z)得到的是二级指针q自身存储的值，也就是p的地址(&amp;p)；对*z再 解引用(**z)，相当于得到p自身存储的值，也就是a的地址&amp;a；对**z再解引用，即***z，相当于得到a自身存储的变量值，即5。用一个 等价式可以更形象的表达：***z &lt;=&gt; **(*z) &lt;=&gt; **q &lt;=&gt; *(*q) &lt;=&gt; *p &lt;=&gt; 5。<br />
	&nbsp;&nbsp;&nbsp; &#8211; 更高级别的指针可依次类推。不过如果再对***z解引用，即****z，那则相当于对整型数5（非地址）进行解引用，会出现编译错误： 一元 &lsquo;*&rsquo;参数类型无效(有&lsquo;int&rsquo;)。</p>
<p style='text-align:left'>&copy; 2013, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2013/03/23/multi-dimension-pointer-in-c/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
