【问题标题】:How is C++ function's default parameter passed?C++函数的默认参数是如何传递的?
【发布时间】:2016-05-12 14:52:06
【问题描述】:

假设我有以下代码:

#include <iostream>

using namespace std;

int defaultvalue[] = {1,2};
int fun(int * arg = defaultvalue)
{
    arg[0] += 1;
    return arg[0];
}

int main()
{
   cout << fun() << endl;
   cout << fun() << endl;

   return 0;
}

结果是:

2
3

这是有道理的,因为指针 *arg 操纵了数组 defaultvalue。但是,如果我将代码更改为:

#include <iostream>

using namespace std;

int defaultvalue[] = {1,2};

int fun(int arg[] = defaultvalue)
{
    arg[0] += 1;
    return arg[0];
}

int main()
{
   cout << fun() << endl;
   cout << fun() << endl;

   return 0;
}

但结果还是:

2
3

此外,当我打印出defaultvalue:

cout << defaultvalue[0] <<endl;

原来是 3。

我的问题是,在第二个例子中,函数参数是否应该按值传递,这样arg的变化对defaultvalue没有影响?

【问题讨论】:

  • 作为函数参数类型,int*int[]完全相同。
  • *arg 不是指针。

标签: c++ parameter-passing


【解决方案1】:

我的问题是,在第二个例子中,函数参数是否应该按值传递,这样arg的变化对defaultvalue没有影响?

没有。

不可能通过值传递数组(非常感谢,C!)所以,作为“妥协”(阅读:设计失败),函数参数列表中的int[]实际上意味着int*。所以你的两个程序是相同的。即使写 int[5]int[24]int[999] 实际上意味着 int*。可笑,不是吗?!

在 C++ 中,我们更喜欢将 std::array 用于数组:它是一个数组包装类,具有适当的对象语义,包括可复制。您可以将它们按值传递给函数。

确实,std::array 的引入主要是为了让这些愚蠢而令人惊讶的原生数组语义过时。

【讨论】:

  • C 程序员注意:你可以通过定义一个带有数组成员的结构来获得与 C 中的std::array 相同的效果;然后你就可以按值传递该结构类型了。
  • 还有另一种选择:将 reference 传递给固定大小的 C 数组可防止指针衰减,但当然最好使用 std::array。
  • @amon 注意 KerrekSB 的解决方案(和 OP 的问题)解决了轻松传递数组的 副本 的需求。
  • @amon:虽然是这样,但这与 OP 的要求恰恰相反;)
【解决方案2】:

当我们像这样声明一个函数时

int func(int* arg);

或者这个

int (func(int arg[])

它们在技术上是相同的。这是表达能力的问题。在第一种情况下,API 作者建议函数应该接收指向单个值的指针;而在第二种情况下,它表明它需要一个数组(例如,长度不确定,可能以 nullptr 结尾)。

你也可以写

int (func(int arg[3])

这在技术上也是相同的,只是它会提示 API 用户他们应该传入一个至少包含 3 个元素的 int 数组。在这些情况下,编译器不会强制执行任何这些添加的修饰符。

如果您想将数组复制到函数中(以非黑客的方式),您将首先在调用代码中创建它的副本,然后将其传递。或者,作为更好的选择,使用std::array(如@LightnessRacesinOrbit 所建议的那样)。

【讨论】:

  • “技术上”是什么意思?它们在哪些方面相同?
  • 我已经看到这个“提示”抓住了不知道参数int[]int[42]int* 相同的人,所以如果他们通过,他们预计会出现编译器错误长度错误的数组,甚至期望数组按值传递。所以我更喜欢照原样说,并使用int*
  • @KerrekSB 好吧,在非技术意义上,例如视觉上。但我们不要被语义冲昏了头脑。
  • @juanchopanza 有这种方法,但你必须在某个地方划清界限,而不仅仅是说任何不懂语言的人都可以决定编码约定。
  • 有点同意。我认为它更像是一个危险信号,即编写它的人对语言不够了解。很多时候,我是对的。但是我看到的很多编码约定都集中在那些对语言理解不够好的人身上。对于 C++ 来说,大多数人都是这样的。
【解决方案3】:

正如其他人所解释的,当你把 int arg[] 作为函数参数,这些括号内的内容并不重要(您甚至可以执行 int arg[5234234] 并且它仍然可以工作],因为它不会改变它仍然只是一个普通 int * 的事实指针。

如果你真的想确保一个函数接受一个数组[],最好像这样传递它

template<size_t size>
void func (const int (&in_arr)[size])
{
    int modifyme_arr[100];
    memcpy(modifyme_arr, in_arr, size);

    //now you can work on your local copied array
}

int arr[100];
func(arr);

或者如果你想要 100 个元素

    void func (const int (&arr)[100])
    {

    }

    func(arr);

这些是传递简单数组的正确方法,因为它可以保证你得到的是一个数组,而不仅仅是一个随机的 int * 指针,函数不知道它的大小。当然你可以传递一个“计数”值,但是如果你犯了一个错误并且它不是正确的怎么办?然后你得到缓冲区溢出。

【讨论】:

  • 我不是反对者,我认为这里没有任何技术问题。但是暗示 OP 已经用大小声明了正式的数组参数,我看不到这样的?
  • OP 希望复制参数。这实际上是相反的。
  • @Cheersandhth.-Alf:{1,2} 隐含大小为 2,无论 OP 的意图是什么
  • @LightnessRacesinOrbit 不完全是,因为我的模板化函数是从一开始就定义为 const 的(这意味着它不受操作所需要的更改的影响),并且它实际上接受 int[] 数组(我觉得这是op也想要)(任何大小),我已经在函数中添加了一个主体来“复制” arr 值,尽管代码很明显
猜你喜欢
  • 2016-01-02
  • 2011-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-22
  • 2020-07-29
  • 1970-01-01
  • 2015-10-30
相关资源
最近更新 更多