0

Please, I am new to C programming, please help me this.There is some error in the following code? Basically, how do we work with pointers to 2-D arrays...

#include<stdio.h>
#include<stdlib.h>

int* max5(int** p,int r,int c)
{

/* have to design a function which takes as argument pointer to a 2-D array and returns a pointer to an array which contains the 5 highest entries*/

}

int main()

{

    int rows,coloumns;
    printf("Enter the number of rows and coloumns separated by a space\n");
    scanf("%d %d",&rows,&coloumns);
    int **ptr;

    ptr=(int**)malloc(sizeof(int*)*rows);
    int i,j;
    for(i=0;i<rows;i++)
    {
        for(j=0;j<coloumns;j++)
        {
            scanf("%d",*(ptr+i)+j);
            printf("%d\n",*(*(ptr+i)+j));
        }
    }
    free(ptr);
    return 0;
}
MOHAMED
  • 41,599
  • 58
  • 163
  • 268

4 Answers4

1

In C, a 2D array is simply a regular array with different indexing. So, you have two options:

  1. Allocate an array and index it as: array[I*n+j]
  2. Allocate an array of arrays (which is what you seem to be trying to do)

The second you can achieve in the way shown in the related post: allocate matrix in C.

Community
  • 1
  • 1
Igor Ševo
  • 5,459
  • 3
  • 35
  • 80
1

Dynamically allocating a 2D array can be done in several ways.

Assuming you have a C99 compiler or C2011 compiler that supports variable-length arrays, this is dead easy:

int rows, columns;
...
scanf("%d %d",&rows,&columns);
int array[rows][columns];

array is a variable-length array (VLA), meaning its dimensions are established at run time. To pass this to another function, the prototype would simply be

int *max5( int rows, int columns, int arr[rows][columns] ) { ... }

which can also be written as

int *max5( int rows, int columns, int (*arr)[columns] ) { ... }

I'll get into that second form below. Note that both rows and columns must be declared before you use them in the VLA declaraion.

You would call it as

int *maxarr = max5( rows, columns, array );

The advantage of this approach is that a) it's easy, and b) the memory for array is allocated contiguously (i.e., as a single, continuous block of memory) and released as soon as you exit it's enclosing scope; there's no need to manually free it. The disadvantage of this approach is that rows and columns can't be arbitrarily large, you can't initialize the array using regular initializer syntax (i.e., you can't write int array[rows][columns] = { ... };), and you can't define VLAs at file scope (i.e., as a global variable). There's also the problem that VLA support has always been a little spotty, and the 2011 standard now makes them optional, so it's not guaranteed they'll be supported everywhere.

If rows or columns is very large, or if for some other reason you want to allocate this memory from the heap, it's just slightly more complicated:

int rows, columns;
...
scanf("%d %d", rows, columns);
int (*array)[columns] = malloc( sizeof *array * rows );

In this version, array is a pointer to an N-element array of int (where N == columns), and we malloc space to hold M such arrays (where M == rows). Again, this takes advantage of VLA syntax, but instead of allocating the memory from the stack frame, we allocate it from the heap. Like in the first method, the memory is allocated contiguously. You would index into it as you would any other array:

array[i][j] = x;

When you're done with the array, you would free it as

free( array );

The prototype for max5 would be

int *max5( int rows, int columns, int (*arr)[columns] ) { ... }

Look familiar? That's the second version we used earlier when we were passing a 2D VLA.

Except when it is the operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted to an expression of type "pointer to T", and the value of the expression will be the address of the first element in the array.

In the first case where we declared array as a 2D VLA, when you call

int *maxarr = max5( rows, columns, array );

the expression array is converted from type "rows-element array of columns-element array of int" to "pointer to columns-element array of int", so what max5 receives is a pointer value, not an array. It just so happens that in the context of a function parameter declaration, T a[N] and T a[] are both interpreted as T *a; IOW, all three declare a as a pointer to T.

Thus, both

int *max5( int rows, int columns, int arr[rows][columns] ) { ... }

and

int *max5( int rows, int columns, int (*arr)[columns] ) { ... }

declare arr as a pointer to an array, not a 2D array. Similarly, both return a simple int *, as opposed to an array of int. The reason for this is simple; if you return a subarray using

return arr[i];

the expression arr[i] will have type "columns-element array of int"; by the rule above, that expression will be converted to type int *. C doesn't allow you to return array objects as arrays, nor can an array expression be the target of an assignment.

Now, what if you're working with a C89 compiler, or a C2011 compiler that doesn't support VLAs? In this case, you'd need to allocate the memory in a piecemeal fashion, like so:

int rows, columns;
int **array;
...
scanf("%d %d", rows, columns);
array = malloc( sizeof *array * rows );
if ( array )
{
   int i;
   for ( i = 0; i < rows; i++ )
   {
     array[i] = malloc( sizeof *array[i] * columns );
   }
}

Your max5 prototype would now be

int *max5( int rows, int columns, int **arr );

You're not dealing with arrays or pointers to arrays anymore; you're dealing with a double pointer, which is not the same thing as a pointer to a 2D array. You can still subscript it as though it were a 2D array

arr[i][j] = x;

but it is not an array object in and of itself.

The most important thing to realize is that the memory is not guaranteed to be contiguous; rows may not be adjacent in memory, so you don't want to try to walk down rows using a pointer, like so:

int (*p)[columns] = arr;
while ( some_condition )
  p++; // sets p to point to the next row

For an array that was allocated piecemeal, that sort of algorithm won't work.

Also, since the memory was allocated piecemeal, it needs to be freed piecemeal:

for ( i = 0; i < rows; i++ )
  free(array[i]);
free( array );

Hope that helps.

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • @haccks: It's the day before Christmas and I don't want to work on what I'm supposed to be working on. Besides, I've written a variation of this answer several times already, so it didn't take too long to write. – John Bode Dec 24 '13 at 16:32
  • Oh! Thank God for clarifying. You know I was thinking about your patience to write such a long answer. – haccks Dec 24 '13 at 16:34
0

this code fix your issue :

#include<stdio.h>
#include<stdlib.h>

int* max5(int** p,int r,int c)
{

/* have to design a function which takes as argument pointer to a 2-D array and returns a pointer to an array which contains the 5 highest entries*/

}

int main()

{

    int rows,coloumns;
    printf("Enter the number of rows and coloumns separated by a space\n");
    scanf("%d %d",&rows,&coloumns);
    int **ptr;
    int i,j;

    ptr=(int**)malloc(sizeof(int*)*rows);
    for(j=0;j<coloumns;j++)
    {
        *(ptr+j) = (int *)malloc(sizeof(int)*coloumns);
    }

    for(i=0;i<rows;i++)
    {
        for(j=0;j<coloumns;j++)
        {
            scanf("%d",*(ptr+i)+j);
           //*(*(ptr+j)+i) = i+j;
            printf("%d\n",*(*(ptr+i)+j));
        }
    }
    free(ptr);
    return 0;
}

in fact you forget to allocate memory for columns !

Anis_Stack
  • 3,306
  • 4
  • 30
  • 53
-1

You can allocate a 2D array in C by defining the type correctly:

int (*ptr)[coloumns] = calloc(sizeof(int) , rows * coloumns);

Then you can address it via ptr[i][j]. If your array does need to persist beyond the scope that allocated it and is fairly small then you can also allocate it on the stack

int ptr[rows][coloumns];
memset(ptr, 0, sizeof(ptr));

What you are trying to allocate is an array of pointers int (*)[coloumns] is not the same type as int ** although they can be both addressed through the use of the indexing operator [i][j]. The latter is a far more complicated data structure and is prone to more errors. I would advise against it unless you need variable row sizes.

Sergey L.
  • 21,822
  • 5
  • 49
  • 75