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

时间:2011-07-04 05:53:25

标签: c arrays

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

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[]的原始副本已更改。

你能解释一下原因吗?

10 个答案:

答案 0 :(得分:95)

将数组作为参数传递时,

void arraytest(int a[])

表示与

完全相同
void arraytest(int *a)

所以你 修改main中的值。

由于历史原因,数组不是一等公民,不能按值传递。

答案 1 :(得分:6)

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

答案 2 :(得分:6)

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

答案 3 :(得分:6)

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

编辑:有三种特殊情况,数组不会衰减到指向它的第一个元素的指针:

  1. sizeof asizeof (&a[0])不同。
  2. &a&(&a[0])不同(与&a[0]不完全相同)。
  3. char b[] = "foo"char b[] = &("foo")不同。

答案 4 :(得分:4)

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

因此,当您开始修改函数内部的数组时,您正在修改原始数组。

请注意a[1]*(a+1)

答案 5 :(得分:4)

如果你想将一维数组作为参数传递给函数,你必须以下列三种方式之一声明一个形式参数,并且所有三种声明方法都会产生类似的结果,因为每个告诉编译器将接收一个整数指针

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

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

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

因此,您正在修改原始值。

谢谢!!!

答案 6 :(得分:3)

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

来自K&R2nd的引用:

  

当一个数组名传递给一个函数时,传递的是   初始元素的位置。在被调用的函数中,这个   参数是局部变量,因此数组名称参数是a   指针,即包含地址的变量。

写作:

void arraytest(int a[])

与写作具有相同的含义:

void arraytest(int *a)

因此,尽管你没有明确地编写它,但是当你传递指针时,你正在修改主要的值。

我真的建议您阅读this

此外,您可以在SO here

上找到其他答案

答案 7 :(得分:3)

将多维数组作为参数传递给函数。 传递一个昏暗的数组作为参数或多或少是微不足道的。 让我们来看看传递2个暗淡数组的更有趣的情况。 在C中,您不能使用指向指针构造(int **)的指针而不是2 dim数组。 让我们举个例子:

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个int数组的指针。 我可以传递任何2个具有5列的暗淡数组作为参数:

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个元素的数组的指针,它们是指向不同类型的指针!

那么,如何为更高维度定义函数参数?很简单,我们只是按照模式! Hier的功能调整相同,采用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;
            }
        }
    }
}

你可以期待它,它可以作为参数任何3个暗淡的数组,在第二个维度中有4个元素,在第三个维度中有5个元素。这样的事情就可以了:

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

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

答案 8 :(得分:0)

@Bo Persson在其出色的回答here中正确指出:

================================================ ==

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

void arraytest(int a[])

与...完全相同

void arraytest(int *a)

================================================ ==

但是,我还要补充一点:

与...完全相同

void arraytest(int a[0])

含义与

完全相同
void arraytest(int a[1])

含义与

完全相同
void arraytest(int a[2])

含义与

完全相同
void arraytest(int a[1000])

事实上,这里的array参数内的“大小”值显然只是出于美观/自我记录的目的,并且可以是您想要的任何正整数(我认为是size_t类型)!

但是,实际上,您应该使用它来指定您希望函数接收的数组的最小大小,以便在编写代码时很容易跟踪和验证。 {3}}标准(MISRA-C-2012)甚至声明:

  

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

...

  

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

...

  

为函数参数使用数组声明符比使用指针更清楚地指定了函数接口。该函数期望的最小元素数已明确说明,而使用指针是不可能的。   [强调]

换句话说,即使C标准在技术上不强制使用,也建议使用显式的大小格式。它至少有助于向您(作为开发人员)和其他使用代码的人员阐明,函数希望您传入的size数组。

答案 9 :(得分:0)

如果您使用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;
}