我对在C / C ++中传递数组感到有点困惑。我看到一些签名就像这样的案例
void f(int arr[])
有些就像这样
void f(int arr[], int size)
任何人都可以详细说明有什么区别以及何时以及如何使用它?
答案 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]);
他们唯一需要注意的是n
和m
来到参数列表中的A
之前。因此,总是使用该顺序的参数声明函数。
答案 7 :(得分:0)
不同之处在于第二个包含指示数组大小的参数。逻辑结论是,如果您不使用这样的参数,该函数不知道数组大小是什么。事实证明确实如此。实际上,它不知道你有一个数组。实际上,你没有数组来调用函数。
这里的数组语法在方括号内没有指定的大小,是假的。该参数实际上是一个指针。有关详细信息,请参阅http://c-faq.com/aryptr/index.html,尤其是第4部分。