0

I'm trying to implement sparse matrix addition(code attached), as c++ doesn't like 2d dynamic arrays I am using three different array each to represent RowIndex, ColumnIndex, and the corresponding values.

say i added A and B to get C.

C = A.add(B)

in the add member function, I am returning the address to the newly created C matrix.

Everything works fine before returning C, as A->C has expected values inside the arrays, but once I store the C in another identifier in the main function and then print the same array, via new object I find garbage in some of the arrays.

what i've tried :

  • At first I was creating the object inside the Add function and then returning the object, tried it with/without the new keyword.
  • I thought it might be a problem with the scope so, Now I am using an attribute of matrix A to instantiate the new C matrix, to store the newly-created matrix and then returning the address to it.

Debugging :

  • The arrays are located at the same address before and after the .add() function's use still different values are printed.
class sparse{
        public:
            int rows , cols, len ; 
            int arr;
            int *rindex, *cindex, *vals;
            sparse *c; 

            sparse(){};
            sparse( int r, int c, int nzs ,  int *rn ,  int *cn, int *values){
                this->rows = r;
                this->cols = c;
                this->rindex = rn;
                this->cindex = cn;
                this->vals   = values;
                this->len    = nzs;
            }
        sparse* add(sparse b){
            int ap=0  , bp =0, cp =0; // pointers that help with merging in A,B,C matrices
            int crindex[this->len+ b.len], 
                ccindex[this->len+ b.len], 
                cvals[this->len+ b.len];
            int crows = this->rows, ccols  = this->cols;  

            int cnzs = cp; //non-zero values in C
            int crf[cnzs], ccf[cnzs], cvf[cnzs];

             //merge sort approach to add two sparse matrices


            for (int i=0; i< cp ; i++){ // to redcuce size individual arrays
                crf[i] = crindex[i];
                ccf[i] = ccindex[i]; 
                cvf[i] = cvals[i];
            }

            this->c =  new sparse(crows, ccols, cnzs, crf, ccf, cvf );
            this->c->print();

            // debugging statements
            printf("\narray before recieving: ");
            for (int i =0 ; i<this->c->len; i++ ){
                printf( "%d ", this->c->rindex[i]  );
            }
            printf("\n address :%d \n", this->c->rindex);
            return c;
        }



        void print( ){
            printf( "\nRow | column | value");
            for (int i =0 ; i<this->len; i++ ){
                printf( "\n %d \t %d \t %d", this->rindex[i], this->cindex[i], this->vals[i] );
            }
        }
};


int main(){
    int ars= 20, acs= 15, anzs= 5, bnzs = 5  ;
             //      .
    int ar [anzs] = { 0,0,0,4,7};
    int ac [anzs] = { 0,1,7,1,0};
    int av [anzs] = { 11,11,11,11,11,};
                 //  .
    int br [5] = { 0,1,3,7, 7};
    int bc [5] = { 0,0,5,9,12 };
    int bv [5] = { 22,22,22,22,22};

    sparse a(ars, acs, anzs, ar, ac, av  );
    sparse b(ars, acs,bnzs , br, bc, bv );

    sparse* c = a.add(b);
    c->print();

    printf("\narray after recieving: ");


    for (int i =0 ; i<c->len; i++ ){
        printf( "%d ",c->rindex[i] );
    }
    printf("\naddress : %d", c->rindex);
    return 0;
    }
Here's the output: 

Row | column | value
 0       0       33
 0       1       11
 0       7       11
 1       0       22
 3       5       22
 4       1       11
 7       0       11
 7       9       22
 7       12      22
array before recieving: 0 0 0 1 3 4 7 7 7
 address :6421672

Row | column | value
 -433312354      0       33
 1       1       11
 1996637728      7       11
 0       0       22
 1       5       22
 12      1       11
 0       0       11
 6421672         9       22
 7       12      22
array after recieving: -433312354 1 1996637728 0 1 13 0 6421672 7
address : 6421672 
dragonLOLz
  • 78
  • 7
  • Can you trim all the excess from your example until you get a [small example](https://stackoverflow.com/help/minimal-reproducible-example)? – Ayjay Sep 14 '19 at 10:13
  • This is after removing a significant portion, i'm afraid i might remove important bits of it, please let me know if there's some specific part which i can remove – dragonLOLz Sep 14 '19 at 10:29
  • O.T.: Is there any reason for `sparse *c;` to be a member variable? I would make it a local variable in `sparce::add()`. Actually, I would drop the pointer and `new` as well and return by value (counting on [return-value-optimization](https://en.wikipedia.org/wiki/Copy_elision#Return_value_optimization)). In opposition to this, I would make the parameter in `add()` a const reference i.e. `sparse add(const sparse &b)`. No need to copy `b` on call of `add()`, is it? – Scheff's Cat Sep 14 '19 at 10:29
  • @scheff I'm not sure if I get the the (return-value-optimization) part, but before this version ive tried creating a local object, as I've stated in "What ive tried section" – dragonLOLz Sep 14 '19 at 10:33
  • Concerning copy by value, you may have a look onto the [Rule of Three](https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)). For classes with raw pointers (with ownership), I would always overload copy constructor and assignment (or delete them). – Scheff's Cat Sep 14 '19 at 10:33
  • 1
    Storing in members only the pointers to arrays passed in constructor - that's very dangerous. IMHO, in this case `sparce` should have ownership of these arrays. A `std::vector` seems to be appropriate for me (instead of the pointers). (And, btw. this would make the default copy constructor working as expected.) – Scheff's Cat Sep 14 '19 at 10:36

1 Answers1

2

The problem is in the add() method. You are creating arrays on the stack and passing their addresses to the new sparse instance. After returning from add() these arrays are no longer valid. You want to allocate those arrays on the heap using the new-operator: int * crindex = new int[this->len+ b.len]. But then you also have to free those arrays (with delete[] at some point or you will leak memory).

This is a common mistake when starting with c++. You might want to read more about "stack vs heap allocations". Try for example this question on SO: Stack Memory vs Heap Memory

If you are new to c++ I recommend using std::vector over plain arrays as they are less error prone. So your class would look like this:

#include<vector>
class sparse{
        public:
            //int rows , cols, len ; <-- use rindex.size() instead
            //int arr;
            std::vector<int> rindex, cindex, vals;
}
Gregor Budweiser
  • 218
  • 3
  • 10
  • 1
    _If you are new to c++ I recommend using std::vector over plain arrays as they are less error prone._ If you are not new anymore, I would recommend the same. ;-) There is less need for C arrays in C++ in general. – Scheff's Cat Sep 14 '19 at 10:48
  • Hey Thanks for the answer, i got the stack Vs heap part, and currently trying vectors in the same code, Now ive changed the arrays with vectors and the formal arguments of the constructor with to vector as well as the actual arguments inside the main function ..... But getting a Segmentation fault – dragonLOLz Sep 14 '19 at 11:21
  • Segmentation faults are when you try to access a memory location that is not valid (i.e. not part of your program). My guess is that you have an index that points beyond the end of a vector. Have you tried to run your program inside a debugger? What IDE/compiler are you using? – Gregor Budweiser Sep 15 '19 at 09:55
  • yes, my problem was that i was assigning values to the vector object without ever initializing it – dragonLOLz Oct 07 '19 at 13:59