我们什么时候需要将数组的大小作为参数传递

时间:2011-07-10 02:13:58

标签: c++ c arrays parameters

我对在C / C ++中传递数组感到有点困惑。我看到一些签名就像这样的案例

void f(int arr[])

有些就像这样

void f(int arr[], int size)

任何人都可以详细说明有什么区别以及何时以及如何使用它?

8 个答案:

答案 0 :(得分:9)

首先,传递给函数的数组实际上传递了一个指向数组第一个元素的指针,例如,如果你有

int a[] = { 1, 2, 3 };
f(a);

然后,f()&a[0]传递给它。因此,在编写函数原型时,以下内容是等效的:

void f(int arr[]);
void f(int *arr);

这意味着数组的大小会丢失,而f()通常无法确定大小。 (这是我更喜欢void f(int *arr)形式而非void f(int arr[])的原因。)

有两种情况f()不需要这些信息,在这两种情况下,可以没有额外的参数。

首先,arr中有一些特殊的,同意的值,调用方和f()都指的是“结束”。例如,可以同意值0表示“完成”。

然后可以写:

int a[] = { 1, 2, 3, 0 }; /* make sure there is a 0 at the end */
int result = f(a);

并定义f()之类的内容:

int f(int *a)
{
    size_t i;
    int result = 0;
    for (i=0; a[i]; ++i)  /* loop until we see a 0 */
        result += a[i];
    return result;
}

显然,上述方案只有在两者调用者和被调用者同意约定时才有效,并遵循它。一个例子是C库中的strlen()函数。它通过查找0来计算字符串的长度。如果你传递了最后没有0的东西,那么所有的赌注都会被取消,你处于未定义的行为领域。

第二种情况是你没有真正的阵列。在这种情况下,f()会获取指向对象的指针(示例中为int)。所以:

int change_me = 10;
f(&change_me);
printf("%d\n", change_me);

void f(int *a)
{
    *a = 42;
}

很好:无论如何f()都没有在数组上运行。

答案 1 :(得分:2)

如果在C或C ++中传递数组,则仅传递其地址。这就是为什么第二种情况很常见,其中第二个参数是数组中元素的数量。该函数不知道,只能通过查看数组的地址,它应该包含多少元素。

答案 2 :(得分:1)

你可以写

void f( int *arr, int size )

同样,后者(大小)允许在读/写时不跨越数组边界

答案 3 :(得分:1)

第一个签名只是传递数组,无法分辨数组的大小,并且可能导致出现越界错误和/或安全漏洞的问题。\

第二个签名是一个更安全的版本,因为它允许函数检查数组的大小,以防止第一个版本的缺点。

除非这是家庭作业,否则原始阵列有点过时了。请改用std :: vector。它允许传递向量而不必手动传递大小,就像它为你做的那样。

答案 4 :(得分:1)

数组的大小不随数组本身传递。因此,如果其他函数需要大小,它将把它作为参数。

问题是,某些函数隐式地将数组理解为具有一定的大小。所以他们不需要明确指定它。例如,如果您的函数在3个浮点数组上运行,则您不需要用户告诉您它是一个包含3个浮点数的数组。拿个阵列吧。

然后有那些函数(让它们称之为“可怕”,因为它们是)将用任意数据填充数组,直到该数据定义的点。 sprintf可能是“最好”的例子。它将继续将字符放在该数组中,直到完成编写它们为止。这非常糟糕,因为用户和函数之间没有关于这个数组有多大的明确或隐含的协议。 sprintf会写一些字符,但用户无法确切知道有多少字符(在一般情况下)。

这就是从不使用sprintf的原因;使用snprintf_snprintf,具体取决于您的编译器。

答案 5 :(得分:1)

任何时候你需要知道数组的大小,都需要提供它。关于传递数组本身的两种形式没有什么特别之处;第一个参数是相同的方式。第二种方法只提供了解数组大小所需的信息,而第一种方法则没有。

有时,数组本身会保存有关其大小的信息。例如,在您的第一个示例中,可能arr[0]设置为数组的大小,实际数据从arr[1]开始。或者考虑c-strings的情况......你只提供一个char[],并假设数组在第一个元素上等于\0。在您的示例中,负值可能充当类似的标记。或者函数可能根本不关心数组的大小,而只是假设它足够大。

这些方法本质上是不安全的,但是很容易忘记设置arr[0]或者意外地覆盖空终止符。然后,f突然无法知道它有多少空间。 始终更愿意通过您显示的size参数或使用指向数组末尾的第二个指针显式提供大小。后者是C ++中标准库函数通常采用的方法。但是仍然存在提供不正确大小的问题,这就是为什么在C ++中不建议您首先使用这样的数组...使用实际容器来跟踪您的信息。

答案 6 :(得分:1)

C和C ++不是一回事。但是,他们有一些共同的子集。你在这里观察到的是,传递给函数时的“第一个”数组维度总是会导致传递指针。声明为

的函数的“签名”(C不使用该术语)
void toto(double A[23]);

始终只是

void toto(double *A);

这就是上面的23有些多余而且编译器没有使用它。 Modern C(又名C99)在这里有一个扩展,它允许你声明A总是有23个元素:

void toto(double A[static 23]);

或指针是const限定的

 void toto(double A[const 23]);

如果您添加其他尺寸图片更改,则使用 数组

void toto(double A[23][7]);

在C和C ++中都是

void toto(double (*A)[7]);

这是指向7元素数组的指针。在C ++中,这些数组边界必须是整数常量。在C中它可以是动态的。

void toto(size_t n, size_t m, double A[n][m]);

他们唯一需要注意的是nm来到参数列表中的A之前。因此,总是使用该顺序的参数声明函数。

答案 7 :(得分:0)

不同之处在于第二个包含指示数组大小的参数。逻辑结论是,如果您不使用这样的参数,该函数不知道数组大小是什么。事实证明确实如此。实际上,它不知道你有一个数组。实际上,你没有数组来调用函数。

这里的数组语法在方括号内没有指定的大小,是假的。该参数实际上是一个指针。有关详细信息,请参阅http://c-faq.com/aryptr/index.html,尤其是第4部分。