10

我已经很多年没有用 C++ 编程了,所以我决定用指针来刷新我的记忆。

在两个数字之间交换的经典示例中,示例是

void swapPointersClassic(double *num1, double *num2) 
{
  double temp;
  temp = *num1;
  *num1 = *num2;
  *num2 = temp;
}

这允许我们像调用函数一样进行函数调用swapPointersClassic(&foo, &bar);,因为我们传入了变量 foo 和 bar 的内存地址,函数将检索值并进行交换。但是我开始怀疑,为什么我不能执行以下操作?

void swapPointers(double *num1, double *num2)
{
  double *temp;
  temp = num1;
  num1 = num2;
  num2 = temp;
}

这对我来说似乎更有意义,因为我们只需要创建足够的临时存储空间来存储内存地址 num1(而不是完整的临时存储空间来存储双精度值 *num1)。但是,函数范围似乎限制了指针交换的效果。拨打电话后swapPointers(&foo, &bar);,我可以看到在函数 swapPointers 中,foo & bar 确实被交换了。一旦我们退出 swapPointers 函数, foo 和 bar 就不再交换了。谁能帮我理解为什么会这样?这种行为让我想起了典型的按值传递方法,但我们在这里通过指针传递。所以这意味着我们只能触摸那些指针指向的值,而不能触摸指针本身?

4

8 回答 8

8

实际上,您并没有真正通过指针传递。您按值传递两个指针。仅传递指针本身是不够的 - 您必须取消引用它们。取消引用(副本)指针的行为使魔术发生,它是实现范围遍历的实际位置。

修改函数的参数对函数来说是本地的,即使参数本身是指针。您必须取消引用它们才能访问它们指向的内存(同样,“通过指针传递”不仅仅是传递指针 - 它正在传递指针正确使用它们)。

另外,请考虑以下事项。如果您的第二种方法有效,并且交换了两个指针,那么这意味着交换了变量的地址。这完全没有意义。

顺便说一句,在 C++ 中,我们有一个真正的按引用传递调用约定,所以 没有必要搞乱指针:

void swap(double &a, double &b)
{
    double temp = a;
    a = b;
    b = temp;
}

float a = 1, b = 2;
swap(a, b);

(这只是一个实现问题,编译器很可能会通过实际使用指针来实现这种行为,但至少你不会头疼。)

于 2012-11-08T17:58:25.297 回答
6

如果要交换指针而不是它们的值,则需要传递指针的指针:

void swapPointers(double** num1, double** num2)
{
  double* temp = *num1;
  *num1 = *num2;
  *num2 = temp;
}

您可以在以下位置查看示例:http: //ideone.com/iklCta

您甚至可以使用参考:

void swapPointers(double*& num1, double*& num2) {
  double* temp = num1;
  num1 = num2;
  num2 = temp;
}

示例:http: //ideone.com/w4k9mV

当您使用大型对象(例如:图像数据)并且您不想移动大量内存而只想移动引用时,这很有用。

于 2012-11-08T17:55:36.377 回答
2

这是一个很好的问题,但是一旦你弄清楚了,请使用std::swaporstd::iter_swap而不是自己编写。

如果foobar是指针,调用将在两个指针之间std::swap(foo, bar)交换地址。调用std::swap(*foo, *bar)std::iter_swap(foo, bar)将取消引用指针并交换指针指向的对象

于 2012-11-08T18:30:44.033 回答
0

您正在按值传递指针本身;num1并且num2是调用者用来调用函数的指针的本地副本。因此,当函数退出时,调用站点上的交换是不可见的,因为只对函数本地的变量进行了修改。

相反,更改函数以引用指针,您的交换代码将按预期工作。

void swapPointers(double*& num1, double*& num2) { ... }

现在,您的函数参数是调用者传递给函数的指针的别名,无论您对它们做什么,都会影响调用者上下文中的指针。

于 2012-11-08T17:56:23.333 回答
0

你打电话时

swapPointersClassic(&foo, &bar)

您正在获取堆栈上两项的地址。这些值作为临时值传入并按 value 复制。在您的函数体中,您交换这些临时对象指向的位置,但不要移动 foo 和 bar 指向的位置。

最重要的是, foo 和 bar 存在于stack中。在 C++ 中,您无法更改变量在堆栈中的位置。一个值被映射到堆栈上的一个点并存在于那里。您可以获得指向该值所在位置的指针以进行传递指针(类似于传递引用),但只需更改临时指针指向的位置,您不会更改指向对象所在的位置。

于 2012-11-08T17:56:53.267 回答
0

当你传入double *num1一个函数时,你是按值传入一个指针。这会在函数范围内创建一个变量,该变量包含一个 double 的地址。当您分配给它时,您正在更改函数范围内指针的值。

就像你必须传递一个指向双精度的指针,取消引用它,并分配交换一个双精度,你必须传递一个指向指针的指针,取消引用它,然后赋值交换一个指针。

于 2012-11-08T17:57:53.590 回答
0

指针 101:指针是一张纸,上面有房子的地址。

通过写 &bar,你将把名为 bar 的房子写在一张匿名的便条纸上。

然后,您使用该指针作为参数调用一个函数。这里发生的情况是该函数制作了另一张纸的副本。

然后,您的交换函数获取两个这样的本地副本并交换写入它们的地址。

所以这在函数之外没有任何作用。清除?

现在基本上即使地址的功能内没有副本,您正在使用的只是带有家庭地址的废纸。对此类文件的任何更改实际上都不能移动存储在 house bar 或 foo 中的数据。

于 2012-11-08T18:04:52.320 回答
0

由于这个问题的正确答案已经发布,我只想说清楚一些事情。

使用引用交换两个变量的值通常是不好的做法,应该避免。原因是不应该重新分配引用。看下面的代码:

1. void swap(double &a, double &b)
2. {
3.    double temp = a;
4.    a = b;  //Not what you think
5.    b = temp;
6. }
7.
8. float a = 1, b = 2;
9. swap(a, b);

在第 4 行和第 5 行,您显然违反了通用规则 - “永远不要重新分配引用”。

如果你真的需要改变变量的值,总是更喜欢“通过指针”而不是“通过引用”。以下代码是有意义的,也是 IMO 的良好实践。

1.  void swapPointersClassic(double *num1, double *num2) 
2.  {
3.   double temp;
4.   temp = *num1;
5.   *num1 = *num2;
6.   *num2 = temp;
7.  }    
8.
9.  float a = 1, b = 2;
10. swap(&a, &b);

同意引用更清晰,更易于使用,并且它们在隐藏信息方面做得更好,正如您在第一个示例中看到的那样。但是,不能重新分配引用。如果您需要先指向一个对象,然后再指向另一个对象,则必须使用指针。

经验法则:

  • 如果引用有效,请不要使用指针。
  • 不要尝试重新分配对不同变量的引用。你不能。
于 2015-07-15T15:10:39.633 回答