使用指针可以最好地解决哪些编程问题?[关闭]


58

好吧,我基本上理解了如何使用指针,但是不了解如何最好地使用它们以进行更好的编程。

有什么好的项目或要解决的问题,包括使用指针,以便我可以更好地理解它们?


询问使用指针而不是什么项目/程序可以最好地解决哪些编程问题。您可以使用或不使用指针来编写程序-它们(项目)对于有意义的答案来说太大了。
Oded

37
编程问题是使用指针创建的,没有解决。:)
xpda 2011年

4
实际上,我想知道没有指针可以解决哪些问题。肯定很少。
deadalnix 2011年

4
@deadalnix,这取决于您是指显式指针还是隐式指针。如果您指的是隐式指针(即,某处使用了指针),则不可能编写任何使用堆栈或堆的程序。如果您指的是显式指针,那么您就不受约束了,您可以通过创建新的堆栈帧(基本上使用函数调用)和使用尾部调用进行释放来进行任何大小的分配,前提是它们已由编译器进行了优化。
dan_waterworth 2011年

3
嗯,这些答案有些可怕。

Answers:


68

指针真正发光的地方是处理内存中的大量数据。

通过引用传递一个大对象等效于仅传递一个普通的旧数字。您可以直接操作所需的部分,而不是复制对象,对其进行更改,然后将副本传回以代替原始对象。


12
这是一个很好的答案,我想补充一点,您不需要大型对象即可处理大量数据。偶尔处理大型对象会做到这一点,但是一个更常见的问题是真正非常频繁地处理一堆小对象。如果您考虑一下,那几乎就是所有计算机程序。
2011年

44

指针概念使您可以按地址引用数据,而不必重复存储数据。这种方法允许编写高效的算法,例如:

  1. 排序
    在排序算法中移动数据时,您可以移动指针而不是数据本身。您可以节省很多不必要的数据移动。

  2. 链接列表
    您可以存储下一个和/或上一个项目位置,而不是与记录关联的整个数据。

  3. 传递参数
    在这种情况下,您传递数据的地址而不是数据本身。再次考虑运行在数百万行上的名称压缩算法。

的概念可以扩展到数据结构,例如关系数据库,其中一个指针是类似于一个外键。某些语言不鼓励使用诸如C#和COBOL之类的指针。

可以在许多地方找到示例,例如:

以下帖子可能在某种程度上相关:


14
我不会说C#不鼓励使用指针。相反,它不鼓励使用非托管指针-C#中有很多东西都是通过引用而不是值传递的。
Blakomen 2011年

好人,很好的解释
Sabby 2011年

5
不得不投票赞成C#注释。C#的“引用”等同于指针,只是C#使您不知道实际上您只是在处理指向托管堆中对象的指针而已。
MattDavey 2011年

1
@MattDavey:C#引用不是指针。您不能从它们中添加,使用它们为数组建立索引,使用多个间接寻址等。例如,您不能具有对引用的引用。
比利·奥尼尔

5
@Billy ONeal坦率地说,“如果您可以对它进行算术运算,它只是一个指针”,这很荒谬,但是我没有耐心与您争论。
MattDavey 2011年

29

令我惊讶的是,没有其他答案提到过:指针允许您创建非连续和非线性的数据结构,其中一个元素可能以复杂的方式与多个其他元素相关联。

链表(单链,双链和循环链),树(红黑,AVL,特里,二进制,空间分区……)和图都是可以最自然地根据引用而非值构造的结构示例。


您忘记了XOR链表[=
dan_waterworth 2011年

@dan_waterworth:不,我知道黑魔法太好了。
乔恩·普迪

8

一种简单的方法是多态。多态仅适用于指针。

另外,在需要动态内存分配时,可以随时使用指针。在C语言中,这通常发生在需要将数据存储到数组但不知道编译时大小的情况下。然后,您将调用malloc分配内存和访问它的指针。同样,无论您是否知道,使用数组时都在使用指针。

for(int i = 0; i < size; i++)
   std::cout << array[i];

等于

for(int i = 0; i < size; i++)
   std::cout << *(array + i);

这些知识使您可以做一些很酷的事情,例如在一行中复制整个数组:

while( (*array1++ = *array2++) != '\0')

在c ++中,可以使用new为对象分配内存并将其存储到指针中。您可以在需要在运行时而不是在编译时创建对象的任何时候执行此操作(即,方法将创建一个新对象并将其存储到列表中)。

为了更好地理解指针:

  1. 查找一些使用传统c字符串和数组的项目。
  2. 查找一些使用继承的项目。

  3. 这是我不遗余力的项目:

从文件中读取两个nxn矩阵,并对它们执行基本的向量空间运算,并将其结果打印到屏幕上。

为此,您将必须使用动态数组并通过指针引用您的数组,因为您将拥有两个数组数组(多维动态数组)。完成该项目之后,您将对如何使用指针有一个很好的了解。


2
“多态只适用于指针。” 不准确:多态性也可以与参考一起使用。最终它们是指针,但是我认为区别很重要,尤其是对于新手。
Laurent Pireyn 2011年

您在一行中复制数组的示例实际上是在一行中复制零终止字符串,而不是任何常规数组。
tcrosley

8

要真正理解指针为什么如此重要,您需要了解堆分配和堆栈分配之间的区别。

以下是堆栈分配的示例:

struct Foo {
  int bar, baz
};

void foo(void) {
  struct Foo f;
}

在堆栈上分配的对象仅在当前函数执行期间存在。当对的调用foo超出范围时,变量也是如此f

成为问题的一种情况是,您需要从函数中返回除整数类型以外的其他内容(例如,上例中的Foo结构)。

例如,以下函数将导致所谓的“未定义行为”。

struct Foo {
  int bar, baz
};

struct Foo *foo(void) {
  struct Foo f;
  return &f;
}

如果struct Foo *要从函数返回类似的内容,则真正需要的是堆分配:

struct Foo {
  int bar, baz
};

struct Foo *foo(void) {
  return malloc(sizeof(struct Foo));
}

该函数malloc在堆上分配一个对象,并返回指向该对象的指针。请注意,术语“对象”在这里被宽松地使用,意思是“某物”而不是面向对象编程的对象。

堆分配对象的生存期由程序员控制。该对象的内存将保留,直到程序员释放它为止,即通过调用free()或直到程序退出。

编辑:我未能注意到此问题被标记为C ++问题。C ++运算符newnew[]执行与相同的功能malloc。运算符deletedelete[]与相似free。虽然newdelete应该专门用于分配和释放C ++对象,但在C ++代码中使用mallocfree完全合法。


1
(+1)也称为“静态分配的变量”和“动态分配的变量” ;-)
umlcat 2011年

4

用C编写任何不平凡的项目,您将必须弄清楚如何/何时使用指针。在C ++中,您将主要使用启用RAII的对象在内部管理指针,但在C中,原始指针的作用更为普遍。至于您应该做什么样的项目,它可以是任何不平凡的事情:

  • 小型而简单的Web服务器
  • Unix命令行工具(less,cat,sort等)
  • 您想为自己而不是为了学习而做的一些实际项目

我推荐最后一个。


因此,我只是去做一个自己想做的项目(例如2D游戏),并且认为我需要学习指针吗?
dysoco 2011年

3
以我的经验,做一些真正超出技能的实际项目是最好的学习方法。可以通过阅读和编写小型“玩具”程序来学习概念。但是,要真正学习如何使用这些概念来编写更好的程序,只需编写非玩具程序即可。
维克多·达尔

3

几乎可以使用指针解决的所有编程问题都可以使用其他更安全的引用类型解决(不是引用C ++引用,而是具有变量的通用CS概念引用存储在其他位置的数据值)。

指针是特定的底层引用实现,您可以在其中直接操作内存地址,功能非常强大,但使用起来可能会有些危险(例如,指向程序外部的内存位置)。

直接使用指针的好处是不必进行任何安全检查,它们的速度会稍快一些。像Java这样的语言不能直接实现C风格的指针,虽然性能会受到轻微的影响,但是会减少许多类型的难以调试的情况。

至于为什么需要间接访问,列表很长,但是本质上两个关键思想是:

  1. 复制大对象的值很慢,并且会使对象两次存储在RAM中(可能会非常昂贵),但是通过引用复制几乎仅使用几个字节的RAM(用于地址)即可进行瞬时复制。例如,假设您的内存中有约1000个大对象(每个对象大约有1MB的RAM),并且您的用户需要能够选择当前对象(用户将对其进行操作)。具有selected_object引用对象之一的变量比将当前对象的值复制到新变量中要有效得多。
  2. 具有引用其他对象(如链表或树)的复杂数据结构,其中数据结构中的每个项目均引用数据结构中的其他项目。引用数据结构中其他项目的好处是,您不必仅在列表中间插入一个新项目(可以有固定的时间插入)就移动内存中列表中的每个项目。

2

使用指针几乎总是更容易,更快地进行像素级图像操作。有时只能使用指针。


1
我认为“有时只能使用指针”这一说法是不正确的。有些语言不会向开发人员公开指针,但是可以将它们用于解决同一空间中的问题。
Thomas Owens

也许……我不了解所有可用的语言,但是我当然不想编写例程,使用没有指针的语言将卷积滤镜应用于图像。
戴夫·奈

@Thomas,虽然您是正确的,但问题是关于c ++的,它确实向开发人员公开了指针。
乔纳森·亨森

@Jonathan即便如此,如果您可以使用不公开指针的语言来解决问题,那么也可以使用不公开指针的语言来解决该问题。这使第一句话为真,但第二句话为假-就我所知,没有使用指针无法解决的问题。
Thomas Owens

1
相反,图像处理通常涉及“坐标算术”,也就是说,取一个角度的正弦和余弦并将其乘以x和y坐标。在其他情况下,则需要环绕或边界外像素复制。从这个意义上说,指针算术是不够的。
rwong 2011年

1

指针可以在表面以下的多种编程语言中使用,而不会引起用户的注意。C / C ++仅使您可以访问它们。

何时使用它们:尽可能频繁,因为复制数据效率很低。何时不使用它们:当您希望可以分别更改两个副本时。(基本上,最终将把对象_1的内容复制到内存中的另一个位置并返回一个指针-这次指向对象_2)


1

指针是C中任何数据结构实现的重要组成部分,而数据结构是任何非平凡程序的重要组成部分。

如果您想了解为什么指针如此重要,那么我建议您学习什么是链表,并尝试在不使用指针的情况下编写一个链表。我没有给您带来一个不可能的挑战,(提示:指针用于引用内存中的位置,如何引用数组中的内容?)。


+1表示数据结构,在算法中的使用是很自然的结果。
Matthieu M.

0

作为一个现实的例子,建立一个限价单。

例如,ITCH 4.1提要具有“替换”订单的概念,价格(因此,优先级)可以更改。您希望能够接受订单并将其移至其他位置。使用指针实现双端队列使操作非常容易。


0

仔细阅读各个StackExchange网站,我意识到提出这样的问题很时尚。坦率地说,冒着批评和不赞成投票的危险。这并不意味着拖延或发火,我仅是通过对问题的诚实评估来提供帮助。

评估如下:这是C程序员要问的一个非常奇怪的问题。它说的几乎都是“我不知道C”。如果我更加愤世嫉俗地进行分析,则有一个跟进此“问题”的暗示:“我是否可以采取一些捷径来快速而突然地获得经验丰富的C程序员的知识,而无需花费任何时间进行独立学习?” 某人可以给出的任何“答案”都不能代替他去做并了解潜在的概念及其用法。

我觉得直接学习C比在网上问这个问题更具建设性。当您很好地了解C时,您就不会再问这样的问题了,就像在问“用牙刷最好地解决什么问题?”。


0

即使在处理大型内存对象时指针确实发光,但是如果没有它们,仍然有一种方法可以做到这一点。

指针对于所谓的动态编程绝对必不可少,这是在您不知道程序执行之前需要多少内存的时候。在动态编程中,您可以在运行时请求内存块并将自己的数据放入其中-因此,您需要指针或引用(此处的区别并不重要)才能使用这些数据块。

只要您可以在运行时声明某些内存并将数据放入新获取的内存中,就可以执行以下操作:

  1. 您可以具有自扩展数据结构。这些结构可以通过声明额外的内存来扩展自身,只要它们的容量用完即可。每个自扩展结构的关键属性是它由小的存储块(取决于结构,称为节点,列表项等)组成,并且每个块都包含对其他块的引用。这种“链接”结构构建了大多数现代数据类型:图形,树,列表等。

  2. 您可以使用OOP(面向对象编程)范例进行编程。整个OOP都不基于使用直接变量,而是基于对类实例(称为对象)的引用并对其进行操作。没有指针就不能存在单个实例(即使即使没有指针也可以使用纯静态类,这是一个例外)。


0

有趣的是,我只是回答了有关C ++的问题,并谈到了指针。

简短的版本是您永远不需要指针,除非1)您正在使用的库强制您2)您需要可为空的引用。

如果需要数组,列表,字符串等,只需将其放在堆栈上并使用stl对象即可。返回或传递的stl对象是快速的(未经检查的事实),因为它们具有内部代码,该内部代码复制指针而不是对象,并且仅当您写入数据时才复制数据。这是常规C ++,甚至不是新的C ++ 11,这将使库编写者更容易使用它。

您的问题可能会在此部分得到回答

如果确实使用了指针,请确保其处于以下两种情况之一。1)您正在传递可能为空的输入。一个示例是可选的文件名。2)如果您想放弃所有权。就像传递或返回指针一样,您将没有剩余的任何副本,也不会使用您放弃的指针

ptr=blah; func(ptr); //never use ptr again for here on out.

但是我已经很长时间没有使用指针或智能指针了,因此我已经介绍了我的应用程序。它运行非常快。

其他说明:我注意到我确实编写了自己的结构,并且确实将它们传递了出去。那么,如何在不使用指针的情况下做到这一点呢?它不是STL容器,因此通过ref传递很慢。我总是加载我的数据列表/双端队列/地图等。我不记得返回任何对象,除非它是某种列表/映射。甚至没有字符串。我查看了单个对象的代码,然后注意到我做了类似的事情,{ MyStruct v; func(v, someinput); ... } void func(MyStruct&v, const D&someinput) { fillV; }所以我几乎返回了对象(多个)或预分配/传递了填充参考(单个)。

现在,如果您正在编写自己的双端队列,地图等,则需要使用指针。但是您不需要。让STL并可能加剧对此的担忧。您只需要编写数据和解决方案。不是用来存放它们的容器;)

希望您现在不再使用指针:D。祝你好运与libs处理,迫使你


0

指针对于使用内存映射设备非常有用。您可以定义一个反映(例如)控制寄存器的结构,然后将其分配给内存中实际控制寄存器的地址并直接对其进行操作。如果MMU已将其映射到系统内存空间,则也可以直接指向卡或芯片上的传输缓冲区。


0

我把指针看作是我的食指,我们常做以下几件事:

  1. 当有人问你一些你不能携带的东西时,例如某条街在哪儿,我会“指着”这条街,这就是我们通过引用进行论证的情况
  2. 在计数或遍历某物时,我们将使用同一根手指,这就是我们在数组中所做的

请原谅这个可怜的答案


0

由于您的问题被标记为C ++,因此我将针对该语言回答您的问题。

在C ++中,指针和引用之间是有区别的,因此在两种情况下,必须使用指针(或智能指针)来促进某些行为。它们可以在其他情况下使用,但是您会问“如何最好地使用它们”,在所有其他情况下,都有更好的选择。

1.多态性

基类指针允许您调用一个虚拟方法,该方法取决于指针所指向的对象的类型。

2.创建持久对象

动态创建对象(在堆而不是堆栈上)时,指针是必需的。当您希望对象的生存期长于创建对象的范围时,这是必需的。

正如其他人已经在这里所说的那样,在“好的项目或要解决的问题”方面,任何不重要的项目都将使用指针。


也许有人会比使用指针更喜欢切片对象。调试愉快:D
deadalnix
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.