C++数组作为函数参数

9
  • 我能像传递 int 和 bool 等基本类型一样传递数组给函数吗?
  • 我能按值传递它们吗?
  • 函数如何知道它所传递的数组的大小?
8个回答

26
我能像对待基本类型如int和bool一样,将数组作为参数传递给函数吗?
可以,但只能使用指针(即:通过引用)。
我能按值传递它们吗?
不行。你可以创建支持此功能的类,但普通数组不行。
函数如何知道传递给它的数组的大小?
不知道。这就是使用vector而不是T*等东西的原因。
澄清
函数可以接受特定大小的数组的引用或指针:
void func(char (*p)[13])
{
    for (int n = 0; n < 13; ++n)
        printf("%c", (*p)[n]);
}

int main()
{
    char a[13] = "hello, world";
    func(&a);

    char b[5] = "oops";
    // next line won't compile
    // func(&b);

    return 0;
}

我相信这不是原帖作者想要的内容。

3
在C++中,数组可以通过引用传递,这样你就可以知道数组的大小。当然,对于在运行时创建的数组,这种方法不适用。 - Khaled Alshaya
@egrunin:而且它不需要模板化:void foo(int (&a)[10])将通过引用接收一个包含确切10个整数的数组。动态分配数组的问题有点复杂... new int[10]的类型是int *,而不是int [10],但您可以动态创建一个包含10个整数的数组:int (*p)[10] = new int[1][10];,然后调用上一个函数:foo(*p) - David Rodríguez - dribeas
@AraK,@David:我知道排列组合,只是不确定我们是否在考虑同一个。已附上一个简单的例子。 - egrunin
你的代码行 func(b) 虽然能编译通过,但是它的行为是未定义的。p 是一个指针参数,而不是一个数组。在 func 函数签名中的 char p[13] 可以被替换成 char *p。如果你想要实现你的目标(并且阻止那一行代码的编译),你可以使用 void func(char (&p)[13]),就像 dribeas 所说的那样。 - Steve Jessop
@Steve - 已修复。过于匆忙的剪切和粘贴。 - egrunin
显示剩余3条评论

14

你可以像C语言一样传递数组(数组会自动退化为指针),或者通过引用来传递,但不能通过值来传递。对于第二种方法,它们携带它们的大小:

template <std::size_t size>
void fun( int (&arr)[size] )
{
   for(std::size_t i = 0; i < size; ++i) /* do something with arr[i] */ ;
}

大多数情况下,使用标准库中的std::vector或其他序列容器会更加优雅,除非你特别需要使用原生数组。


这几乎不是他们随身携带尺寸的问题 ;) 尺寸可用作模板参数(或者您可以有一个非模板化版本,在某个地方定义适当的“const”)。您无法从arr本身获取大小,这就是我认为您当前措辞有点误导的地方。 - Troubadour
1
@Troubadour:在C++中,大小不是数组的一部分,而是数组类型的一部分。如果您将其非模板化,则无需添加const,唯一的区别是它只能在签名中定义的确切大小的数组中工作:void foo(int(&a)[3])仅接受3个整数的数组。 - David Rodríguez - dribeas
@dribeas:我没有意识到它强制执行正确数量的元素。这很好,谢谢你让我知道,但它仍然不能在函数内提供我需要的元素数量,这是我的观点。此外,我无法编译int N=10; void fun( int (&arr)[N] ) {}。我做错了什么还是我们有不同的目的? - Troubadour

5

我可以像传递int和bool等原始类型一样将数组传递给函数吗?

是的,你可以。将数组名称作为参数传递到函数会传递第一个元素的地址。

简而言之:

void foo(int a[]);

等同于

void foo(int * a);

我能通过值传递它们吗?

实际上不行。使用数组名称传递的“值”是第一个元素的地址。

传递 C 风格数组的唯一方法是将其包装在实现深拷贝语义的类或结构中。

函数如何知道它所传递的数组的大小?除非您的函数接受一个额外的参数,该参数是数组的大小,否则它不知道。


关于int a[]int* b的等效性:实际上还存在一个区别,就是第一种形式不允许你操作指针(如a++是非法的)。 - Raphaël Saint-Pierre
@RaphaelSP,不是的,没有这样的区别。第一种形式确实允许 a++,它们是完全等价的。 @John,“没有办法接受或传递C风格数组的副本”在严谨性上是错误的。如果将数组包装到结构体中并按值取该结构体,则会复制包装的数组,并且函数将接收到C风格数组的副本。 - Johannes Schaub - litb
@RaphaelSP:void bar(int a[]) { ++a; *a = 7; } 在g++和comeau编译都通过。 - David Rodríguez - dribeas
没错 - 我也试过了 - 我本来想说 int * const a,但是尝试后发现你可以修改指针。我会把“没有办法”澄清为不那么绝对的说法。 - JohnMcG
@Johannes, @dribeas:哇,我不知道数组变量和数组参数是不同的。我以为我无所不知呢。 - Raphaël Saint-Pierre

3

你可以传递一个数组,但是如果你想确切地知道它的大小,你必须同时传递大小:

function(array, size);

数组始终按引用传递,函数可以有两种编写方式:

Declaration:    void function (class*, int);
Implementation: void function(class array[]; int size) {}

或者

Declaration:    void function (class*, int);
Implementation: void function(class *array; int size) {}

当您将数组传递给函数时,函数实质上会接收到该数组的指针,就像上面的第二个示例中所看到的那样。这两个示例都可以完成相同的事情。

2
你可以通过值传递一个 std::vector 和其他设计良好的对象(尽管这相对较少见)。通常情况下,你应该通过引用或 const 引用进行传递。
C/C++ 数组,例如:
void foo(int x[])

在it技术中,函数参数始终通过引用传递。此外,函数无法确定调用者传入的参数的真实大小,因此您必须假定一个大小(或将其作为单独的参数传递)。


3
准确来说,数组按值传递的语法会将数组衰减为指向第一个元素的指针,然后通过值传递该指针。这与通过引用传递数组不同:void foo( int (&a)[10] )。 - David Rodríguez - dribeas

2

C风格的数组在传入函数时表现非常奇怪。对于固定大小的数组,我建议使用std::array<int, 10>代替,它可以像内置类型一样通过值传递。对于可变大小的数组,我建议使用std::vector<int>,正如其他答案中所建议的。


1
1. 是的。例如:void f(int a[]);,可以这样调用:int myArr[size]; f(myArr); 2. 不需要,数组自动以引用方式传递。如果要模拟按值传递,必须将数组封装在结构体或类中。 3. 不会自动获取数组大小,需要手动传递大小。

1
准确来说,数组的按值语法会将数组解析为指向第一个元素的指针,然后通过值传递该指针。这与通过引用传递数组不同:void foo(int(&a)[10]) - David Rodríguez - dribeas

1

是的,您可以像原始类型一样传递它们。

如果您真的想要使用一些包装器代码按值传递,那么您也可以这样做,但您可能不应该这样做。

它将采用函数原型的大小作为长度 - 这可能与传入的数据匹配,也可能不匹配 - 因此它并不真正知道。

或者!

传递向量的引用或常量引用,更安全,并且可以轻松获取大小信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接