-2

First of all I'm not confident with C, but I have a 2D array of int and I want a function to write all the values of a single line of this array.

For example:

int main(int argc, char *argv[])
{ 
    int a[2][2];
    a[0][0] = 1;
    a[0][1] = 2;
    a[1][0] = 3;
    a[1][1] = 4;
    change_array(&a[0]);
}

void change_array(int* array[])
{
    (*array)[0] = -1; 
    (*array)[1] = -1;        
}

The program crash immediately. I tried to change the change_array function to array[0] = -1 and... it works! Values are changed correctly (and I don't know why because it should be totally wrong), but if I use this function in other part of the program the values of array remain unchanged. How it could be possible? Any suggestion to successfully change the values of my array? Thank you very much!

mordicchio
  • 135
  • 4
  • 12
  • 1
    Dupe thousands of times over. Arrays are not pointers. –  Jun 24 '13 at 12:08
  • 2
    your orignal code in gcc will say: `warning: passing argument 1 of ‘change_array’ from incompatible pointer type` and `note: expected ‘int **’ but argument is of type ‘int (*)[2]’` even without -Wall, which is already very clear. – vvy Jun 24 '13 at 12:19

3 Answers3

2

You can try to do it like this:

#include <stdio.h>

void change_array(int array[2][2])
{
    array[0][0] = -1;
    array[0][1] = -1;
}

int main(int argc, char *argv[])
{
    int a[2][2];
    a[0][0] = 1;
    a[0][1] = 2;
    a[1][0] = 3;
    a[1][1] = 4;
    printf("%d %d\n%d %d\n\n", a[0][0], a[0][1], a[1][0], a[1][1]);
    change_array(a);
    printf("%d %d\n%d %d\n\n", a[0][0], a[0][1], a[1][0], a[1][1]);
}

It depends on your needs, but in some cases I have found that it is better to use a single-diemensional array and build a getter/setter for 2 diemensions. Such solution can be found in this answer: Correct way to allocate and free arrays of pointers to arrays

Community
  • 1
  • 1
Dariusz
  • 21,561
  • 9
  • 74
  • 114
  • What's "not standard"? –  Jun 24 '13 at 12:15
  • Also, using multidimensional arrays is perfectly fine, why should one bother messing with an explicit setter/getter? –  Jun 24 '13 at 12:17
  • @H2CO3 depends on a case, but whenever I used a standard 2d array sooner or later I had to modify it to a dynamic version. And since I didn't want a doubly allocated ** structure I used the getter/setter i mentioned. I changed the answer a bit, though. – Dariusz Jun 24 '13 at 12:20
  • @H2CO3 I removed that note; that function declaration seemed strange to my eyes, but after some googling I have found out that it is perfectly fine. Thanks for the remark. – Dariusz Jun 24 '13 at 12:24
  • Don't forget that you can dynamically allocate a 2D array as well. `int (*arr)[10] = malloc(sizeof(*arr) * 10);` creates a 10x10 array of `int`s. –  Jun 24 '13 at 12:24
  • @H2CO3 isn't that just syntactic sugar? – Dariusz Jun 24 '13 at 12:27
  • Syntactic sugar for what? Using that code, - seemingly - you'll obtain a proper 2D array, dynamically allocated. More precisely, an array of 10 pointers to an array of 10 `int`s. And the syntax with which you subscript that has exactly the same semantics as if you had a statically allocated 2D array –  Jun 24 '13 at 12:34
  • it's virtually `malloc(10*10*sizeof(int))` and accessing the array `arr[y][x]` is syntactic sugar for `arr[10*y+x]`. – Dariusz Jun 24 '13 at 12:42
  • Well, if you wish so, yes it is, the point is that the allocation is contiguous (i. e. we don't have a pointer-to-pointer), and the compiler does the arithmetic required for indexing implicitly. –  Jun 24 '13 at 12:46
  • Indeed it is, I never claimed otherwise. Getters and setters provide means to check the bounds and return value instead of generating error. – Dariusz Jun 24 '13 at 12:53
0

Your code passes something to change_array that is different from the parameter declared for change_array.

In the code change_array(&a[0]), a is an array of two arrays of two int. So a[0] is the first array of two int. So &a[0] is the address of the first array of two int. Compare this with the declaration of change_array. In void change_array(int* array[]), array is an array of pointers to int. So that is a different type.

Instead, you could declare change_array with void change_array(int (*array)[]). Then array is a pointer to an array of int, and your code would work (using (*array)[0] = -1).

Note: You should compile with warnings enabled, and preferably with strict or pedantic language semantics. Then the compiler should have warned you that change_array is used in main without a prior declaration. You should have a prior declaration, so that the compiler knows the full type of change_array before it is used. With that, the compiler would have seen that the wrong type was passed, and it would warn you.

Although the above would correct your code, most people would use a different solution. They would declare change_array with void change_array(int *array), and they would call it with change_array(a[0]).

In change_array(a[0]), a[0] is the first array of two int. However, there is a rule in the C language that an array expression is converted to a pointer to its first element. So a[0] automatically becomes &a[0][0]. (This conversion occurs whenever the array is not the operand of &, sizeof, or _Alignof and is not a string literal used to initialize an array.) So the call is passing a pointer to int, which matches the parameter that is a pointer to int. Then the parameter array can be used as array[i] to access array elements, so you can use the simpler syntax array[0] = -1 instead of (*array)[0] = -1.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
-3

In C, an array variable decays into a pointer to the memory address that contains the first element of the array, when it is passed as a parameter to a function. That is joined to the fact that all it's elements are placed in contigous memory. So C only needs to save the first position of the array (no matter how many dimensions it has) and will then be able to calculate, based on the indexes, what offset of the pointed memory it should access.

So, on your particular piece of code variable a points to the first element, which is a[0][0]. So, basically doing this:

change_array(&a[0]);

Is roughly (but not exactly) the same as doing:

change_array(a);

Now, knowing how arrays are passed in C, you should be able to deduce that, indeed, two dimensional arrays, when passed as parameters, actually contain on the access to their first coordinate a pointer to where the first element of the second coordinate is. So, when you do array[0] you're passing a pointer to the first element of the array under the first coordinate. and then when you do *(array[0]) you're actually accessing the first element of the second coordinate.

Here it is also important to add then that; since array variables decay into pointers, then all arrays in C are passed by reference, because you are passing a pointer. So all function calls that modify an array passed to them will do actual modifications.

Knowing this, then your function should be:

void change_array(int *array)
{
    array[0] = -1;
    array[1] = -1;
}

And then you can perform a call such as:

change_array(a[0]);

In order to modify the elements of the first array of a.

Now, as good practice in C, and in order to avoid segmentation faults, one would pass to the function along with the pointer, an integer saying the size of the array:

void change_array(int *array, int size);

And then always perform the access checking this bound.

cgledezma
  • 612
  • 6
  • 11
  • 2
    "an array variable is actually a pointer to the memory address that contains the first element of the array" - no, it isn't. An array is an array, a pointer is a pointer. An array may decay into a pointer to its first element in some cases, but it's not always true. –  Jun 24 '13 at 12:16
  • @H2CO3 Actually in C it is always true. And it is a normal error to think it isn't, which usually degenerates in errors when people think that the array is being passed by value, when actually the pass by value happens only on the value of the pointer. – cgledezma Jun 24 '13 at 12:21
  • 2
    Don't teach me C. Arrays aren't "always" pointers. In fact, they **never** are. What if you use `sizeof()`? What if you take the address of an array and a pointer and perform pointer arithmetic on those? In both cases, you'll obtain different results because ***arrays are NOT FRIGGIN' POINTERS!*** –  Jun 24 '13 at 12:22
  • @H2CO3 excuse my misuse of terms. The correct proposition to use here is "since in C pointer arithmetic and array indexing are equivalent" as stated here: http://c-faq.com/~scs/cgi-bin/faqcat.cgi?sec=aryptr, question 6.3. No need to pist off > – cgledezma Jun 24 '13 at 12:27
  • EDIT: proper terminology used, thanks to @H2CO3 for an enriching conversation.. – cgledezma Jun 25 '13 at 06:39