考虑以下代码:
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的所有元素都设置为零?
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;
}
}
}
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;
}
}
}
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;
}
}
}
我很困惑为什么第三个选项不正确。传递位于堆栈中的数组和位于堆中的数组之间有区别吗?
答案 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;
}
该功能对x
和y
的作用完全相同:
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语言中数组与指针算术之间关系的必要性。如果您真正理解了这一点,则无需再次提出类似的问题。