int array[5];
int *array_begin = &array[0];
int *array_end = &array[5];
在这种情况下是否合法的C ++代码?如果可能的话,我希望参考标准。
知道它是否符合C标准也很有趣。如果不是标准C ++,为什么要决定将其与
&array[5]
或array + 5
区别对待?#1 楼
您的示例是合法的,但这仅是因为您实际上并未使用界外指针。首先让我们处理界外指针(因为这是我最初解释您的问题的方式,然后我才注意到该示例改为使用一个过去的指针):
通常,甚至不允许您创建越界指针。指针必须指向数组中的一个元素,或者指向末尾的一个元素。
甚至不允许指针存在,这显然也不允许您取消引用它。
这是标准中必须说明的主题:
5.7:5:
当将具有整数
类型的表达式添加到
指针或从中减去时,结果的类型为
指针操作数。如果指针
操作数指向
数组对象的元素,并且数组足够大
,则结果指向从原始
偏移的
元素元素,使得结果数组元素和
原始数组元素的下标之差等于
整数表达式。换句话说,如果表达式P指向数组对象的第i个
元素,则
表达式(P)+ N(相当于,
N +(P) )和(P)-N(其中N具有
值n)分别指向
数组对象的第i + n个元素和第i-n个元素,
此外,如果表达式P指向数组的最后一个元素
,则表达式(P)+1指向数组的最后一个元素一个数组
对象,如果表达式Q指向
数组的最后一个元素之后
,则表达式(Q)-1指向
该数组的最后一个元素数组对象。
如果指针操作数和
结果都指向同一
数组对象的元素,或者指向最后一个元素
数组对象的元素,
求值不应产生
溢出;否则,行为是未定义的。
(强调我的)
当然,这是针对operator +的。因此,可以肯定的是,这是标准关于数组下标的内容:
5.2.1:1:
表达式
E1[E2]
是相同的(按定义)到*((E1)+(E2))
当然,有一个明显的警告:您的示例实际上并未显示越界指针。它使用“结束后一个”指针,这是不同的。允许指针存在(如上所述),但是据我所知,标准没有提及取消引用。我能找到的最接近的是3.9.2:3:
[注意:例如,将数组末尾的地址(5.7)视为
指向数组元素类型的不相关对象,该对象可能位于该地址。 —尾注]
在我看来,是的,您可以合法地取消引用它,但未指定读取或写入该位置的结果。
感谢ilproxyil在这里纠正了最后一点,回答了问题的最后一部分:
array + 5
实际上并没有取消引用任何东西,它只是简单地创建指向
array
末尾的指针。&array[4] + 1
取消引用array+4
(非常安全),采用该地址左值,并向该地址添加一个
,该地址
导致最后一个指针
(但该指针永远不会被取消引用。
&array[5]
取消引用array + 5 (据我所知,这是合法的,
导致“数组元素类型的不相关对象
”,作为
以上所述),然后采用该元素的
地址,这也很合法。
因此,他们做的事情并不完全相同,尽管在这种情况下,最终结果是相同的。
评论
&array [5]指向过去。但是,这不是获得该地址的合法方法。
–马修·弗拉申(Matthew Flaschen)
09年6月12日在18:27
最后一句不正确。 “ array + 5”和“&array [4] + 1”不会在末尾取消引用,而array [5]会取消引用。 (我还假设您的意思是&array [5],但该注释仍然有效)。前两个只是指向终点。
–user83255
09年12月12日在18:52
@jalf注意-我认为如果b在数组“ a”之后直接分配,则“ a + sizeof a”与“&b”同等有效,并且得出的地址同样“指向”相同的内容目的。不多。请记住,所有注释都是信息性的(非规范性的):如果要陈述这样的基本重要事实,例如数组对象之后的对象位于过去的末尾,则需要使该规则具有规范性
– Johannes Schaub-小人
09年6月12日在22:21
就ANSI C(C89 / C90)而言,这是正确的答案。如果您遵循字母的标准,则&array [5]在技术上是无效的,而array + 5是有效的,即使几乎每个编译器都会为两个表达式生成相同的代码。 C99更新了标准以明确允许&array [5]。请参阅我的答案以获取全部详细信息。
–亚当·罗森菲尔德
09年6月13日在1:56
@jalf,注释中的整个文本也以“如果类型T的对象位于地址A ...开头” <-表示“以下文本假定地址A处有一个对象。”因此,您的报价单没有(而且在这种情况下也不能)说在地址A处始终有一个对象。
– Johannes Schaub-小人
09年6月13日在13:06
#2 楼
是的,这是合法的。根据C99标准草案:第6.5.2.1节第2段:
后缀表达式和方括号
[]
中的表达式是带下标的数组对象元素的指定。下标运算符
[]
的定义是
E1[E2]
与(*((E1)+(E2)))
相同。由于适用于二进制
+
运算符的转换规则,如果E1
是数组对象(相当于指向数组对象的初始元素的指针),而
E2
是整数,则E1[E2]
指定E2
-th E1
的元素(从零开始计数)。第6.5.3.2节,第3段(重点是我的):
一元
&
运算符产生其操作数的地址。如果操作数的类型为“类型”,结果的类型为“类型的指针”。如果操作数是一元
*
运算符的结果,则不对该运算符和
&
运算符进行求值,并且结果似乎都被省略了,只是对运算符的约束仍然适用并且结果不是左值。同样,如果操作数是
[]
运算符的结果,则不会对&运算符或*
所隐含的一元[]
求值,并且结果就像删除了&
运算符并更改了
[]
运算符一样给+
运算符。否则,结果是指向由其操作数指定的对象或函数的指针。
第6.5.6节第8段:
将具有整数类型的表达式添加到指针或从指针中减去时,
结果具有指针操作数的类型。如果指针操作数指向
数组对象的元素,并且数组足够大,则结果指向距
的元素偏移量原始元素,以使结果数组元素和原始
数组元素的下标之差等于整数表达式。换句话说,如果表达式
P
指向数组对象的第i
个元素,则表达式(P)+N
(等效为N+(P)
)和(P)-N
(其中N
具有值n
)分别指向,数组对象的第
i+n
个元素和第i−n
个元素(如果存在)。此外,如果表达式P
指向数组对象的last (P)+1指向一个数组对象的最后一个元素,如果表达式
Q
指向一个数组对象的最后一个元素。一个数组对象,表达式(Q)-1
指向数组对象的最后一个元素。如果指针操作数和结果都指向同一数组对象的元素,或者指向数组对象的最后一个
元素之后,则评估不应产生溢出;否则,
行为是不确定的。如果结果指向数组对象的最后一个元素,则它
不能用作被评估的一元
*
运算符的操作数。如果未取消引用,则标准明确允许指针指向数组末尾的一个元素。在6.5.2.1和6.5.3.2中,表达式
&array[5]
等效于&*(array + 5)
,等效于(array+5)
,后者指向数组末尾。这不会导致取消引用(按6.5.3.2),因此是合法的。评论
他明确询问了C ++。这是在两者之间移植时不能依靠的细微差别。
–马修·弗拉申(Matthew Flaschen)
09年6月12日在21:34
他问了两个问题:“知道它是否符合C标准也很有趣。”
– CB Bailey
09年6月12日在21:42
@Matthew Flaschen:C ++标准通过引用合并了C标准。附件C.2包含更改列表(ISO C和ISO C ++之间的不兼容性),并且所有更改均与这些条款无关。因此,&array [5]在C和C ++中是合法的。
–亚当·罗森菲尔德
09年6月12日在22:00
C标准是C ++标准中的规范性参考。这意味着C ++标准引用的C标准条款是C ++标准的一部分。这并不意味着C标准中的所有内容都适用。特别是附件C是提供信息的,不是规范性的,因此,仅因为本节中未突出显示差异并不意味着C“版本”适用于C ++。
– CB Bailey
09年6月12日在22:20
@亚当:谢谢你指出这一点。我从未完全确定C的历史。关于C ++ 0x,指的是C99,我对C99的更改知之甚少,我很确定C ++会继续参考C89 / 90,并且会逐案挑选C99的“理想”更改。这个问题/答案就是一个很好的例子。我想说C ++将继续使用“从左值到右值”(novalue-to-rvalue),因此没有未定义的行为,而不是集成“&* == no-op”的措辞。
–理查德·科登(Richard Corden)
09年6月17日在7:49
#3 楼
这是合法的。根据C ++的gcc文档,
&array[5]
是合法的。在C ++和C语言中,您都可以安全地在数组末尾寻址该元素-您将获得一个有效的指针。因此,将&array[5]
作为表达式是合法的。因此,即使指针本身有效,尝试取消引用由该表达式生成的指针仍然是未定义的行为(即非法)。实际上,我认为它通常不会导致崩溃。 br />
编辑:顺便说一句,这通常是实现STL容器的end()迭代器的方式(作为指向过去的指针),因此这是对实践的很好证明是合法的。
编辑:哦,现在我看到您不是真正在问持有指向该地址的指针是否合法,但是获取指针的确切方法是否合法。我会请其他回答者来回答。
评论
我会说,如果且仅当C ++规范没有说&*必须被视为无操作时,您才是正确的。我想它可能不会这么说。
–泰勒·麦克亨利(Tyler McHenry)
09年6月12日在18:33
您引用的页面(正确)说,指向终点是合法的。 &array [5],从技术上讲,首先要取消引用(数组+ 5),然后再次引用它。因此,从技术上讲,它是这样的:(&*(array + 5))。幸运的是,编译器足够聪明,可以知道&*可以归零。但是,他们不必这样做,因此,我要说的是UB。
–埃文·特兰(Evan Teran)
09年6月12日在18:34
@Evan:还有更多。请查看核心问题232的最后一行:std.dkuug.dk/JTC1/SC22/WG21/docs/cwg_active.html#232。那里的最后一个示例看起来是错误的-但它们清楚地说明了区别是在“左值到右值”转换上,在这种情况下不会发生。
–理查德·科登(Richard Corden)
09年6月12日在18:44
@Richard:有趣的是,似乎对此话题有些争论。我什至同意应该允许它:-P。
–埃文·特兰(Evan Teran)
09年6月12日在18:46
这与人们一直在讨论的“对NULL的引用”是一样的未定义行为,而且似乎所有人都投票赞成“这是未定义行为”的答案。
– Johannes Schaub-小人
09年6月12日在19:21
#4 楼
我认为这是合法的,并且取决于发生的“从左值到右值”的转换。最后一行核心问题232具有以下内容:我们同意该标准中的方法似乎还可以:p = 0; * p;本质上不是错误。从左值到右值的转换会带来不确定的行为尽管这是一个稍有不同的示例,但它确实表明,'*'不会导致左值到右值的转换,并且因此,假设表达式是'&'的直接操作数,且期望该值是一个左值,则定义行为。
评论
+1为有趣的链接。我仍然不确定我是否同意p = 0; * p;定义得很好,因为我不认为'*'是针对其值不是指向实际对象的指针的定义良好的。
– CB Bailey
09年6月12日在19:25
作为表达的陈述是合法的,并且意味着评估该表达。 * p是一个可调用未定义行为的表达式,因此该实现所执行的所有操作均符合标准(包括向老板发送电子邮件或下载棒球统计信息)。
– David Thornley
09年6月12日在22:01
请注意,该问题的状态仍然是“起草”,并且尚未成为标准(尚未),至少我可以找到那些C ++ 11和C ++ 14的草案版本。
–音乐爱好者
15年7月2日在23:32
“空的左值”提案从未被任何已发布的标准采用
– M.M
1月13日4:36
问题中的注释显式涉及&* a [n]问题:“类似地,只要不使用该值,就应允许取消引用指向数组末尾的指针”。不幸的是,自2003年以来,这个问题一直没有解决,标准中还没有解决方案。
– Pablo Halpern
8月11日21:18
#5 楼
我不认为这是非法的,但我确实认为&array [5]的行为未定义。5.2.1 [expr.sub] E1 [E2]是(根据定义)与*((E1)+(E2))相同。
5.3.1 [expr.unary.op]一元*运算符...结果是一个左值,它引用对象或函数。表达式点。
此时,您有未定义的行为,因为表达式((E1)+(E2))并未实际指向对象,并且标准确实指出了结果应该是什么,除非它
1.3.12 [defns.undefined]当本国际标准省略任何对行为的明确定义的描述时,也可能会出现未定义的行为。
如其他地方所述,
array + 5
和&array[0] + 5
是获得数组末尾一个指针的有效且定义明确的方法。评论
关键点是:“'*'的结果是左值”。据我所知,仅当您对该结果进行从左值到右值的转换时,它才变成UB。
–理查德·科登(Richard Corden)
09年6月12日在18:41
我认为,“ *”的结果仅根据应用了运算符的表达式所定义的对象来定义,因此,如果没有表达式,则它是不确定的-省略-结果是什么实际引用对象的值。但是,这还远远不够。
– CB Bailey
09年12月12日在18:52
#6 楼
除了上述答案外,我还指出了可以将operator&覆盖为类。因此,即使它对POD有效,对您知道无效的对象也不是一个好主意(就像首先覆盖了operator&()一样。)。评论
即使操作员建议不要覆盖它,因为某些STL容器依赖它将指针返回到元素中,因此即使讨论该问题,+ 1也是讨论的重点。这是他们在不了解之前就已经成为标准的事情之一。
–DavidRodríguez-dribeas
09年6月12日在19:28
#7 楼
这是合法的:int array[5];
int *array_begin = &array[0];
int *array_end = &array[5];
第5.2.1节下标表达式E1 [E2](通过定义)与*((E1)+( E2))
因此,我们可以说array_end也等效:
int *array_end = &(*((array) + 5)); // or &(*(array + 5))
5.3节.1.1一元运算符'*':一元*运算符执行间接操作:对其应用的表达式应为指向对象类型的指针,或为
指向函数类型的指针,并且结果为引用的左值表达式指向的对象或函数。
如果表达式的类型为“指向T的指针”,则结果的类型为“ T”。 [注意:指向不完整类型(cv void以外的
)的指针可以被取消引用。这样获得的左值可以以有限的方式使用(以初始化引用为例)。此左值不能转换为右值,请参见4.1。 —结束语]
上面重要的部分:
'结果是引用对象或函数的左值'。 br />
一元运算符'*'返回一个引用int的左值(无反引用)。一元运算符'&'然后获取左值的地址。
,只要不取消对超出范围的指针的引用,该操作就被标准完全覆盖,并且所有行为定义。因此,根据我的阅读,以上内容完全合法。
许多STL算法都依赖于行为定义良好的事实,这暗示着标准委员会已经做到了这一点,并且我肯定有一个明确地涵盖了这一点的东西。
下面的注释部分提出了两个参数:
(请阅读:但是它很长,我们俩都最终trollish)
参数1
由于第5.7条第5款的规定,这是非法的
当将具有整数类型的表达式添加到指针或从指针中减去时,结果将具有指针操作数的类型。如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向与原始元素偏移的元素,以使结果数组元素和原始数组元素的下标之差等于整数表达式。换句话说,如果表达式P指向数组对象的第i个元素,则表达式(P)+ N(相当于N +(P))和(P)-N(其中N的值为n)到数组对象的第i + n个元素和第i-n个元素,只要它们存在。此外,如果表达式P指向数组对象的最后一个元素,则表达式(P)+1指向数组对象的最后一个元素之后,如果表达式Q指向数组对象的最后一个元素之后,表达式(Q)-1指向数组对象的最后一个元素。如果指针操作数和结果都指向同一数组对象的元素,或者指向数组对象的最后一个元素,则求值不会产生溢出;
并且尽管该节是相关的;否则,该行为是不确定的。它不会显示未定义的行为。我们正在讨论的数组中的所有元素都在数组中,或者在末尾(在上面的段落中已定义)。
论点2:
下面显示的第二个参数是:
*
是取消引用运算符。尽管这是用于描述'*'运算符的常用术语;在标准中故意避免使用此术语,因为术语“取消引用”在语言以及对底层硬件的含义方面定义不充分。
尽管访问超出数组末尾的内存绝对是未定义的行为。我不相信
unary * operator
在这种情况下(不是按照标准定义的方式)访问内存(对内存的读/写)。在这种情况下(如标准所定义(请参阅5.3.1.1)),unary * operator
返回lvalue referring to the object
。在我对语言的理解中,这不是访问基础内存。然后,该表达式的结果立即由unary & operator
运算符使用,该运算符返回lvalue referring to the object
所引用的对象的地址。还提供了许多其他有关Wikipedia和非规范资源的参考。我发现所有这些都不相关。 C ++是由标准定义的。 。以下提供了NON。如果您给我看一个显示这是UB的标准参考书。我会
留下答案。
大写这是愚蠢的,我所有人都读错了。参数:
并非全世界都由C ++标准定义。敞开心mind。
评论
根据谁?根据哪个段落? *执行取消引用。它是取消引用运算符。这就是它的作用。可以争辩的是,随后获得一个指向结果值的新指针(使用&)是无关紧要的。您不能仅仅给出一个评估序列,给出最终表达的语义,并假装没有发生中间步骤(或者该语言的规则不适用于每个规则)。
–轨道轻赛
13年8月6日在11:58
我引用同一段话:结果是一个左值,表示表达式所指向的对象或函数。显然,如果不存在这样的对象,则不会为此操作符定义任何行为。您的后续语句返回一个指向int的左值(不进行反引用),这对我来说毫无意义。您为什么认为这不是取消引用?
–轨道轻赛
2013年8月6日12:00
它返回对所指向内容的引用。如果不取消引用,这是什么?这段话说*执行间接操作,而从指针到指针的间接操作称为解引用。您的论点实质上断言指针和引用是同一件事,或者至少是隐式链接,这根本不是事实。 int x = 0; int * ptr =&x; int&y = * x;在此我取消引用x。我不需要使用y就可以了。
–轨道轻赛
13年8月6日在12:04
@LokiAstari:我有一个问题,如果不“调用返回返回指向表达式所指向对象的左值的一元*运算符”,您认为“取消引用”意味着什么? (请注意,该标准的后续句子确实将此过程称为C ++ 11规范中的取消引用)
–鸭鸭
13年8月6日在17:25
@LokiAstari:我几天前给你看过参考资料;您出于某种原因拒绝承认它们的存在。如何证明这种行为的合理性超出了我的范围,但您必须是唯一的巨魔。
–轨道轻赛
13年8月10日在1:11
#8 楼
工作草案(n2798):“一元&运算符的结果是一个指向其操作数的指针。操作数应为左值或限定值-id。
在第一种情况下,如果
表达式的类型为“ T”,则
结果的类型为“指向T的指针”。(第103页)
array [5]并不是我所能分辨的合格ID(列表在第87页);最接近的似乎是标识符,但是当array是标识符时,array [5]不是。它不是左值,因为“左值是指对象或函数。”(第76页)。 array [5]显然不是函数,并且不能保证引用有效对象(因为array + 5在最后分配的数组元素之后)。
显然,它在某些情况下可能有效,但它不是有效的C ++或安全代码。
注意:合法的是通过数组加一(p。113):
表达式P [指针]
指向数组的最后一个元素
,表达式(P)+1指向
指向数组的最后一个元素
,并且如果表达式Q指向
数组的最后一个元素
,则表达式(Q)-1指向
数组对象的最后一个元素。
如果指针操作数和
结果都指向同一个
数组对象的元素,或者指向数组对象的最后一个
元素之后的元素,则
求值不应产生
溢出”
但是使用&。这样做是不合法的。
评论
我赞成你,因为你是对的。没有任何对象可以保证位于过去的位置。打败您的人可能会误解您(您听起来好像说任何array-index-op都根本没有指向任何对象)。我认为这是一件有趣的事情:它是一个左值,但也没有引用对象。因此,这与标准所说的是矛盾的。因此,这会产生未定义的行为:)这也与此相关:open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232
– Johannes Schaub-小人
09年6月12日在19:16
@jalf,注释说“可能位于该地址”。不保证有一个位于:)
– Johannes Schaub-小人
09年6月12日在19:28
该标准说op *的结果必须是一个左值,但是仅说明如果操作数是实际上指向对象的指针,那么左值是什么。这暗示着(粗俗地)如果到最后没有碰巧指向一个合适的对象,则实现必须从其他地方找到合适的左值并使用它。那真的会弄乱&array [sizeof array]!
– CB Bailey
09年6月12日在21:49
“但是,我仍然认为它不是左值。因为没有对象可以保证在array [5]处,所以array [5]不能合法地引用对象。” <-这就是我认为这是未定义行为的原因:它依赖于标准未明确指定的某些行为,因此在1.3.12 [defns.undefined]之内
– Johannes Schaub-小人
09年6月12日在21:58
小东西,还算公平。假设它不是/绝对是左值,因此绝对/不是100%安全。
–马修·弗拉申(Matthew Flaschen)
09年6月12日在22:48
#9 楼
即使合法,为什么还要违背约定?数组+ 5无论如何都更短,而且我认为可读性更高。编辑:如果希望通过对称方式实现,则可以编写
int* array_begin = array;
int* array_end = array + 5;
评论
我认为我在问题中使用的样式看起来更加对称:数组声明和begin / end指针,或者有时我将它们直接传递给STL函数。这就是为什么我使用它而不是较短的版本。
–赞·山猫
09年12月12日23:45
为了对称,我认为它必须是array_begin = array + 0; array_end =数组+ 5;长时间延迟的评论回复如何?
–赞·山猫
10 Mar 16 '10在18:44
可能是世界纪录:)
– rlbond
10 Mar 16 '10在20:56
#10 楼
出于以下原因,它应该是未定义的行为:尝试访问越界元素会导致未定义的行为。因此,在这种情况下,标准不禁止实现抛出异常(即,在访问元素之前检查边界的实现)。如果将
& (array[size])
定义为begin (array) + size
,则在越界访问的情况下抛出异常的实现将不再符合标准。如果array不是数组而是一个数组,则无法产生
end (array)
的产量。任意集合类型。#11 楼
C ++标准5.19,第4段:地址常量表达式是指向左值的指针...。该指针应使用一元&运算符或使用数组(4.2)...类型下标运算符[] ...可以用于创建地址常量表达式,但是不能通过使用这些运算符来访问对象的值。如果使用下标运算符,则其操作数之一应为整数常量表达式。
对我来说,&array [5]是合法的C ++,是地址常量表达式。
评论
我不确定原来的问题一定是在谈论带有静态存储的数组。即使我想知道&array [5]是否不是因为不指向指定对象的左值的地址常量表达式吗?
– CB Bailey
09年6月12日在22:28
我认为数组是静态的还是堆栈分配的都不重要。
– David Thornley
09年6月13日在15:40
如果您引用的是5.19,则可以。您省略的部分说:“……指定静态存储持续时间的对象,字符串文字或函数。……”。这意味着,如果您的表达式包含分配给堆栈的数组,则不能使用5.19推理这些表达式的有效性。
– CB Bailey
09年6月13日在19:52
您的报价是说,如果&array [5]是合法的,并且引用了静态存储期限数组,那么它将是一个地址常量。 (例如,与&array [99]比较,本段中没有文字可区分这两种情况)。
– M.M
16年2月13日在0:06
#12 楼
如果您的示例不是一般情况,而是特定情况,则可以使用。您可以合法地通过AFAIK将内存移动到分配的内存块之外。它不适用于一般情况,例如,当您尝试访问距离数组结尾较远1的元素时。
仅搜索C-Faq:链接文本
评论
最重要的答案是“合法”,我也说同样的话。那么为什么要下投票:)。我的答案有问题吗?
– Aditya Sehgal
09年6月12日在18:40
#13 楼
这是完全合法的。当调用myVec.end()时,来自stl的vector <>模板类正是这样做的:它为您提供了一个指针(在此为迭代器),该指针指向数组末尾的一个元素。
评论
但这是通过指针算法实现的,而不是通过形成对过去的引用,然后将地址运算符应用于该左值。
– Ben Voigt
2014年10月10日,下午1:54
评论
@Brian:是的,如果需要运行时才能捕获错误,则只需要进行边界检查。为了避免这种情况,该标准可以简单地说“不允许”。最好的情况是未定义的行为。不允许这样做,也不需要运行时和编译器告诉您是否执行此操作。好的,请澄清一下,因为标题误导了我:指向数组末尾的指针不会超出范围。通常不允许使用超出范围的指针,但是标准的“过去一末端”指针要宽松得多。如果您专门询问最后一指针,则可能需要编辑标题。如果您想大致了解越界指针,则应编辑示例。 ;)
通常,他并不是在问指针。他正在询问使用&运算符获取指针。
@Matthew:但是答案取决于指针指向的位置。允许您采用过去最后一次的地址,但不允许超出范围。
5.3.1.1节一元运算符“ *”:“结果是引用对象或函数的左值”。 5.2.1节下标表达式E1 [E2](通过定义)与*((E1)+(E2))相同。通过我在这里阅读标准。不会取消结果指针的引用。 (请参阅下面的完整说明)