【问题标题】:C++ Need explaination for this functions [duplicate]C ++需要对此功能进行解释[重复]
【发布时间】:2016-02-03 21:41:51
【问题描述】:

今天我在用C++编程的时候注意到了这个现象:

int main()
{
    int a = 42;
    func (a);

    cout << a << endl;
}

void func (int x)
{
    x = 5;
}

当然输出是 42。否则要得到 5 作为输出,我可以使用地址重写函数:

void func (int &x)
{
    x = 5;
}

这是我理解的,但是当我这样做时:

int main()
{
    int a[2][2] = { {2,2}, {2,2} };
    func (a);

    cout << a[1][2] << endl;
}

void func (int x[2][2])
{
    x[1][2] = 5;
}

输出实际上是 5,但对我来说这是无法解释的。为什么 func 会影响 main 的任何变量?就因为它是一个二维数组?

【问题讨论】:

  • 你一开始就不应该使用数组。
  • 为什么降价?这个问题在我看来完全合理。
  • “否则要得到 5 作为输出,我可以使用地址重写函数”。小心:void func (int &amp;x) 表示您正在通过 referenceint &amp; 是对 int 的引用。当然,它看起来像“地址”运算符,但它是不同的。当您调用函数时,&amp; 表示您要传递变量的地址(例如,如果您调用了func(&amp;a);,但在函数声明和definitions 你是在表示参数会通过引用传递。也许你只是用错了词,但是如果这个概念不清楚,一定要检查一下!
  • 我看到 Christian Hackl 刚刚解释了我的意思。
  • @FabioTurati:对于 C++ 初学者来说(可以理解的)令人困惑的事情就是 &amp; 字符用于两个完全不相关的事情。

标签: c++ arrays function multidimensional-array


【解决方案1】:

否则要获得 5 作为输出,我可以使用 地址:

void func (int &x)
{
    x = 5;
}

这里没有“地址”。 x 是一个参考。不要混淆引用和指针;它们是完全不同的东西。

void func (int x[2][2])

这是一种将数组传递给函数的尝试。真正发生的是你传递了一个指针。您传递的数组被称为“衰减”到指向数组第一个元素的指针(丢失所有大小信息)。

就好像你用这样一个可爱的参数类型编写了你的​​函数:

void func(int (*x)[2])
{
    x[1][2] = 5;
}

这里令人困惑的是您使用的是二维数组。第二个维度的大小实际上保留在类型中。你可以再次将x[1] 传递给void func2(int y[2]) 这样的函数,它会衰减为一个简单的int*

不过,关键是您没有传递了一个引用而是一个指针。指针本身是按值传递的——但指针是用来间接修改main中的数组。

请注意,可以通过引用传递数组。方法如下:

void func(int (&x)[2][2])

结论:

  • 您需要了解引用和指针之间的区别。
  • 尝试按值传递数组会使其“衰减”到指向其第一个元素的指针。
  • 可以通过引用传递数组,即使语法很丑陋。
  • 二维数组只会使前三个规则看起来比实际更复杂。
  • 使用std::vector / std::array 代替原始数组。

【讨论】:

    【解决方案2】:

    首先声明这个函数

    void func (int x[2][2])
    {
        x[1][2] = 5;
    }
    

    等价于

    void func (int x[][2])
    {
        x[1][2] = 5;
    }
    

    又等价于

    void func (int ( *x )[2])
    {
        x[1][2] = 5;
    }
    

    即参数的类型为指向int[2] 类型的一维数组的指针。该函数已获得内存地址。

    当你通过以下方式调用函数时

    func (a);
    

    数组a 被隐式转换为指向其第一个“行”(第一个元素)的指针。

    所以该函数处理一个指针。它不会改变指针本身。 它改变了这个指针指向的内存。

    函数体表达式表达式内

    x[1]
    

    等价于*( a + 1 )并产生数组a的第二“行”(索引从0开始)。我们将其命名为行

    表达式

    x[1][2]
    

    等同于row[2],并产生对该行第三个元素的引用。

    在函数中改变了数组占用内存的这个单元格中的值。那就是该函数不处理此单元格中的值的副本。它直接处理单元本身,因为我们提供了它的地址。

    考虑到这个声明

    cout << a[1][2] << endl;
    

    错了。数组a 的有效索引范围声明为

    int a[2][2]
    

    [0, 1]

    所以你试图覆盖数组之外的内存。

    【讨论】:

    • 哇,这是一个非常详细的答案,最重要的是,您甚至发现了一个我完全错过的错误(越界访问)。干得好,+1!
    • @FabioTurati 所有人都在回答主要问题。:)
    【解决方案3】:

    这被称为衰减
    当作为参数传递时,数组衰减为原始指针。

    对于编译器,declaration

    void func (int x[2][2]); 
    

    相同
    void func (int* x[2]);
    

    因此,数组作为指向第一个元素的指针发送,函数可以更改原始数组。

    【讨论】:

    • 为了获得良好的学习效果,OP应该尽量定义这两个函数。编译器会抱怨函数重定义。
    • @ChristianHackl 我尝试了这个答案中给出的两个函数,编译器没有抱怨。这是为什么?我做错了什么还是我的编译器有错误?
    • @ᐅJohannesSchaub-litbᐊ:啊,我现在才看到第二个函数中缺少内括号。嗯... C 声明语法很难:)
    • @ᐅJohannesSchaub-litbᐊ:对于其他阅读和想知道的人,我指的是void func (int x[2][2]) {} void func (int (* x)[2]) {}
    猜你喜欢
    • 1970-01-01
    • 2012-07-08
    • 2016-09-06
    • 2012-11-08
    • 1970-01-01
    • 2015-02-17
    • 2014-01-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多