将位于堆中的数组传递给C中的Func

时间:2019-02-19 22:40:22

标签: c

考虑以下代码:

int main(void) {
    int *x[5];

    for(int i = 0; i < 5; i++) {
        x[i] = malloc(sizeof(int) * 5);
    }
    for(int i = 0; i < 5; i++) {
        for(int j = 0; j < 5; j++) {
            x[i][j] = i * j;
        }
    }
    modify(x, 5, 5);
    return 0; 
}

下面哪种方法修改的实现将矩阵x的所有元素都设置为零?

  1. void modify(int **x, int m, int n) {
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                x[i][j] = 0;
            }
        }
    }
    
  2. void modify(int *x[], int m, int n) {
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                x[i][j] = 0;
            }
        }
    }
    
  3. void modify(int x[5][5], int m, int n) {
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                x[i][j] = 0;
            }
        }
    }
    

我很困惑为什么第三个选项不正确。传递位于堆栈中的数组和位于堆中的数组之间有区别吗?

2 个答案:

答案 0 :(得分:4)

编译器提供了一个很好的线索:

~$ gcc mat.c 
mat.c: In function ‘main’:
mat.c:22:12: warning: passing argument 1 of ‘modify’ from incompatible pointer type [-Wincompatible-pointer-types]
     modify(x, 5, 5);
            ^
mat.c:3:17: note: expected ‘int (*)[5]’ but argument is of type ‘int **’
 void modify(int x[5][5], int m, int n) {
             ~~~~^~~~~~~

声明int x[5][5]声明一个数组数组。这与声明指针数组的int *x[5];完全不同。有趣的事实:像这样int (*x)[5]加上括号,您将获得一个指向大小为5的int数组的指针。这是一个将C声明转换为英语的好站点:https://cdecl.org/

  

传递位于堆栈中的数组与位于堆中的数组之间有区别吗?

通常动态分配的内存通常结束在堆上,而其余通常进入堆栈。但是,这只是实现细节,而C标准中的任何内容都不需要堆或堆栈。

当您将数组传递给函数时,它将衰减指向它的第一个元素的指针。该函数将不知道数据在哪里(嗯,好吧,因为指针具有它的实际地址,但它不知道堆或堆栈)或如何分配数据。因此,请考虑以下代码段:

void setToZero(int * arr, int size) {
    for(int i=0; i<size; i++) arr[i] = 0;
}

该功能对xy的作用完全相同:

int x[10];
int *y = malloc(10 * sizeof *y);
setToZero(x);
setToZero(y);

答案 1 :(得分:0)

回顾一下C中没有数组,只有指针和内存块可能会有所帮助。剩下的就是编译器的伪造。

您的主程序为堆栈上的5个*int指针(称为x)分配了一个内存块,并为堆上的5个int s分配了5个块。它使用指向x开头的指针来调用该函数,并依靠该函数执行正确的指针算法以正确访问其他块。

在这种情况下,前两个函数是等效的(并不总是如此),并且指针算法正确匹配了分配。第三个函数错误地执行了指针算术,以寻址5x5排列的25个整数,因此前五个存储被*int覆盖了int,并且第六次访问超出范围(假定int和{ {1}}的大小相同。

我已经通过这种方式进行了解释,以强调理解C语言中数组与指针算术之间关系的必要性。如果您真正理解了这一点,则无需再次提出类似的问题。