指针入门






4.42/5 (45投票s)
2002年12月11日
6分钟阅读

245479

1122
关于指针和指针操作的快速参考
引言
对于刚开始接触 C++ 的新手来说,指针无疑是一个令人望而生畏的话题,这促使我写下了这篇简短的文章。请注意,这不是一篇关于指针的详尽论述,而是一个关于其用法和真正含义的常见问题的快速参考。这也可以作为 Andrew Peace[^] 优秀文章《初学者指针指南[^]》的一个很好的入门读物,他在其中对该主题进行了精彩的讨论。
理念
正如我之前所说,我的目的不是要追溯指针宇宙的起源,而是提供一个关于指针如何使用的简单示例,并在此过程中希望能解决论坛上的一些疑虑以及对上述文章提出的类似问题。因此,本文的理念是通过一个简单的实际工作示例来揭开指针的神秘面纱。
术语和技术
在处理指针时,我采用以下术语和技术:
- 值 (Value) - 指定数据类型的实际值。
- 指向 (Pointer To) - 指向指定数据类型内存位置的指针。
- 地址 (Address Of) -给定数据类型的物理内存地址。
- 引用 (Reference To) - 对给定数据类型的引用,而不是它的副本(仅限 C++)。
- 解引用的值 (Dereferenced Value) - 指针所指向的数据类型的值。
指针的主题很大程度上涉及符号 * 和 & 的使用,这对于初学者来说常常令人困惑;尤其是在不同的人将修饰符放在不同位置时(例如我与 VS6 类向导:P)。我的技术是将修饰符放在它们所修改的内容旁边。例如:
// Initialize a variable, 'x', of type 'int' to the 'value' 5.
int x = 5;
// Initialize a variable, 'px' of type 'pointer to int' to the value of
// 'the address of' x.
int* px = &x;
---- --
| |
| |- This is read "address of" x
|
|- Notice I put the * modifier after int, not before px (int* px not
int *px). The compiler doesn't care, however I am modifying the
data type, not the variable. Thus I remain consistent to my technique.
// Now initialize a variable 'ref_to_x' as a reference to x.
int& ref_to_x = x;
----
|
|- Again, I keep the modifier with what I am modifying.
// Initialize a variable,'deref_px' of type 'int' to the 'dereferenced
// value' of px.
int deref_px = *px;
---
|
|- Notice in this case, the * is with px, because that is
what I'm modifying.
好了,既然这个已经讲清楚了,我们继续。
按引用传递
关于“按引用传递值”的问题一直层出不穷。为什么要按引用传递?按引用传递值可以加快速度并缩小体积,因为你没有复制传递的变量。当应用程序实例的整个生命周期中只进行一次函数调用时,这可能看起来并不重要,但当你每分钟调用一个函数 1800 次时,性能就成为一个主要的驱动因素。此外,修改值的副本时,原始值保持不变(在某些情况下可能需要),而按引用传递时,原始变量会被修改。下载的示例程序将说明原因,但这里有一个简短的示例,应该可以消除混淆。
考虑以下函数:
void MyFunctionByCopy(CString x)
{
CString y = "Test by copy.";
x.Format("%s",static_cast<const char*>(y));
return;
}
void MyFunctionByReference(CString& x)
{
CString y = "Test by reference.";
x.Format("%s",static_cast<const char*>(y));
return;
}
注意:第一个函数会创建一个传递变量的副本,而后者将修改变量本身。请注意,int 的引用是一个自动解引用的指针,因此不需要指针表示法。请查看示例程序以了解此操作的实际工作情况。
给定以上两个函数,你能猜出以下示例中结果的值吗?
{
CString szTestOne = "Test Me One";
CString szTestTwo = "Test Me Two";
MyFunctionByCopy(szTestOne);
MyFunctionByReference(szTestTwo);
printf("\nszTestOne = %s", static_cast<const char*>(szTestOne));
printf("\nszTestTwo = %s", static_cast<const char*>(szTestTwo));
}
对于那些无法忍受悬念的人来说,szTestOne
= "Test Me One",szTestTwo
= "Test by reference."。如果你不相信我,试试就知道了。
指向指针的指针
另一个持续存在的担忧是“指向指针的指针”。如果你遵循我的技术,指针只不过是一种任意数据类型,因此“指向指针的指针”只不过是一个普通的指针。当然,它可能看起来令人生畏且有些陌生,但将其分解为最基本的要素,其实并没有什么难的。所以事实上,你也可以有“指向指向指针的指针”以及“指向指向指向指针的指针……”,嗯,你懂我的意思。
对于任何有兴趣了解此类实现外观的人,可以参考下面的几行代码。你可以自己尝试一下,看看结果。
int x = 7;
int* px = &x;
int** ppx = &px;
printf("\nx = %d", x );
printf("\n*px = %d", *px );
printf("\n**ppx = %d", **ppx);
为什么要使用指针?
我记得刚开始学习 C++ 并接触指针时,经过一番困惑,我曾想:“为什么要使用指针呢?” 我可以直接实例化所有变量,而不用担心。(注意到我使用了“实例化”和“显式实例”等时髦的词,是的,我知道 C++……对吧……)嗯,随着经验的增长,你会变得谦虚和明智;而有了 CodeProject,你会获得启发和鼓励。当我开始涉足命令行脚本以外的应用程序时(我说脚本,因为我的整个程序都在 main()
函数中,而且我没有进行函数调用。我的代码重用概念就是复制粘贴),我发现自己进入了一个全新的世界。
结果是,在实际的编程中,你并不总是能够准确地知道你要分析多少东西,或者你的数组需要多大。你不知道你的用户机器有多少系统资源。你开始担心诸如优化和按引用传递之类的事情。你开始使用默认接受指针的内置函数,尽管你可能不知道原因……
根据我个人(只有两年的)经验,我转向指针领域的首要驱动力是那些美妙的运算符:
new and delete
虽然这不是一本全面的指针指南,但我认为如果不提及上述运算符,就无法谈论指针。为什么这些运算符如此重要?它们之所以重要,不仅是因为它们允许你动态地为你的程序分配内存,而且它们是通过使用指针来实现的。关于这个主题有很多信息,所以我不会详细介绍,但我会涵盖基本要点。再次考虑以下代码:
int* pNewInt = new int;
*pNewInt = 7;
printf("\nThe pointer, pNewInt, is located at memory address: 0x%X",
&pNewInt);
printf("\npNewInt points to memory location 0x%X and contains the
value %d.",
pNewInt, *pNewInt);
delete pNewInt;
在这段代码中,我们使用关键字 new
创建了一个新的“指向 int
的指针”。请注意,new 返回的是一个“指向 int
的指针”。要记住的一个重要事项是,在你用完新的指针后要删除它们。如果你不这样做,你将面临许多内存泄漏,并可能耗尽目标机器的所有资源。关于这方面的细节可以在其他地方找到,但关键是:new 是一个强大的朋友,值得花时间去理解。
结论
指针一开始可能看起来令人生畏,但它们实际上是 C 和 C++ 中的基本概念。阅读它们只能让你走得更远;最终有助于让世界变得更美好的,是使用它们的经验和掌握它们的力量的顿悟。所以,享受你的指针吧。经常使用它们,并好好照顾它们。但最重要的是,要勇敢而自信地使用它们,当你“超越天空中那伟大的异常……”时,你将获得丰厚的回报(Mark Conger - Death of a Coffee Pot)
许可证
本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。