要传递2D(或更高维度)数组,请参见我的其他答案:
- 如何在C和C ++中将多维[C风格]数组传递给函数,以及这里:
- 仅通过
std :: vector<std :: vector <int>>&
将多维数组传递给C ++函数
在C(和C ++)中将1D数组作为函数参数传递
1. C中的标准数组用法,自然类型衰减(调整)从数组到ptr
@Bo Persson 在他的出色回答此处中正确地陈述:
When passing an array as a parameter, this
void arraytest(int a[])
means exactly the same as
void arraytest(int *a)
让我对这两个代码片段添加一些注释,以增加清晰度:
// param is array of ints; the arg passed automatically "adjusts" (frequently said
// informally as "decays") from `int []` (array of ints) to `int *`
// (ptr to int)
void arraytest(int a[])
// ptr to int
void arraytest(int *a)
然而,我也要补充一点,上述两种形式也:
mean exactly the same as
// array of 0 ints; automatically adjusts (decays) from `int [0]`
// (array of zero ints) to `int *` (ptr to int)
void arraytest(int a[0])
which means exactly the same as
// array of 1 int; automatically adjusts (decays) from `int [1]`
// (array of 1 int) to `int *` (ptr to int)
void arraytest(int a[1])
which means exactly the same as
// array of 2 ints
// (array of 2 ints) to `int *` (ptr to int)
void arraytest(int a[2])
which means exactly the same as
// array of 1000 ints; automatically adjusts (decays) from `int [1000]`
// (array of 1000 ints) to `int *` (ptr to int)
void arraytest(int a[1000])
etc.
在上述所有数组示例中,正如下面代码中的示例调用所示,输入参数类型会调整(衰减)为
int *
,即使打开了构建选项
-Wall -Wextra -Werror
(有关这3个构建选项的详细信息,请参见
我的存储库),也可以无警告和错误地调用,如下所示:
int array1[2];
int * array2 = array1;
// works fine because `array1` automatically decays from an array type
// to a pointer type: `int *`
arraytest(array1);
// works fine because `array2` is already an `int *`
arraytest(array2);
实际上,这里数组参数中的“size”值(
[0]
,
[1]
,
[2]
,
[1000]
等)显然仅用于美学/自我记录目的,可以是任何正整数(我认为是
size_t
类型)!
然而,在实践中,您应该使用它来指定您希望函数接收到的数组的最小大小,以便在编写代码时轻松跟踪和验证。
MISRA-C-2012标准(
在此处购买/下载2012版236页标准PDF,售价15.00英镑)甚至明确规定(强调添加):
Rule 17.5 The function argument corresponding to a parameter declared to have an array type shall have an appropriate number of elements.
...
If a parameter is declared as an array with a specified size, the corresponding argument in each function call should point into an object that has at least as many elements as the array.
...
The use of an array declarator for a function parameter specifies the function interface more clearly than using a pointer. The minimum number of elements expected by the function is explicitly stated, whereas this is not possible with a pointer.
换句话说,他们建议使用显式大小格式,即使C标准在技术上没有强制执行 - 至少它可以帮助开发者以及使用代码的其他人明确该函数期望你传递的数组大小。
2. 在C语言中强制数组类型安全
(不建议(更正:有时候建议,尤其是对于固定大小的多维数组),但是这是可能的。请参见我在结尾反对这样做的简要论点。另外,如果想了解我的多维数组[例如:2D数组]版本,请看这里。)
正如@Winger Sendon在我的答案下方的评论中指出的那样,我们可以通过数组大小来强制C对待数组类型不同!
首先,你必须认识到,在我上面的例子中,使用int array1[2];
像这样:arraytest(array1);
会导致array1
自动衰减为int *
。然而,如果你取array1
的地址并调用arraytest(&array1)
,你会得到完全不同的行为!现在它不会衰减成int *
了!这是因为如果你取数组的地址,那么你已经有了一个指针类型,而指针类型不会调整到其他指针类型。只有数组类型才能调整到指针类型。所以,&array1
的类型是int (*)[2]
,这意味着"指向大小为2的int数组的指针",或"指向类型为int的大小为2的数组的指针",也可以说成"指向2个int数组的指针"。因此,你可以通过传递显式的数组指针来强制C检查数组的类型安全性,就像这样:
void arraytest(int (*a)[2])
{
}
这个语法很难读,但与
函数指针类似。在线工具
cdecl告诉我们
int (*a)[2]
的意思是:
"声明a为指向由2个int组成的数组的指针"(指向2个
int
的数组的指针)。不要将其与没有括号的版本混淆:
int * a[2]
,它的意思是:
"声明a为包含2个指向int的指针的数组"(又称:包含2个
指向int
的指针的数组,又称:包含2个
int*
的数组)。
现在,这个函数要求你使用地址运算符(
&
)调用它,使用正确大小的数组的指针作为输入参数!:
int array1[2];
arraytest(&array1);
然而,这将会产生一个警告:
int array1[2];
arraytest(array1);
您可以在此处测试此代码。
为了强制C编译器将此警告转换为错误,以便您必须始终仅使用正确大小和类型的输入数组(在本例中为int array1[2];
)调用arraytest(&array1);
,请将-Werror
添加到构建选项中。如果在onlinegdb.com上运行上面的测试代码,请单击右上角的齿轮图标,然后单击“额外的编译器标志”以键入此选项。现在,这个警告:
main.c:34:15: warning: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Wincompatible-pointer-types]
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
将会转化为这个构建错误:
main.c: In function ‘main’:
main.c:34:15: error: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Werror=incompatible-pointer-types]
arraytest(array1); // warning!
^~~~~~
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
void arraytest(int (*a)[2])
^~~~~~~~~
cc1: all warnings being treated as errors
请注意,您还可以创建指向给定大小数组的“类型安全”指针,如下所示:
{{像这样}}
int array[2]; // variable `array` is of type `int [2]`, or "array of 2 ints"
// `array_p` is a "type safe" ptr to array of size 2 of int; ie: its type
// is `int
...但我并不一定推荐这样做(在C中使用这些“类型安全”的数组),因为它让我想起了C++中用于强制实现类型安全的花招,这样做的代价是语言语法复杂度、冗长性和难以架构代码,而我对此很不喜欢,并且之前多次发表过抨击言论(例如:请参见“我的C++思考”)。
如需进行额外的测试和实验,请查看下面的链接。
参考资料
请参阅上方链接。还有:
- 我的在线代码实验:https://onlinegdb.com/B1RsrBDFD
另请参阅:
- 我在多维数组(例如2D数组)上的回答,扩展了上述内容,并在多维数组的类型安全方法可行时使用 "类型安全" 方法:如何将多维数组传递给C和C++中的函数
main()
函数必须返回int
类型。 - underscore_d