Reputation: 33
Consider the following code:
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;
}
Which of the implementations of method modify below set all elements of the matrix x to zero?
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;
}
}
}
I'm quite confused about why the third option is not correct. Is there a difference between passing an array located in stack and an array located in heap?
Upvotes: 3
Views: 678
Reputation: 10868
It may help to recall that there are no arrays in C, only pointers and blocks of memory. The rest is compiler fakery.
Your main program allocates a block of memory for 5 *int
pointers (called x
) on the stack and 5 blocks of 5 int
s on the heap. It calls the function with a pointer to the beginning of x, and relies on the function to perform the correct pointer arithmetic to access the other blocks correctly.
The first two functions are equivalent in this case (that's not always true), and the pointer arithmetic correctly matches the allocations. The third function incorrectly performs pointer arithmetic to address 25 ints arranged 5x5, so the first 5 stores overwrite a *int
by an int
and the 6th access is out of bounds (assuming int
and *int
are the same size).
I've explained it this way to emphasise the necessity to understand the relationship between arrays and pointer arithmetic in C. If you really understand this you won't need to ask questions like this again.
Upvotes: 0
Reputation: 31379
The compiler gives a pretty good clue:
~$ 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) {
~~~~^~~~~~~
The declaration int x[5][5]
declares an array of arrays. That's something completely different from int *x[5];
which declares an array of pointers. Fun fact: Add parenthesis like this int (*x)[5]
and you will instead get a pointer to an int array of size 5. Here is a good site that translates C declarations to English: https://cdecl.org/
Is there a difference between passing an array located in stack and an array located in heap?
Dynamically allocated memory usually ends up on the heap, while the rest usually goes to the stack. However, this is just an implementation detail, and nothing in the C standard requires either a heap or a stack to exist at all.
When you're passing an array to a function it will decay to a pointer to it's first element. The function will have no idea of where the data is (hmmm, well since the pointer has the actual address it does, but it does not know about heap or stack) or how it was allocated. So consider this code snippet:
void setToZero(int * arr, int size) {
for(int i=0; i<size; i++) arr[i] = 0;
}
The function will have EXACTLY the same effect for both x
and y
:
int x[10];
int *y = malloc(10 * sizeof *y);
setToZero(x);
setToZero(y);
Upvotes: 5