6

I have defined array in the following formats, but apparently the program works fine only in CASE B.

CASE A:

int **M1;
M1 = (int **)malloc(m * sizeof(int *));
for (int i=0; i<m; i++){
    M1[i] = (int *)malloc(d * sizeof(int));
} 

CASE B:

int (*M1)[m] = malloc(sizeof(int[m][d]));

I am getting a segmentation error in CASE A. What could be the reason?

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
re3el
  • 735
  • 2
  • 12
  • 28
  • 2
    The A code works perfectly for me. What is your environment ? – blue112 Aug 13 '15 at 08:12
  • 1
    Where exactly do you get the segmentation error ? – Jabberwocky Aug 13 '15 at 08:18
  • [Please don't cast the result of `malloc()`](http://stackoverflow.com/a/605858/3233393). – Quentin Aug 13 '15 at 08:32
  • 5
    These are two different ways to lay out an array in memory, they each have pros and cons. The rest of your program either has a bug or is expecting only one particuar layout. You will need to show the code that causes a segfault (preferably a [MCVE](http://stackoverflow.com/help/mcve)) – M.M Aug 13 '15 at 08:33
  • 1
    Case B should be `int (*M1)[d]` , if `m` is the row count and `d` the column count as it is in Case A – M.M Aug 13 '15 at 08:35
  • How do you define `d`? Is it initialized? – szx Aug 13 '15 at 10:56
  • @MattMcNabb Otherwise I'd say it's pretty unclear what's the question. – edmz Aug 13 '15 at 11:54
  • @black me too, no idea why it has upvotes – M.M Aug 13 '15 at 12:36

3 Answers3

3

The following code compiles without error or warning in gcc compiler,

    int **M1,i;
    M1 = (int **)malloc(5 * sizeof(int *));
    for (i=0;i<5;i++){
        M1[i] = (int *)malloc(2 * sizeof(int));
    }
    M1[0][0]=111;
    M1[0][1]=222;
    printf("\n%d %d",M1[0][0],M1[0][1]);

Gives 111 222 as output.

The problem might be some where else in your code.

Deepu
  • 7,592
  • 4
  • 25
  • 47
1

CASE A;

M1 is a pointer to a pointer. You are allocating memory for a 2D array of type int and you can store m*d values in the array.

M1[0][0] to M[m-1][d-1] are valid access for this array in order to get value of type int and anything other than this will lead to undefined behavior.

Case B:

M1 is a pointer to an array of type int which has m elements.

In Case A, the segmentation fault most likely looks like is because of array out of bound access

Gopi
  • 19,784
  • 4
  • 24
  • 36
  • No, he allocates memory for `m` number of `int*`, which is perfectly fine. It's also perfectly fine to access `M1[0]`, you just get an `int*`, not an `int`. – Jite Aug 13 '15 at 08:21
  • @Jite I think you misunderstood my answer. Yeah `int *` is fine but I meant the valid int values accessible are `M1[0][0]` to `M1[4][4]` – Gopi Aug 13 '15 at 08:22
  • 1
    Where do you get `[4][4]` from? We don't know the value of `m` nor `d`? – Jite Aug 13 '15 at 08:23
  • @Jite Very sorry about it. Yeah you are right in that regard – Gopi Aug 13 '15 at 08:25
  • `M1[m-1]` is the upper end for retreiving `int*` while in this case `M1[m-1][d-1]` is the upper end for retreiving `int` values (from the second malloc). – Jite Aug 13 '15 at 08:27
  • @Jite I overlooked `d` :) somehow `m*m` was picked :) – Gopi Aug 13 '15 at 08:29
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/86860/discussion-between-gopi-and-jite). – Gopi Aug 13 '15 at 08:32
1

A pointer and an array are different things. A pointer to a type holds the address where a variable of declared type is allocated.

An array of a type is a memory space where variables of the declared type are contiguously stored.

Now you're declaration. There is no ambiguity (even if is not so evident) in C declarations. In case A you have declared:

int **M1;

This is not a 2d array, not even a monodimensional one. You are declaring a variable, M!, as a pointer to another pointer to an int. In plane word M1 will hold the address of another variable that in turn holds the address of where in memory is stored an integer. Now executing:

M1 = (int **)malloc(m * sizeof(int *));

You assign to M1 the address of a memory area that can store up to m contiguous integer pointers, an access to memory pointed by M1, and successive locations acts as an array access (but is not). This is more or less equivalent to the static declaration:

int *[m];    //an array of pointers to int

Then assigning each element of this pseudo-array a memory storage for d contiguous integers:

for (int i=0; i<m; i++)
{
    M1[i] = (int *)malloc(d * sizeof(int));
} 

you now have room to store d consecutive integers that begins at addresses saved in M1[i] for i=0->d-1.

What happens when you try to access a value using subscripts: M1[a][b]? The compiler retrieve the address pointed by M1 and using the first subscript (a), retrieves the contents pointed by the address in the atht position of the pointers array. This points to the first integer of the subspace that we have allocated to hold d consecutive int's. This is indeed a monodimensional array of int. Applying the second subscript to it the compiler finally retrieve the required integer. Cose to a bidimensional array addressing, but no banana! It isn't a bidimensional array of int's. :-)

In case B you are declaring a pointer to a monodimensional array of int, to which you are assigning enough space to hold your bidimensional array. Because in C doesn't exist the concept of multidimensional array, but a basic principle of array of array of array.... etc. (ad-libitum), the declaration:

int (*M1)[m] = malloc(sizeof(int[m][d]));

as a pointer to modimensiional array with space allocated for an array of [m][d] elements made the trick.

But of course both the solutions are wrong!

You are using side effects to obtain what you want, but using surrogates of what you declared to need: a bidimensional array of int's.

The correct solution is to define a pointer to a bidimensional array of integers where only the first subscript is required:

int (*M1)[][m];    //declare a pointer to a real array

M1 = malloc(m * d * sizeof(int));    //Allocate room for bidimensional array

for (int i=0; i<m; i++)
    for (int j=0; j<d; j++)
    {
        (*M1)[i][j] = (i*100)+j;    //Fill elements with something using bidimensional subscripting
    }
for (int i=0; i<m; i++)
    for (int j=0; j<d; j++)
    {
        printf("[%d][%d]=%d\n", i, j, (*M1)[i][j]);    //Check back data...
    }

Now have a look to what happens if you go out bounds in the 3 cases. In case A if you get out of bounds with the first subscript you'll collect a wrong pointer that will cause immediately a memory fault, in case B you will have memory fault only if you go out of process addressable memory, the same for the correct solution. This should answer to your question.

Last, because we are talking of misunderstandigs about arrays and pointers, don't misinterpret the standard ISO 9899:2011§6.7.6.3/7 that says:

A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’, where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.

It simply states that arrays will be passed only by reference (automatically adjusted by compiler), never by value (that is legal for structs ie). You are not requested to supply any qualified pointer instead of an array in function call or the program will crash (A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’ - means that will be adjusted by compiler, not by you). Cases as char *[] and char ** works for the reason reported above, not because it is legal to exchange them!

Frankie_C
  • 4,764
  • 1
  • 13
  • 30