Creating 2D Arrays with Pointers in C | Efficient Memory Allocation

  • Comp Sci
  • Thread starter Crystal037
  • Start date
  • Tags
    Pointers
In summary, your make() function allocates an array of y pointers, but then the loop following it initialized z members of that array to point to some other pointers.
  • #1
Crystal037
167
7
Homework Statement
Returning 2d array from a function by passing pointer to that array.
Relevant Equations
None
C:
#include <stdio.h>
#include<stdlib.h>
int** make(int**x,int y,int z);
void read(int **x,int y,int z);
int main()
{
    int ** r;
    int a,b;
    scanf("%d %d",&a,&b);
    /*r=(int**)malloc(sizeof(int*)*a);
    for(int i=0;i<b;i++){
        r[i]=(int*)malloc(sizeof(int**));
    }*/
    make(r,a,b);
//r=make(r,a,b);
    /*for(int i=0;i<a;i++)
    for(int j=0;j<b;j++)
    scanf("%d",&r[i][j]);
    for(int i=0;i<a;i++)
    for(int j=0;j<b;j++)
    printf("%d",r[i][j]);*/
    read(r,a,b);
    
}
int** make(int**x,int y,int z){
    x=(int**)malloc(sizeof(int*)*y);
    for(int i=0;i<z;i++){
        x[i]=(int*)malloc(sizeof(int**));
    }
    return x;
}
void read(int **x,int y,int z){
    for(int i=0;i<y;i++)
    for(int j=0;j<z;j++)
    scanf("%d",&x[i][j]);
    for(int i=0;i<y;i++)
    for(int j=0;j<z;j++)
    printf("%d",x[i][j]);
}
In the program given above, I have written a function that takes pointer to a pointer as argument and then allocates memory to it to make a 2 d array with the given rows and columns given to it in argument, Since I have used pointer changing its value in function should change it everywhere but then after calling function make(r,a,b) there's no memory allocated to it by make function. Why do I have to equate make function to the pointer to pointer taken in main function. Here I have taken r as a pointer to pointer and put it in the argument of make function but after calling make function no change happens to r. For the expected result, why do I have to equate r to make function
 
Last edited by a moderator:
Physics news on Phys.org
  • #2
Your make() function allocates an array of y pointers, but then the loop following it initialized z members of that array to point to some other pointers. If z is less than y, the first array will not be fully initialized, and if z is greater than y, you'll overrun the memory allocated in line 26.

Your line 18 reads memory that has not been allocated. r[ i ] is a pointer (only potentially) allocated at line 28, but that memory is uninitialized. &r[ i ][ j ] is the same as r[ i ]+j, but r was the result of an allocation of a single pointer, so any nonzero value for j points to unallocated memory.
 
Last edited:
  • #3
Crystal037 said:
Homework Statement:: Returning 2d array from a function by passing pointer to that array.
Relevant Equations:: None

C:
#include <stdio.h>
#include<stdlib.h>
int** make(int**x,int y,int z);
void read(int **x,int y,int z);
int main()
{
    int ** r;
    int a,b;
    scanf("%d %d",&a,&b);
    /*r=(int**)malloc(sizeof(int*)*a);
    for(int i=0;i<b;i++){
        r[i]=(int*)malloc(sizeof(int**));
    }*/
    make(r,a,b);
//r=make(r,a,b);
    /*for(int i=0;i<a;i++)
    for(int j=0;j<b;j++)
    scanf("%d",&r[i][j]);
    for(int i=0;i<a;i++)
    for(int j=0;j<b;j++)
    printf("%d",r[i][j]);*/
    read(r,a,b);
 
}
int** make(int**x,int y,int z){
    x=(int**)malloc(sizeof(int*)*y);
    for(int i=0;i<z;i++){
        x[i]=(int*)malloc(sizeof(int**));
    }
    return x;
}
void read(int **x,int y,int z){
    for(int i=0;i<y;i++)
    for(int j=0;j<z;j++)
    scanf("%d",&x[i][j]);
    for(int i=0;i<y;i++)
    for(int j=0;j<z;j++)
    printf("%d",x[i][j]);
}
In the program given above, I have written a function that takes pointer to a pointer as argument and then allocates memory to it to make a 2 d array with the given rows and columns given to it in argument, Since I have used pointer changing its value in function should change it everywhere but then after calling function make(r,a,b) there's no memory allocated to it by make function. Why do I have to equate make function to the pointer to pointer taken in main function.
Since your make() function modifies its pointer argument, you don't need it to return a value. Having it do so is confusing to both readers and to you.
Also, your program is not working because you aren't allocating enough memory in your first call to malloc() -- it is allocating only 4 bytes of memory. If you want your two-dimensional array to be contiguous memory, the call to malloc() should look like x = (int**)malloc(y * z * sizeof(int));. If you do things like this, you don't need to call malloc() again in your make() function.
On the other hand, your code seems to be set up to allocate space for pointers to each row of the two-d array, and then call malloc() inside a loop to allocate space for as many elements of the row as there are columns.

Your variable names are awful. You should not be using x, y, z, etc. Instead, use variable names that suggest what you are using them for. None of the variable names should be single letters, with the exception of loop control variables in for loops.

Crystal037 said:
Here I have taken r as a pointer to pointer and put it in the argument of make function but after calling make function no change happens to r. For the expected result, why do I have to equate r to make function
The explanation for why this happens would take longer than I want to write. Instead, I recommend that you change both of your functions, make() and read(), so that they return a value rather than modify their passed arguments.

Here's a sort of outline of how you might proceed where make() allocates enough memory for a two-dimensional array in a single call to malloc().

C:
int** make(int rows, int cols);

int main()
{
   <snip>
    int ** array = NULL;
    <snip>
    array = make(rows, cols);
    <snip>
}

int ** make(int numRows, int numCols)
{
    int** ppArr = (int**)malloc(numRows * numCols * sizeof(int));
    // Fill in array elements
    return ppArr;
}
 
  • Informative
  • Like
Likes Klystron, FactChecker and berkeman
  • #4
Mark44 said:
Your variable names are awful. You should not be using x, y, z, etc. Instead, use variable names that suggest what you are using them for.
I couldn't agree more.
Mark44 said:
None of the variable names should be single letters, with the exception of loop control variables in for loops.
Even in this case, I recommend something like rowIndex and colIndex rather than i and j. The i,j might be acceptable only because they are traditionally used in mathematics for the row and column indices, but I think it's better to spell them out.

Also,
1) Code comments are good and free.
2) Please indent and sub-indent nested loops
3) The original code never allocated anything of size int. All the mallocs are for pointer sizes. That should raise a red flag.
 
  • Like
Likes Mark44
  • #5
FactChecker said:
The i,j might be acceptable only because they are traditionally used in mathematics for the row and column indices
They're also traditionally used in programming, but I agree that in this case rowIndex and colIndex or similar would be better choices.
 
  • Like
Likes berkeman and FactChecker
  • #6
Halc said:
Your make() function allocates an array of y pointers, but then the loop following it initialized z members of that array to point to some other pointers. If z is less than y, the first array will not be fully initialized, and if z is greater than y, you'll overrun the memory allocated in line 26.

Your line 18 reads memory that has not been allocated. r[ i ] is a pointer (only potentially) allocated at line 28, but that memory is uninitialized. &r[ i ][ j ] is the same as r[ i ]+j, but r was the result of an allocation of a single pointer, so any nonzero value for j points to unallocated memory.
Ok y should have been on the loop but why r pointer is only potentially allocated, I can't understand why r is only potentially allocated.
 
Last edited:
  • #7
C:
#include <stdio.h>
#include<stdlib.h>
int** make(int y,int z);
void read(int **x,int y,int z);
int main()
{
    int ** r;
    int a,b;
    scanf("%d %d",&a,&b);
    /*r=(int**)malloc(sizeof(int*)*a);
    for(int i=0;i<b;i++){
        r[I]=(int*)malloc(sizeof(int**));
    }*/
    r=make(a,b);
//r=make(r,a,b);
   /* for(int i=0;i<a;i++)
    for(int j=0;j<b;j++)
    scanf("%d",&r[ i][j]);
    for(int i=0;i<a;i++)
    for(int j=0;j<b;j++)
    printf("%d",r[ i][j]);*/
    read(r,a,b);
   
}
int** make(int y,int z){
    int **x;
    x=(int**)malloc(sizeof(int*)*y);
    for(int i=0;i<y;i++){
        x[ i]=(int*)malloc(z*sizeof(int**));
    }
    return x;
}
void read(int **x,int y,int z){
   
    printf("%d %d",y,z);
    for(int i=0;i<y;i++)
    for(int j=0;j<z;j++)
    scanf("%d",&x[ i][j]);
    for(int i=0;i<y;i++)
    for(int j=0;j<z;j++)
    printf("%d",x[ i][j]);
}
i have made y in the loop and inside it each of pointer is pointing to z columns. I am getting the output but I don't understand why isn't it coming when I pass the pointer as an argument
 
Last edited by a moderator:
  • #8
Crystal037 said:
Ok y should have been on the loop but why r pointer is only potentially allocated, I can't understand why j points to unallocated memory since r is a pointer to pointer

@Crystal037, here is the code you wrote from post #1:
C:
int** make(int**x,int y,int z){
    x=(int**)malloc(sizeof(int*)*y);
    for(int i=0;i<z;i++){
        x[i]=(int*)malloc(sizeof(int**));
    }
    return x;
}

The first line in the body of make() above allocates enough memory for a single pointer, which is probably 4 bytes. Presumably, that line of code should have allocated enough memory for y pointers to type int, where y is the number of rows in your two-dimensional array.

Inside the for loop, each x[ i] should be allocated enough memory for an array of z ints. x[0] should hold the address of the index-0 row, x[1] should hold the address of the index-1 row, and so on up to and including x[y-1] with the address of index y-1 row., As has already been said, you really need better names for these variables than x, y, and z.

What I'm saying about your calls to malloc() has already been stated:
FactChecker said:
3) The original code never allocated anything of size int. All the mallocs are for pointer sizes. That should raise a red flag.
 
Last edited:
  • #9
Mark44 said:
@Crystal037, here is the code you wrote from post #1:
C:
int** make(int**x,int y,int z){
    x=(int**)malloc(sizeof(int*)*y);
    for(int i=0;i<z;i++){
        x[ i]=(int*)malloc(sizeof(int**));
    }
    return x;
}

The first line in the body of make() above allocates enough memory for a single pointer, which is probably 4 bytes. Presumably, that line of code should have allocated enough memory for y pointers to type int, where y is the number of rows in your two-dimensional array.

Inside the for loop, each x[ i] should be allocated enough memory for an array of z ints. x[0] should hold the address of the index-0 row, x[1] should hold the address of the index-1 row, and so on up to and including x[y-1] with the address of index y-1 row., As has already been said, you really need better names for these variables than x, y, and z.

What I'm saying about your calls to malloc() has already been stated:
I have corrected that in my next post and multiplied the size by z. But I am unable to understand why isn't it working when passing the pointer as an argument
 
Last edited by a moderator:
  • #10
Crystal037 said:
But I am unable to understand why isn't it working when passing the pointer as an argument
Because C function parameters are strictly "pass by value" rather than "pass by reference." This means that C can't change the value of any parameters. In particular, in this function header int** make(int**x,int y,int z), if you call it like this -- make(r,a,b);, whatever value r has before the call won't change, no matter what happens with x in the body of the make() function.
 
  • #11
Crystal037 said:
C:
#include <stdio.h>
#include<stdlib.h>
int** make(int y,int z);
void read(int **x,int y,int z);
int main()
{
    int ** r;
    int a,b;
    scanf("%d %d",&a,&b);
    /*r=(int**)malloc(sizeof(int*)*a);
    for(int i=0;i<b;i++){
        r[ i]=(int*)malloc(sizeof(int**));
    }*/
    r=make(a,b);
//r=make(r,a,b);
   /* for(int i=0;i<a;i++)
    for(int j=0;j<b;j++)
    scanf("%d",&r[ i][j]);
    for(int i=0;i<a;i++)
    for(int j=0;j<b;j++)
    printf("%d",r[ i][j]);*/
    read(r,a,b);
 
}
int** make(int y,int z){
    int **x;
    x=(int**)malloc(sizeof(int*)*y);
    for(int i=0;i<y;i++){
        x[ i]=(int*)malloc(z*sizeof(int**));
    }
    return x;
}
void read(int **x,int y,int z){
 
    printf("%d %d",y,z);
    for(int i=0;i<y;i++)
    for(int j=0;j<z;j++)
    scanf("%d",&x[ i][j]);
    for(int i=0;i<y;i++)
    for(int j=0;j<z;j++)
    printf("%d",x[ i][j]);
}
i have made y in the loop and inside it each of pointer is pointing to z columns. I am getting the output but I don't understand why isn't it coming when I pass the pointer as an argument
1. Please use meaningful variable names. y and z for the number of rows and columns are terrible names. Please change them. You should not be using any single-letter variable names. If you want us to help, at least make the effort to make your code easier to understand.
2. Please use code tags, which preserve indentation and make your code easier to read. At the top of this forum section there's a short post on how to use BBCode code tags. I have put code tags around your code in post #1 and in the post I quoted.
3. Because your code doesn't have indentation your read() function is extremely difficult to read.
4. Add comments to your code.
 
Last edited:
  • #12
Note that the only reason to use C is because it can be fast. Implementing a 2-D array of integers as a pointer to a 1-D array of pointers to n 1-D arrays of pointers to integer values is slow requiring up to 4 memory accesses to retrieve a value.

Instead, typically this would be implemented as a pointer to a 1-D array of values indexed by e.g. (row_index * column_count + column_index) * size: typically this is optimised by the compiler so that the pointer is held in a register and only 1 memory access is required to retrieve a value.
 
Last edited:
  • Informative
Likes berkeman
  • #13
Mark44 said:
Because C function parameters are strictly "pass by value" rather than "pass by reference." This means that C can't change the value of any parameters. In particular, in this function header int** make(int**x,int y,int z), if you call it like this -- make(r,a,b);, whatever value r has before the call won't change, no matter what happens with x in the body of the make() function.
Using pointers isn't it pass by reference
 
  • #14
Crystal037 said:
Using pointers isn't it pass by reference
The pointer value is still passed by value. The called function can not change the pointer value up in the calling program. So the programmer can mimic pass-by-reference using a pointer that is passed-by-value. But the C language itself is still doing pass-by-value.
 
  • #15
Crystal037 said:
Using pointers isn't it pass by reference
No. All parameters in C are pass by value.
A pointer is usually (but not always) a variable that can hold an address. A pointer parameter in a function doesn't change its value, but what can change is the value at that address.
 
Last edited:
  • #16
@Crystal037, here's an example of what I'm talking about, regarding pointers being passed by value.
C:
#include <stdio.h>
void increment(int * arg, int n);
int main()
{
   int num = 2;
   int * ptrNum = &num;
   increment(ptrNum, 3);
   printf("num: %d", num);
}

void increment(int * pNum, int n)
{
   *pNum += n;
}
Before the call to increment(), the variable ptrNum contains the address of the variable num. The expression *ptrNum has a value of 2. Inside increment(), the address value in ptrNum is passed to the formal parameter pNum. Because pNum also points to the memory represented by num, the assignment statement *pNum += n; causes num to be reset to 5.

When increment() returns to main, the updated value of num is displayed. Notice that ptrNum does not change its value. It still contains the unaltered address of num.

All parameters in C are passed by value, not by reference.
 
  • #17
Mark44 said:
@Crystal037, here's an example of what I'm talking about, regarding pointers being passed by value.
C:
#include <stdio.h>
void increment(int * arg, int n);
int main()
{
   int num = 2;
   int * ptrNum = &num;
   increment(ptrNum, 3);
   printf("num: %d", num);
}

void increment(int * pNum, int n)
{
   *pNum += n;
}
Before the call to increment(), the variable ptrNum contains the address of the variable num. The expression *ptrNum has a value of 2. Inside increment(), the address value in ptrNum is passed to the formal parameter pNum. Because pNum also points to the memory represented by num, the assignment statement *pNum += n; causes num to be reset to 5.

When increment() returns to main, the updated value of num is displayed. Notice that ptrNum does not change its value. It still contains the unaltered address of num.

All parameters in C are passed by value, not by reference.
Ok so I have just declared a pointer which doesn't point to anything( points to garbage value) and even after function call it will point to that same garbage value. Ok
Also I wanted to ask in my malloc statement if I use sizeof(int) instead of sizeof(int**) inside the loop, what is the difference and I think I shouldn't use int**. It's hard to understand the difference because both of them is working.
 
  • #18
Crystal037 said:
Ok so I have just declared a pointer which doesn't point to anything( points to garbage value) and even after function call it will point to that same garbage value. Ok
The pointer should be initialized so that it points to valid memory. In my example, ptrNum was initialized with the address of an int named num. If you don't initialize the pointer, but later try to use it to assign values to that memory, you will probably get an access violation error at run time.
Crystal037 said:
Also I wanted to ask in my malloc statement if I use sizeof(int) instead of sizeof(int**) inside the loop, what is the difference and I think I shouldn't use int**. It's hard to understand the difference because both of them is working.
If your compiler is generating 32-bit code, sizeof(int) and sizeof(int*) likely will both evaluate to 4 bytes. When you use malloc(), you want it to allocate enough memory to hold the right number of array elements. If one row of your array needs to hold rowSize int values, malloc() should be called something like this: rowPtr = (int*)malloc( rowSize * sizeof(int));.
 

FAQ: Creating 2D Arrays with Pointers in C | Efficient Memory Allocation

How do you create a 2D array using pointers in C?

To create a 2D array using pointers in C, you first need to allocate memory for the array using the malloc() function. Then, you can use nested for loops to assign values to each element of the array.

What is the advantage of using pointers for 2D arrays in C?

Using pointers for 2D arrays in C allows for more efficient memory allocation. Instead of allocating memory for each row of the array separately, pointers allow for a single block of memory to be allocated for the entire array.

How do you access elements of a 2D array using pointers in C?

To access elements of a 2D array using pointers in C, you can use the * operator to dereference the pointer and access the value stored at a specific memory address. You can also use pointer arithmetic to navigate through the array.

Can you dynamically resize a 2D array created with pointers in C?

Yes, you can dynamically resize a 2D array created with pointers in C by using the realloc() function. This allows for more flexibility in managing memory and can be useful when working with large or constantly changing arrays.

How do you free the memory allocated for a 2D array created with pointers in C?

To free the memory allocated for a 2D array created with pointers in C, you can use the free() function. It is important to remember to free the memory once you are finished using the array to avoid memory leaks.

Similar threads

Replies
3
Views
906
Replies
3
Views
973
Replies
4
Views
1K
Replies
1
Views
9K
Replies
2
Views
2K
Replies
12
Views
1K
Replies
3
Views
968
Replies
3
Views
1K
Replies
2
Views
1K
Back
Top