【问题标题】:Why can't my function access and modify the variable passed to it?为什么我的函数不能访问和修改传递给它的变量?
【发布时间】:2017-07-20 05:13:53
【问题描述】:

我编写了这个小程序来解释我的观点,并且我的变量 a 保持不变,它会打印 4。后来我知道我需要使用指针或引用;这是为什么呢?

#include <iostream>

void setToTen(int x) { x = 10; }

int main(){  
    int a = 4;
    setToTen(a);
    std::cout << a << std::endl;
}

【问题讨论】:

  • 根据语言规则中的定义,变量通过值传递或复制到函数,除非它们是引用。
  • 您需要将x 声明为引用。那是int&amp; 而不是int
  • 这不是问题,但您真的需要std::endl 需要的额外内容吗? '\n' 结束一行。
  • 问题不应被否决,因为它们是基本问题或初学者问题。这个问题说明了预期的行为、观察到的行为并提供了minimal reproducible example。如果缺乏研究,这很好问。专门针对新用户。

标签: c++ pointers reference


【解决方案1】:

在 C++ 中,函数的参数按值传递。这意味着当你写

setToTen(a);

setToTen 中的参数int x 被赋予了存储在变量a 中的值的副本。换句话说,您实际上并没有将变量a 传递给setToTen 函数。相反,您将该值的副本提供给 setToTen,因此在该函数中所做的更改会影响副本而不是原始值。

另一方面,如果您更改 setToTen 使其通过引用获取其参数,如下所示:

void setToTen(int& x) {
    x = 10;
}

故事不同。在这里,调用setToTen(a) 实质上是将变量a 传递给函数setToTen,而不是值的副本。这意味着对setToTen 中的参数x 所做的更改将更改变量a

【讨论】:

  • @marouanejaakik:如果这个答案让您满意,请接受。
【解决方案2】:

您的代码通过签名void setToTen(int x) 请求x副本

能够通过复制来获取事物意味着推理函数的行为要容易得多。这对您和编译器都是如此。

例如,想象一下:

int increase_some( int x, int y, int z ) {
  for (int i = 0; i < y; ++i )
    x+=z;
  return x;
}

因为 x yz 是副本,所以您可以推断这是做什么的。如果他们引用了increase_some 的“外部”值,那么您x+=z 的位置可能会更改yz,事情可能会变得疯狂。

但是因为我们知道它们是副本,所以我们可以说increase_some 如果y&lt;=0 返回x,否则返回x+y*z

这意味着优化器可以将其更改为:

int increase_some( int x, int y, int z ) {
  if (y<=0) return x;
  return x + y*z;
}

并生成该输出。

这是一个玩具示例,但我们将一个复杂的函数变成了一个简单的函数。真正的优化器一直都在用你的复杂函数来做这件事。

更进一步,通过不可变的值获取事物,并且从不触及全局状态,我们可以将您的代码视为“函数式”,仅取决于其参数。这意味着编译器可以重复调用一个函数并将它们减少为一次调用。

这是非常有价值的,以至于编译器会将没有原始数据不可变副本的代码转换为尝试优化之前的代码——这被称为静态单一赋值形式。

理论上,一个具有许多通过引用获取事物的函数的复杂程序可以以同样的方式进行优化,并且不会丢失任何内容。但实际上这会变得很困难,而且真的很容易不小心搞砸了。

那是另一边;让人们更容易推理。

而你所必须接受的就是以价值为依据的观点。

【讨论】:

    【解决方案3】:

    函数参数是退出函数后不活跃的函数局部变量。

    你可以想象函数定义和它的调用

    int a = 4;
    setToTen(a);
    
    //...
    
    void setToTen(int x) { x = 10; }
    

    以下方式

    int a = 4;
    setToTen(a);
    
    //...
    
    void setToTen( /* int x */ ) { int x = a; x = 10; }
    

    正如在函数中看到的那样,声明了一个局部变量x,它由参数a 初始化。局部变量x 的任何更改都不会影响原始参数a

    如果您想更改原始变量本身,您应该通过引用传递它,因为该函数将处理对该变量的引用。例如

    void setToTen(int &x) { x = 10; }
    

    在这种情况下,您可以想象函数定义及其调用方式如下

    int a = 4;
    setToTen(a);
    
    //...
    
    void setToTen( /* int x */ ) { int &x = a; x = 10; }
    

    如您所见,引用 x 与往常一样是本地的。但它引用了原始参数a。在这种情况下,参数将通过本地引用进行更改。

    另一种方法是将参数声明为指针。例如

    void setToTen(int *x) { *x = 10; }
    

    在这种情况下,您必须通过地址间接传递原始参数。

    int a = 4;
    setToTen( &a );
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-08-01
      • 1970-01-01
      • 2019-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-06
      相关资源
      最近更新 更多