为什么我们不能通过值传递数组?

时间:2011-09-17 13:08:42

标签: c++ arrays function arguments

显然,我们可以将复杂的类实例传递给函数,但为什么我们不能将数组传递给函数呢?

9 个答案:

答案 0 :(得分:53)

起源是历史的。问题是规则“数组在传递给函数时衰减为指针”很简单。

复制数组会有点复杂而且不太清楚,因为不同参数和不同函数声明的行为会发生变化。

请注意,您仍然可以按值进行间接传递:

struct A { int arr[2]; };
void func(struct A);

答案 1 :(得分:26)

这是另一个视角:C中没有单一类型的“数组”。相反,T[N]是每个N不同的类型。因此T[1]T[2]等等都是不同的类型

在C语言中没有函数重载,因此唯一可以允许的是一个函数,它接受(或返回)一个单一类型的数组

void foo(int a[3]);  // hypothetical

据推测,这被认为远不如实际决定使所有数组衰变成指向第一个元素的指针并且要求用户通过其他方式传递大小的实用决策。毕竟,以上内容可以改写为:

void foo(int * a)
{
  static const unsigned int N = 3;
  /* ... */
}

因此,没有表现力的损失,但普遍性的巨大收益。

请注意,这在C ++中没有任何不同,但模板驱动的代码生成允许您编写模板化函数foo(T (&a)[N]),其中N是为您推导的 - 但这只是意味着您可以创建一整套不同的,不同的函数,每个函数对应N的每个值。

作为极端情况,假设您需要两个函数print6(const char[6])print12(const char[12])来表示print6("Hello")print12("Hello World")如果您不想腐烂数组到指针,否则你必须添加一个显式转换,print_p((const char*)"Hello World")

答案 2 :(得分:6)

回答一个非常古老的问题,因为问题是市场,C ++只是为完成目的而添加,我们可以使用std :: array并通过值或引用将数组传递给函数,从而防止访问绑定索引:

以下是样本:

#include <iostream>
#include <array>

//pass array by reference
template<size_t N>
void fill_array(std::array<int, N>& arr){
    for(int idx = 0; idx < arr.size(); ++idx)
        arr[idx] = idx*idx;
}

//pass array by value
template<size_t N>
void print_array(std::array<int, N> arr){
    for(int idx = 0; idx < arr.size(); ++idx)
        std::cout << arr[idx] << std::endl;
}

int main()
{
    std::array<int, 5> arr;
    fill_array(arr);
    print_array(arr);
    //use different size
    std::array<int, 10> arr2;
    fill_array(arr2);
    print_array(arr2);
}

答案 3 :(得分:4)

您无法按值传递数组的原因是因为没有特定的方法来跟踪数组的大小,因此函数调用逻辑将知道要分配多少内存以及要复制的内容。您可以传递类实例,因为类具有构造函数。数组没有。

答案 4 :(得分:0)

相当于首先制作数组的副本,然后将其传递给函数(对于大型数组来说效率非常低)。

除此之外我会说这是出于历史原因,即无法通过C中的值传递数组。

我的猜测是,在C ++中不按值引入传递数组的原因是,与数组相比,对象被认为是中等大小。

正如delnan所指出的,当使用std::vector时,您实际上可以按值传递类似数组的对象。

答案 5 :(得分:0)

传递值:指向数组的指针的值。请记住,在C中使用方括号表示法只是取消引用指针的简写。 ptr [2]表示*(ptr + 2)。

删除括号会获得一个指向数组的指针,该指针可以通过值传递给函数:

int x[2] = {1, 2};
int result;
result = DoSomething(x);

请参阅ANSI C规范中的list of types。数组不是原始类型,而是由指针和运算符组合而成。 (它不会让我放置另一个链接,但构造在“数组类型派生”下描述。)

答案 6 :(得分:0)

综述:

  1. 传递数组第一个元素的地址 &a = a = &(a[0])
  2. 新指针(新指针,新地址,4个字节,在内存中)
  3. 指向相同的内存位置其他类型
  4. 示例1:

    void by_value(bool* arr) // pointer_value passed by value
    {
        arr[1] = true;
        arr = NULL; // temporary pointer that points to original array
    }
    
    int main()
    {
        bool a[3] = {};
        cout << a[1] << endl; // 0
        by_value(a);
        cout << a[1] << endl; // 1 !!! 
    }
    

    <强>地址:

    [main] 
         a = 0046FB18 // **Original**
         &a = 0046FB18 // **Original**
    [func]
         arr = 0046FB18 // **Original**
         &arr = 0046FA44 // TempPTR
    [func]
         arr = NULL
         &arr = 0046FA44 // TempPTR
    

    示例2:

    void by_value(bool* arr) 
    {
        cout << &arr << arr; // &arr != arr
    }
    
    int main()
    {
        bool a[3] = {};
        cout << &a << a; // &a == a == &a[0]
        by_value(arr);
    }
    

    <强>地址

    Prints: 
    [main] 0046FB18 = 0046FB18
    [func] 0046FA44 != 0046FB18
    

    请注意:

    1. &amp;(required-lvalue): lvalue -to-&gt;右值
    2. 数组衰减:新指针(临时)指向(按值)数组地址
    3. <强> readmore:

        

      Rvalue

           

      Array Decay

答案 7 :(得分:0)

这样做是为了保持与B语言的句法和语义兼容性,在B语言中,数组被实现为物理指针。

Dennis Ritchie的"The Development of the C Language"中给出了对该问题的直接答案,请参见“批判”部分。它说

  

例如,函数声明中的方括号

int f(a) int a[]; { ... }
  

是活的化石,是NB声明指针的方式的残余;仅在这种特殊情况下,a在C中被解释为指针。该表示法之所以幸存,部分原因是出于兼容性的考虑,部分是出于合理化的考虑,即它将允许程序员与读者进行交流,以传递f从数组生成的指针,而不是引用单个整数的意图。 。不幸的是,它不仅使学习者感到困惑,而且使读者感到震惊。

这应该在本文的前一部分(特别是“胚胎C”)的背景下进行,该文章解释了在C中引入struct类型如何导致拒绝采用B和BCPL风格的方法来实现数组(即作为普通指针)。 C切换到非指针数组实现,仅将旧的B样式语义保留在函数参数列表中。

因此,数组参数行为的当前变体是一种折衷的结果:一方面,我们必须在struct s中拥有可复制的数组,另一方面,我们希望保留与用B编写的函数,其中数组始终“通过指针”传递。

答案 8 :(得分:-1)

实际上,指向数组的指针是通过值传递的,使用被调用函数内部的指针会让你感觉数组是通过引用传递的,这是错误的。尝试更改数组指针中的值以指向函数中的另一个数组,您会发现原始数组不受影响,这意味着数组不会通过引用传递。