Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

returning a 2D array (in C# and in C++)

I am coding a function that will return a 2D array. This leds me to think about the implications of this in C# (with garbage collection) and in C++(no GC)

(Why in both-you may ask: I am now writing it on a windows platform with C# but in some months I will implement my algorithms on an embedded device using C++)

So basically I have a 2D array say table, and through a function I assigned to it the return value. My question is: what happen to the original piece of memory that hold the original table??

Now to the code: In C#

    using System;

    public class Test
    {
        public static void Main()
        {
            int[,] table= new int [10,10];  //Here some memory is separated for table 
            int[,] table= createTable(10,10); //Here the return value of createTable is assigned to the original value 

//WHAT HAPPENED TO THE ORIGINAL MEMORY THAT table HAD?

            printTable(table,10,10); //disregard this. Not that important
        }

        public static int[,] createTable(int rows, int columns)
        {
            int[,] t = new int[rows,columns];
            for(int i=0;i<rows;i++)
              for(int j=0;j<columns;j++)
                 t[i,j]=(i+j);

             return t;    
        }

        public static void printTable(int[,]t, int rows, int columns)
        {
            for(int i=0;i<rows;i++)
              for(int j=0;j<columns;j++)
                Console.WriteLine(t[i,j]);

             foreach( var im in t)
               Console.WriteLine(im);
        }
    }

(Please don't tell me that the first new int is not necessary, etc. It is necessary for the question, it could be replaced by calling createTable twice)

I am guessing that in C#, the garbage collector takes care of this and I don't have to worry?


Now in C++

#include <cstdio>
#include <cstdlib>


int** createTable(int rows, int columns){
    int** table = new int*[rows];
    for(int i = 0; i < rows; i++) {
        table[i] = new int[columns]; 
        for(int j = 0; j < columns; j++){ table[i][j] = (i+j); }// sample set value;    
    }
    return table;
}
void freeTable(int** table, int rows){
    if(table){
        for(int i = 0; i < rows; i++){ if(table[i]){ delete[] table[i]; } }
        delete[] table;    
    }
}
void printTable(int** table, int rows, int columns){
    for(int i = 0; i < rows; i++){
        for(int j = 0; j < columns; j++){
            printf("(%d,%d) -> %d\n", i, j, table[i][j]);
        }    
    }
}
int main(int argc, char** argv){

    int** table = createTable(5, 5);
    table = createTable(10,10);
    printTable(table, 10, 10);
    freeTable(table, 10);
    return 0;
}

What happened to the original memory hold by table (5x5). Does it create a memory leak when table get assigned the 10x10 table?? how do I avoid this?

like image 386
KansaiRobot Avatar asked Sep 15 '25 13:09

KansaiRobot


2 Answers

In C# the garbage collector will take care of memory that isn't reachable any more.

I compiled your C++ program with gcc (Version: (Debian 6.4.0-1) 6.4.0 20170704 ) on Debian Buster.

valgrind which is a very useful tool for checking for various kinds of memory problems, gave me the following output with valgrind --leak-check=full ./test :

Memcheck, a memory error detector
Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
Command: ./test

[... program output ...]

HEAP SUMMARY:
    in use at exit: 140 bytes in 6 blocks
  total heap usage: 19 allocs, 13 frees, 74,348 bytes allocated

140 (40 direct, 100 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 2
   at 0x4C2C97F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
   by 0x10883E: createTable(int, int) (in /home/user/code/test/test)
   by 0x108A26: main (in /home/user/code/test/test)

LEAK SUMMARY:
   definitely lost: 40 bytes in 1 blocks
   indirectly lost: 100 bytes in 5 blocks
     possibly lost: 0 bytes in 0 blocks
   still reachable: 0 bytes in 0 blocks
        suppressed: 0 bytes in 0 blocks

For counts of detected and suppressed errors, rerun with: -v
ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

As you can see, you are definitely loosing memory. Either call your freeTable() function first and then reassign your pointer or use smart pointers to get around this problem.

like image 102
EXIT_FAILURE Avatar answered Sep 17 '25 02:09

EXIT_FAILURE


For C++ part, yes, you create memory leak.you have to call freeTable:

int** table = createTable(5, 5);
freeTable(table, 5);
table = createTable(10,10);
printTable(table, 10, 10);
freeTable(table, 10);

Better to use Raii object as vector<vector<int>> (or dedicated class Matrix) to avoid to worry about memory deallocation (and you can add size information in class too).

So it would simply be:

auto table = createTable(5, 5);
table = createTable(10,10);
printTable(table);
like image 21
Jarod42 Avatar answered Sep 17 '25 02:09

Jarod42