将数组作为参数传递给 C 中的函数

我写了一个包含数组作为参数的函数, 并通过传递数组的值来调用它,如下所示。

void arraytest(int a[])
{
// changed the array a
a[0] = a[0] + a[1];
a[1] = a[0] - a[1];
a[0] = a[0] - a[1];
}


void main()
{
int arr[] = {1, 2};
printf("%d \t %d", arr[0], arr[1]);
arraytest(arr);
printf("\n After calling fun arr contains: %d\t %d", arr[0], arr[1]);
}

我发现虽然我通过传递值来调用 arraytest()函数,但是 int arr[]的原始副本发生了变化。

你能解释一下为什么吗?

273257 次浏览

传递数组第一个成员的内存位置的值。

Therefore when you start modifying the array inside the function, you are modifying the original array.

记住 a[1]*(a+1)

您没有将数组作为副本传递。它只是一个指向数组第一个元素在内存中的地址的指针。

将数组作为参数传递时,此

void arraytest(int a[])

意思完全相同

void arraytest(int *a)

所以您可以在 main 中修改 中的值。

由于历史原因,数组不是一级公民,不能通过值传递。

您正在传递数组的第一个元素的地址

在 C 语言中,除了少数特殊情况外,数组引用总是“衰减”到指向数组第一个元素的指针。因此,不可能通过值传递数组。函数调用中的数组将作为指针传递给函数,这类似于通过引用传递数组。

编辑: 在三种特殊情况下,数组不会衰减为指向其第一个元素的指针:

  1. sizeof a is not the same as sizeof (&a[0]).
  2. &a&(&a[0])不同(与 &a[0]也不完全相同)。
  3. char b[] = "foo"不同于 char b[] = &("foo")

如果希望使用 传递一维数组作为函数中的参数,则必须使用以下三种方法之一声明形式参数,并且所有三种声明方法都会产生类似的结果,因为每个 tells the compiler that an integer pointer is going to be received

int func(int arr[], ...){
.
.
.
}


int func(int arr[SIZE], ...){
.
.
.
}


int func(int* arr, ...){
.
.
.
}

因此,要修改原始值。

谢谢! ! !

在大多数情况下,C 中的数组被转换为指向数组本身第一个元素的指针。更详细地说,传递给函数的数组总是被转换成指针。

以下是 K & R 第二大道的一段话:

当数组名传递给函数时,传递的是 初始元素的位置 参数是局部变量,因此数组名称参数是 指针,即包含地址的变量。

Writing:

void arraytest(int a[])

和写作有着相同的含义:

void arraytest(int *a)

因此,尽管您没有显式地编写它,但它是在传递指针的过程中进行的,因此您要修改 main 中的值。

对于更多我真的建议阅读 这个

此外,您还可以在 SO给你上找到其他答案

将多维数组作为参数传递给函数。 将一个 dim 数组作为参数传递或多或少是琐碎的。 让我们来看一个更有趣的例子,传递一个2 dim 数组。 在 C 语言中,不能使用指针结构(int **)而不是2 dim 数组。 Let's make an example:

void assignZeros(int(*arr)[5], const int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 5; j++) {
*(*(arr + i) + j) = 0;
// or equivalent assignment
arr[i][j] = 0;
}
}

这里我指定了一个函数,它的第一个参数是一个指向5个整数数组的指针。 我可以把任何有5列的2 dim 数组作为参数传递:

int arr1[1][5]
int arr1[2][5]
...
int arr1[20][5]
...

您可能会想到定义一个更通用的函数,它可以接受任意2 dim 数组,并按如下方式更改函数签名:

void assignZeros(int ** arr, const int rows, const int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
*(*(arr + i) + j) = 0;
}
}
}

这段代码可以编译,但是当您尝试以与第一个函数相同的方式分配值时,会得到一个运行时错误。 所以在 C 中,多维数组不同于指向指针的指针... 指向指针的指针。int(*arr)[5]是一个指向包含5个元素的数组的指针, int(*arr)[6]是指向6个元素的数组的指针,它们是指向不同类型的指针!

那么,如何定义高维函数参数呢? 很简单,我们只是按照模式! 下面是同一个函数,它被调整为采用3维数组:

void assignZeros2(int(*arr)[4][5], const int dim1, const int dim2, const int dim3) {
for (int i = 0; i < dim1; i++) {
for (int j = 0; j < dim2; j++) {
for (int k = 0; k < dim3; k++) {
*(*(*(arr + i) + j) + k) = 0;
// or equivalent assignment
arr[i][j][k] = 0;
}
}
}
}

如您所料,它可以将第二维度中的4个元素和第三维度中的5个元素中的任意3个 dim 数组作为参数。任何类似的事情都是可以的:

arr[1][4][5]
arr[2][4][5]
...
arr[10][4][5]
...

但是我们必须指定所有尺寸大小直到第一个。

要传递2D (或更高的多维)数组,请参阅我的其他答案:

  1. How to pass a multidimensional [C-style] array to a function in C and C++, and here:
  2. 如何传递一个多维数组到一个函数在 C + + 只,通过 std::vector<std::vector<int>>&

在 C (和 C + +)中将1D 数组作为函数参数传递

1. C 语言中的标准数组使用,自然类型衰减(调整)从数组到 ptr

@ Bo Persson 在他的伟大回答中正确地指出:

将数组作为参数传递时,此

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)

不过,我还要补充一点,上述两种形式也是:

  1. 完全相同的意思

     // array of 0 ints; automatically adjusts (decays) from `int [0]`
    // (array of zero ints) to `int *` (ptr to int)
    void arraytest(int a[0])
    
  2. 也就是说

     // array of 1 int; automatically adjusts (decays) from `int [1]`
    // (array of 1 int) to `int *` (ptr to int)
    void arraytest(int a[1])
    
  3. which means exactly the same as

     // array of 2 ints; automatically adjusts (decays) from `int [2]`
    // (array of 2 ints) to `int *` (ptr to int)
    void arraytest(int a[2])
    
  4. 也就是说

     // array of 1000 ints; automatically adjusts (decays) from `int [1000]`
    // (array of 1000 ints) to `int *` (ptr to int)
    void arraytest(int a[1000])
    
  5. 等等。

在上面的每一个数组例子中,如下面代码中的例子调用所示,输入参数类型调整(衰减)为 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标准(点击此处购买/下载236-pg 2012-15.00版本的标准 PDF)甚至声明(重点补充) :

规则17.5与声明为具有数组类型的参数对应的函数参数应具有适当数量的元素。

...

如果将参数声明为具有指定大小的数组,则每个函数调用中的相应参数应指向至少与该数组具有同样多元素的对象。

...

对函数参数使用数组声明符比使用指针更清楚地指定函数接口。函数所期望的最小元素数是显式声明的,而使用指针则不能这样做。

换句话说,他们建议使用显式的大小格式,即使 C 标准在技术上没有强制使用它—— it at least helps clarify to you as a developer, and to others using the code, what size array the function is expecting you to pass in.


2. 在 C 语言中强制数组的类型安全

(不推荐(更正: 有时推荐使用 < em > ,特别是对于固定大小的多维数组) ,但可能。请看我最后反对这样做的简短论点。此外,对于我的多维数组[ ex: 2D array ]版本,请参阅 我的答案就在这里。)

正如@Winger Sendon 在我答案下面的评论中指出的,我们可以强制 C 根据数组 尺寸来处理数组 type

首先,您必须认识到,在上面的示例中,使用 int array1[2];如下所示: arraytest(array1);导致 array1自动衰减为 int *。然而,如果改为使用 ABC2的 < em > 地址并调用 arraytest(&array1),则会得到完全不同的行为!现在,它不衰变成一个 int *!这是因为如果你采取的 arraytest(array1);0一个数组,那么你 arraytest(array1);1有一个指针类型,指针类型不调整到其他指针类型。只有数组类型可以调整为指针类型。因此,&array1的类型是 arraytest(array1);2,意思是 arraytest(array1);3,或者 arraytest(array1);4,或者也可以说是 arraytest(array1);5。arraytest(array1);6

// `a` is of type `int (*)[2]`, which means "pointer to array of 2 ints";
// since it is already a ptr, it can NOT automatically decay further
// to any other type of ptr
void arraytest(int (*a)[2])
{
// my function here
}

这种语法很难读懂,但类似于 函数指针。在线工具 Cdecl告诉我们,int (*a)[2]的意思是: 声明一个指向 int 数组2的指针(指向由2个 int组成的数组)。不要把它和没有括号的版本混淆: int * a[2],意思是: 声明一个指向 int 的数组2(也就是: 从2个 pointersint的数组,也就是: 2个 int*的数组)。

现在,这个函数要求您像这样使用地址操作符(&)来调用它,使用一个指向正确大小的数组的指针作为输入参数!:

int array1[2];


// ok, since the type of `array1` is `int (*)[2]` (ptr to array of
// 2 ints)
arraytest(&array1); // you must use the & operator here to prevent
// `array1` from otherwise automatically decaying
// into `int *`, which is the WRONG input type here!

然而,这将产生一个警告:

int array1[2];


// WARNING! Wrong type since the type of `array1` decays to `int *`:
//      main.c:32:15: warning: passing argument 1 of ‘arraytest’ from
//      incompatible pointer type [-Wincompatible-pointer-types]
//      main.c:22:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
arraytest(array1); // (missing & operator)

你可以 在这里测试这个代码

若要强制 C 编译器将此警告转换为错误,以便您必须始终只使用大小正确的 and类型(在本例中为 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

注意,您还可以创建指向给定大小的数组的“ type safe”指针,如下所示:

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 (*)[2]`, which can also be stated: "ptr to array of 2 ints"
int (*array_p)[2] = &array;

但是我不推荐 necessarily这样做(使用 C 语言中的“类型安全”数组) ,因为它让我想起了 C + + 用来强制所有地方的类型安全的许多滑稽动作,代价是语言语法的复杂性、冗长性和难以构建代码,我不喜欢这些,并且以前抱怨过很多次(例如: 参见 “我对 C + + 的看法”)。


有关其他测试和实验,请参阅下面的链接。

References

参见上面的链接:

  1. My code experimentation online: https://onlinegdb.com/B1RsrBDFD

参见:

  1. My answer on multi-dimensional arrays (ex: 2D arrays) which expounds upon the above, and uses the "type safety" approach for multi-dimensional arrays where it makes sense: 如何传递一个多维数组到一个函数在 C 和 C + +

如果使用 a[]*a,数组总是通过引用传递:

int* printSquares(int a[], int size, int e[]) {
for(int i = 0; i < size; i++) {
e[i] = i * i;
}
return e;
}


int* printSquares(int *a, int size, int e[]) {
for(int i = 0; i < size; i++) {
e[i] = i * i;
}
return e;
}

数组也可以被称为衰减指针。

Usually when we put a variable name in the printf statement the value gets printed in case of an array it decays to the address of the first element, Therefore calling it as a decay pointer.

我们只能把衰减指针传递给函数。

像 Bo 先生说的 int arr []或 int arr [10]等价于 int * arr;

它们将有4个字节的内存空间,并存储接收到的衰减指针。我们对它们进行指针算法。