0

I have just encountered a case in this C program:

void row_del(int**& a, int& row, int col, int k) {
    for (int j = 0; j < col; j++) {
        for (int i = k; i < row - 1; i++) {
            a[i][j] = a[i + 1][j];
        }
    }
    row--;
    a = (int**)realloc(a, row * sizeof(int*));
}

Is this necessary for a reference to int **a when using realloc in this function ?

4
  • 9
    References (like in int& row) do not exists in C, only in C++. So this is not a C program. And in C++ it is not recommended to use malloc and friends at all. But anyway a reference is indeed needed to update the pointer on the callers side. Commented Oct 26 at 6:04
  • 1
    ... or else you'd need a to be an int*** and do *a = realloc(*a, ...); Commented Oct 26 at 6:09
  • 2
    It would probably be better to return the reallocated pointer to the caller, so they can check whether the reallocation failed — especially as the code overwrites the value in a, so if the reallocation fails, the previously allocated data is also lost unless there was a spare pointer to it somewhere. The idiom ptr = realloc(ptr, new_size); is dangerous because it loses the previous data on memory failure. This is a variant on the theme. All of that is independent of the fact that the & in the function signature means the code is not C code. Commented Oct 26 at 6:45
  • 1
    Note, realloc does not return void, it returns a pointer. Ask yourself why. Commented Oct 26 at 7:11

3 Answers 3

5

The code is not valid in C, it only compiles in C++. References such as int**& a and int& row are not supported in C as of C23.

For this function to update the matrix address and size in the caller's scope requires explicit pointers in C. There are multiple ways to do this:

  • Both a and row passed as pointers:
    void row_del(int ***p_a, int *p_row, int col, int k);
    
  • Alternately, one can use the return value to avoid a triple pointer for a:
    int **row_del(int **a, int *p_row, int col, int k);
    
  • Another possibility: encapsulate the matrix data pointer and dimensions in a structure:
    typedef struct Matrix {
        int **data;
        int rows, cols;
    } Matrix;
    void row_del(Matrix *m, int k);
    

Note these additional problems:

  • copying the row contents using nested loops in columnwise order is very inefficient. Swapping the column- and rowwise loops would make the code more efficient.
  • copying the row contents is not even necessary: storing a[i] = a[i + 1] in a loop would suffice for the purpose of removing the target row k.
  • the removed row is not freed by this function, resulting in a memory leak.

Regarding your question:

Is this necessary for a reference to int **a when using realloc in this function?

realloc takes a pointer to memory allocated by malloc, calloc, realloc, etc. and a new block size. It returns a pointer to the reallocated block, which may be a new pointer, a pointer identical to the argument, or a null pointer if the request cannot be fulfilled. Your code works but should test for potential reallocation failure, which is possible even for a lesser size.

Here is a modified version for C using the encapsulation in a struct:

typedef struct Matrix {
    int **data;
    int rows, cols;
} Matrix;

void row_del(Matrix *m, int k) {
    if (k < 0 || k >= m->rows) return; // could be an assert

    free(m->data[k]);
    for (int i = k + 1; i < m->rows; i++) {
        m->data[i - 1] = m->data[i];
    }
    m->rows -= 1;
    if (m->rows) {
        // try and reallocate the array. cast is needed for C++ only
        int **new_data = (int**)realloc(m->data, m->rows * sizeof(*m->data));
        if (new_data)
            m->data = new_data;
    }
}
Sign up to request clarification or add additional context in comments.

Comments

2

Firstly it is a C++ code not a C code. There are used references in parameter declarations

void row_del(int**& a, int& row, int col , int k){
                 ^^^     ^^^

and also there is used a casting of the pointer of the type void returned by the call of realloc that is not required in C.

a = (int**)realloc(a, row * sizeof(int*));
    ^^^^^^^   

Within the function two objects of the caller, the original pointer and the integer object denoting the number of rows are modified (not their copies). So they are passed by reference.

In C passing by reference means passing an object indirectly through a pointer to it. Thus dereferencing the passed pointer a function can change the original object pointed to by the passed pointer.

That is in C the function could look like

void row_del(int ***a, int *row, int col , int k){
    for(int j = 0; j < col ; j ++){
       for(int i = k; i < *row - 1; i ++){
          ( *a )[i][j] = ( *a )[i + 1][j];
       }
    }
    --*row;
    *a = realloc( *a, *row * sizeof(int*));
}

Pay attention to that the function declaration and its definition is not good. The function realloc can return a null pointer. In this case the original array of pointers to rows can be lost. Also the function does not check whether the value of the variable k is less than the value of the expression *row. In this case the array of pointers can be reallocated and the value of rows can be decreased independent on wthere the value of the variable k is the range [0, *rows). And the types of the corresponding parameters should have unsigned integer type size_t. Instead of the return type void it would be better if the function returned a mark whether deleting a row was successful.

Also if there is only one row exists then the original pointer *a should be just set to NULL after deleting its elements and the row itself instead of calling realloc.

And moreover there is a serious bug that can produce memory leaks. Before callling realloc you need to free the memory allocated for elements of the row *row-1.

Take into account that instead of the nested for loops you could use only one for loop within the body of which you could call standard C function memcpy.

Here is a demonstration program that shows how the function could be declared and defined.

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

// Finction row_del
// deletes k-th row in a matrix.
// The function returns:
// -1 if memory reallocation failed;
//      In this case nevertheless all the rows starting from the k-th row will be overwritten
//      and the pointer to the last row will be set to NULL.
// 0  if the k is out of the acceptable range or if the matrix is empty
// 1 if the k-th row was successfully deleted

int row_del( int ***a, size_t *row, size_t col, size_t k )
{
    int success = k < *row;
    
    if ( success )
    {
        if ( *row == 1 )
        {
            free( **a );
            free( *a );
            *a = NULL;
        }
        else
        {
            for ( size_t i = k; i < *row -1; i++ )
            {
                memcpy( ( *a )[i], ( *a )[i+1], col * sizeof( int ) );
            }
            
            free( ( *a )[*row - 1] );
            ( *a )[*row-1] = NULL;
            
            int **tmp = realloc( *a, ( *row - 1 ) * sizeof( int * ) );
            
            if ( tmp == NULL )
            {
                success = -1;
            }
            else 
            {
                *a = tmp;
            }
        }
        
        --*row;
    }
    
    return success;
}

int main(void) 
{
    size_t row = 2;
    size_t col = 3;
    
    int **a = malloc( row * sizeof( *a ) );
    
    int value = 0;
    
    for ( size_t i = 0; i < row; i++ )
    {
        a[i] = malloc( col * sizeof( **a ) );
        
        for ( size_t j = 0; j < col; j++ )
        {
            a[i][j] = value++;
        }
    }
    
    for ( size_t i = 0; i < row; i++ )
    {
        for ( size_t j = 0; j < col; j++ )
        {
            printf( "%d, ", a[i][j] );
        }
        putchar( '\n' );
    }

putchar( '\n' );

    while ( row != 0 )
    {
        row_del( &a, &row, col, 0 );
        for ( size_t i = 0; i < row; i++ )
        {
            for ( size_t j = 0; j < col; j++ )
            {
                printf( "%d, ", a[i][j] );
            }
            putchar( '\n' );
        }
    }
    
    printf( "a == NULL is %s\n", a == NULL ? "true" : "false" );
}

The program output is

0, 1, 2, 
3, 4, 5, 

3, 4, 5, 
a == NULL is true

Comments

2

This is not a valid C code. In C, you pass parameters as value, not reference.

The equivalent of this in C is:

void row_del(int ***a, int *row, int col, int k);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.