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

  • Thread starter Thread starter Crystal037
  • Start date Start date
  • Tags Tags
    Pointers
AI Thread Summary
The discussion focuses on the challenges of creating a 2D array using pointers in C, particularly regarding memory allocation and pointer manipulation. The original code fails to allocate sufficient memory for the 2D array, as it mistakenly allocates only for pointers rather than for the actual integer values. It is clarified that in C, function parameters are passed by value, which means changes to the pointer within the function do not affect the original pointer in the calling function unless it is returned. Suggestions include modifying the `make` function to return the allocated pointer and using more descriptive variable names for clarity. The importance of proper memory allocation and understanding pointer behavior in C is emphasized for successful implementation.
Crystal037
Messages
167
Reaction score
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
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:
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
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
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
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:
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:
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:
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));.
 

Similar threads

Replies
3
Views
1K
Replies
3
Views
1K
Replies
4
Views
1K
Replies
1
Views
10K
Replies
2
Views
2K
Replies
12
Views
2K
Replies
3
Views
1K
Replies
3
Views
1K
Replies
2
Views
2K
Back
Top